pandeydigant31's picture
Initial deploy: DELPHI Pump Analytics (renamed from MURPHY)
199bfa3 verified
Raw
History Blame Contribute Delete
9.22 kB
"""
Configuration for CSH2 Web Analytics Dashboard
Supports local (.env), Streamlit Cloud (st.secrets), and HuggingFace Spaces (env vars) deployment.
"""
import os
from datetime import timedelta
from zoneinfo import ZoneInfo
import streamlit as st
from dotenv import load_dotenv
load_dotenv()
# ── Timezone Configuration ──────────────────────────────────────────────────
# The PLC logs in US/Eastern. The DB's `dateandtime` column is Eastern local
# time, while `utc_full_timestamp` is the real UTC. The `cycle_periods` table
# stores Eastern times incorrectly labeled as UTC (+00:00), requiring a +4h
# correction for sensor data queries. All data (Mar-Sep 2025) falls within
# EDT (UTC-4). We display all user-facing times in Eastern.
DISPLAY_TZ = ZoneInfo("US/Eastern")
DISPLAY_TZ_LABEL = "ET" # label for axis titles and UI
CYCLE_PERIODS_UTC_OFFSET = timedelta(hours=4) # correction for cycle_periods timestamps
def _get_secret(key, default=None):
"""Read from st.secrets (Streamlit Cloud) or os.environ/.env (local dev)"""
try:
return st.secrets[key]
except (KeyError, FileNotFoundError, AttributeError):
return os.getenv(key, default)
# Database Configuration
DB_CONFIG = {
'host': _get_secret('DB_HOST'),
'port': _get_secret('DB_PORT'),
'database': _get_secret('DB_NAME'),
'user': _get_secret('DB_USER'),
'password': _get_secret('DB_PASSWORD'),
'connect_timeout': 10,
}
ANTHROPIC_API_KEY = _get_secret('ANTHROPIC_API_KEY')
# Database Tables
TABLES = {
'raw': 'procdatafloattable',
'agg_1sec': 'procdatafloattable_utc_1sec',
'agg_15sec': 'procdatafloattable_utc_15sec',
'tag_metadata': 'procdatatagtable',
'events': 'allevent',
'fill_reports': 'fillreport',
'cooldown_periods': 'cooldown_periods',
'cycle_periods': 'cycle_periods',
}
# Smart table routing thresholds (seconds)
TABLE_ROUTING = {
'raw_max_seconds': 3600, # <= 1 hour -> raw 10Hz
'agg_1sec_max_seconds': 86400, # <= 1 day -> 1sec aggregates
# > 1 day -> 15sec aggregates
}
MAX_ROWS_RETURN = 500000
# Resolution name to table mapping (used by Advanced Data Explorer execution engine)
RESOLUTION_MAP = {
'raw': TABLES['raw'],
'1sec': TABLES['agg_1sec'],
'15sec': TABLES['agg_15sec'],
}
# Key sensor tags with descriptions and units
SENSOR_TAGS = {
'AmbTempAvg': {'description': 'Ambient Temperature Average', 'units': 'K', 'tagindex': 0},
'AOV140': {'description': 'Valve Position AOV140', 'units': '%', 'tagindex': 1},
'AvgPower': {'description': 'Average Power', 'units': 'kW', 'tagindex': 2},
'CalcMass': {'description': 'Calculated Mass', 'units': 'kg', 'tagindex': 3},
'DPT01T': {'description': 'Differential Pressure', 'units': 'bar', 'tagindex': 4},
'DispTempAvg': {'description': 'Dispenser Temperature Average', 'units': 'K', 'tagindex': 5},
'FD100': {'description': 'Flow Detector', 'units': '', 'tagindex': 6},
'FT140': {'description': 'Flow Transmitter', 'units': 'kg/min', 'tagindex': 7},
'FTTMP': {'description': 'Flow Transmitter Temperature', 'units': 'K', 'tagindex': 8},
'GD100': {'description': 'Gas Detector', 'units': '', 'tagindex': 9},
'H201T': {'description': 'H2O Temperature', 'units': 'K', 'tagindex': 10},
'IR01T': {'description': 'IR Temperature', 'units': 'K', 'tagindex': 11},
'LT200': {'description': 'Level Transmitter', 'units': '%', 'tagindex': 12},
'MC130_VFD_Speed': {'description': 'Motor VFD Speed', 'units': 'RPM', 'tagindex': 13},
'PowerConsumption': {'description': 'Power Consumption', 'units': 'kW', 'tagindex': 14},
'PT01T': {'description': 'Cryotank Pressure', 'units': 'bar', 'tagindex': 15},
'PT100': {'description': 'Pressure PT100', 'units': 'bar', 'tagindex': 16},
'PT110': {'description': 'Inlet Pressure', 'units': 'bar', 'tagindex': 17},
'PT130': {'description': 'Discharge Pressure', 'units': 'bar', 'tagindex': 18},
'PT131': {'description': 'Discharge Pressure Alt', 'units': 'bar', 'tagindex': 19},
'PT150': {'description': 'Pressure PT150', 'units': 'bar', 'tagindex': 20},
'PT200': {'description': 'Pressure PT200', 'units': 'bar', 'tagindex': 21},
'PT310': {'description': 'Pressure PT310', 'units': 'bar', 'tagindex': 22},
'TT100': {'description': 'Temperature TT100', 'units': 'K', 'tagindex': 23},
'TT110': {'description': 'Pump Feed Line Temperature', 'units': 'K', 'tagindex': 24},
'TT120': {'description': 'Temperature TT120', 'units': 'K', 'tagindex': 25},
'TT121': {'description': 'Temperature TT121', 'units': 'K', 'tagindex': 26},
'TT130': {'description': 'Discharge Temperature', 'units': 'K', 'tagindex': 27},
'TT131': {'description': 'Discharge Temperature Alt', 'units': 'K', 'tagindex': 28},
'TT140': {'description': 'Temperature TT140', 'units': 'K', 'tagindex': 29},
'MC130_PwrConsumption': {'description': 'Motor Power Consumption', 'units': 'kW', 'tagindex': 30},
'PercentFill': {'description': 'Percent Fill', 'units': '%', 'tagindex': 31},
'PeakPower': {'description': 'Peak Power', 'units': 'kW', 'tagindex': 32},
'N2Density': {'description': 'N2 Density', 'units': 'kg/m3', 'tagindex': 33},
'PS100': {'description': 'Pump Status', 'units': '', 'tagindex': 34},
'H2Density': {'description': 'H2 Density', 'units': 'kg/m3', 'tagindex': 35},
'FTTMP2': {'description': 'Flow Temp 2', 'units': 'K', 'tagindex': 54},
'M130_BusV': {'description': 'Motor Bus Voltage', 'units': 'V', 'tagindex': 55},
'M130_Current': {'description': 'Motor Current', 'units': 'A', 'tagindex': 56},
'M130_Freq': {'description': 'Motor Frequency', 'units': 'Hz', 'tagindex': 58},
'M130_PF': {'description': 'Motor Power Factor', 'units': '', 'tagindex': 61},
'M130_Pwr': {'description': 'Motor Power', 'units': 'kW', 'tagindex': 62},
'M130_PwrCon': {'description': 'Motor Power Consumption', 'units': 'kW', 'tagindex': 63},
'M130_RPM': {'description': 'Motor RPM', 'units': 'RPM', 'tagindex': 66},
'M130_Speed': {'description': 'Motor Speed', 'units': 'RPM', 'tagindex': 68},
'M130_Torque': {'description': 'Motor Torque', 'units': 'Nm', 'tagindex': 70},
'PT010': {'description': 'Pressure PT010', 'units': 'bar', 'tagindex': 72},
'PT020': {'description': 'Pressure PT020', 'units': 'bar', 'tagindex': 73},
}
# Sensor groups for quick selection
SENSOR_GROUPS = {
'Pressure': ['PT01T', 'PT100', 'PT110', 'PT130', 'PT131', 'PT150', 'PT200', 'PT310', 'PT010', 'PT020'],
'Temperature': ['TT100', 'TT110', 'TT120', 'TT121', 'TT130', 'TT131', 'TT140', 'AmbTempAvg', 'DispTempAvg'],
'Flow': ['FT140', 'FD100', 'FTTMP', 'FTTMP2', 'CalcMass'],
'Motor/VFD': ['MC130_VFD_Speed', 'M130_Speed', 'M130_RPM', 'M130_Pwr', 'M130_Current', 'M130_Freq', 'M130_Torque', 'M130_BusV', 'M130_PF'],
'Power': ['AvgPower', 'PeakPower', 'PowerConsumption', 'MC130_PwrConsumption', 'M130_PwrCon'],
'Level & Density': ['LT200', 'PercentFill', 'N2Density', 'H2Density'],
'Status': ['PS100'],
}
# Motor speed tags (for testing cycle detection - try in order)
MOTOR_SPEED_TAGS = ['M130_Speed', 'MC130_VFD_Speed', 'M130_RPM']
# Motor speed display multiplier: M130_Speed (tag 68) reads as a % signal (0-100).
# Multiply by 5/3 to convert to actual RPM matching Grafana display.
# e.g., M130_Speed=180 raw → 180 × 5/3 = 300 RPM.
# Set to 1.0 to disable scaling.
MOTOR_SPEED_DISPLAY_MULTIPLIER = 5 / 3
# Testing cycle detection parameters
CYCLE_DETECTION = {
'idle_threshold': 10.0, # RPM below which motor is idle
'min_cycle_seconds': 60, # Minimum cycle duration
'smoothing_window': 5, # Rolling median window (in samples)
'max_gap_seconds': 120, # Max gap to merge adjacent cycles
# Qualification: cycle must show real compression or flow
'min_peak_pressure_bar': 50.0, # PT130 must exceed this (real fills reach 300+ bar)
'min_pressure_rise_bar': 10.0, # PT130 must rise this much from initial
'min_avg_flow_kg_min': 0.05, # FT140 average must exceed this (real fills do 1+ kg/min)
'max_cycle_duration_hours': 8.0, # Reject multi-day noise spans (no real test > 8h)
}
# Key tags for cycle detail analysis
CYCLE_DETAIL_TAGS = ['PT130', 'PT01T', 'PT110', 'TT110', 'TT130', 'FT140', 'M130_Speed', 'MC130_VFD_Speed', 'AOV140']
# Performance targets
PERFORMANCE_TARGETS = {
'pressure': {'phase1': 500, 'h70': 700, 'ultimate': 1000},
'flow': {'simplex': 2, 'triplex': 6},
'efficiency': {'pump_gas_ratio': 0.95, 'seal_blowby': 0.03},
'energy': {'specific_consumption': 0.5},
}
# Testing phases
TESTING_PHASES = {
'mechanical': {'description': 'Mechanical validation with LN2', 'status': 'completed', 'max_pressure': '937 bar'},
'phase1a': {'description': 'Phase 1A H2 testing with LH2', 'status': 'completed', 'max_pressure': '495 bar'},
'phase1b': {'description': 'Phase 1B ccH2 fills', 'status': 'completed', 'max_pressure': '495 bar'},
'phase2': {'description': 'Phase 2 H35/H70 fills', 'status': 'planned', 'target_pressure': '700+ bar'},
}