# ============================================================================= # process_values.yml — sampled analogue history. # # SOURCE: fixture.process_value_history while USE_FIXTURES=true; the agreed imh # process value table from Phase 4. # # THE THING THAT WILL BITE WHEN imh IS CONNECTED: CI Server historises with a # deadband, so real samples are IRREGULAR. The fixtures are regular 1-minute # samples. Any measure that averages rows rather than time-weighting them will # look correct on fixtures and be wrong on imh - a flat period compresses to # one row and a noisy period to hundreds, so a plain avg is weighted by how # interesting the signal was. avg_value below is a plain average and is # documented as an approximation; time_weighted_avg is the one to trust, and it # must be re-verified against imh at the Phase 5 gate. # # SENTINELS: PS_STN_TIME_TO_SPILL_WEIR and PS_STN_TIME_TO_LSHH use 32767 to # mean "drawing down or holding" - it is not a duration. Every measure here # excludes it. Do not remove that filter to make a number look tidier. # # QUALITY: rows with quality other than GOOD are excluded from every measure. # A BAD sample from a failed transmitter is not a low reading. # # UNITS: whatever the historian stores, which is not always what the PLC works # in. Wet well level is historised as percent of the spill weir crest (raw mm # divided by 60): 100.0 % = 6000 mm. See db/seed/tags.csv for every conversion. # ============================================================================= cubes: - name: process_values sql_table: fixture.process_value_history # -> imh PV table at Phase 4 description: > Sampled analogue history - wet well level, inflow, discharge flow, drive speed, run hours. This is what makes an advisory question answerable with evidence; you cannot answer a flow question from alarms. joins: - name: equipment sql: "{CUBE}.equipment_id = {equipment}.equipment_id" relationship: many_to_one dimensions: - name: id sql: "{CUBE}.tag_id || '@' || {CUBE}.sample_time" type: string primary_key: true - name: sample_time sql: sample_time type: time description: Stored UTC, presented in SITE_TIMEZONE. Converted once, here. - name: tag_id sql: tag_id type: string - name: equipment_id sql: equipment_id type: string - name: engineering_unit sql: engineering_unit type: string description: > Always report this with the number. A level of 86.7 is a percentage of the weir crest, not a metre reading. - name: quality sql: quality type: string - name: is_fixture sql: is_fixture type: boolean measures: - name: sample_count type: count filters: - sql: "{CUBE}.quality = 'GOOD' AND {CUBE}.value <> 32767" - name: avg_value sql: value type: avg filters: - sql: "{CUBE}.quality = 'GOOD' AND {CUBE}.value <> 32767" description: > APPROXIMATION. Plain average of samples. Correct on the regular fixture data; biased on deadband-compressed imh data. Prefer time_weighted_avg for anything an engineer will check. - name: time_weighted_avg sql: > SUM({CUBE}.value * EXTRACT(EPOCH FROM ( LEAD({CUBE}.sample_time) OVER ( PARTITION BY {CUBE}.tag_id ORDER BY {CUBE}.sample_time ) - {CUBE}.sample_time))) / NULLIF(SUM(EXTRACT(EPOCH FROM ( LEAD({CUBE}.sample_time) OVER ( PARTITION BY {CUBE}.tag_id ORDER BY {CUBE}.sample_time ) - {CUBE}.sample_time))), 0) type: number description: > Time-weighted average - each sample weighted by how long it stood. This is the honest average on deadband-compressed history. Verify it against imh by hand at the Phase 5 gate before trusting it in prose. - name: max_value sql: value type: max filters: - sql: "{CUBE}.quality = 'GOOD' AND {CUBE}.value <> 32767" - name: min_value sql: value type: min filters: - sql: "{CUBE}.quality = 'GOOD' AND {CUBE}.value <> 32767" - name: p95_value sql: "PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY {CUBE}.value)" type: number filters: - sql: "{CUBE}.quality = 'GOOD' AND {CUBE}.value <> 32767" description: > 95th percentile. More useful than max for "how high does it normally get", because max is one sample and often a transient. - name: seconds_above_high_level_alarm sql: > SUM(CASE WHEN {CUBE}.tag_id = 'PS_STN_WET_WELL_LEVEL' AND {CUBE}.value >= 86.7 THEN 60 ELSE 0 END) type: number description: > Seconds the wet well spent above the high level alarm setpoint (86.7 % = 5200 mm, the %MW8 default). ASSUMES A 60 SECOND SAMPLE INTERVAL, true of the fixtures and NOT true of imh. When imh is connected this must be rewritten to sum actual sample gaps - it is on the Phase 5 gate list for exactly that reason. If the setpoint itself was changed during the window (PS_STN_HIGH_LEVEL_ALARM_SP), this measure is wrong and the answer must say so. - name: seconds_above_lshh sql: > SUM(CASE WHEN {CUBE}.tag_id = 'PS_STN_WET_WELL_LEVEL' AND {CUBE}.value >= 91.7 THEN 60 ELSE 0 END) type: number description: > Seconds above LSHH (91.7 % = 5500 mm). Same 60 second assumption as above. Any non-zero value here is worth reporting explicitly. pre_aggregations: - name: pv_by_hour measures: [avg_value, max_value, min_value, sample_count] dimensions: [tag_id, equipment_id, engineering_unit] time_dimension: sample_time granularity: hour partition_granularity: month refresh_key: every: 10 minutes build_range_start: sql: "SELECT now() - interval '180 days'" build_range_end: sql: "SELECT now()"