DataFusion SQL FDD Cookbook
Open-FDD edge executes fault detection as DataFusion SQL against Apache Arrow historian tables. Copy-paste rules into the SQL FDD Rules tab or POST /api/fdd-rules/{id}/test-sql.
Analyst mirror: every section below has a matching implementation in the Pandas cookbook for workflows outside Open-FDD.
Table of contents
- Platform concepts
- Fault confirmation delay (default 5 minutes)
- Haystack → SQL columns
- Test & activate workflow
- Sensor validation
- Air handling units (FC1–FC15 / GL36 A–M)
- VAV zones
- Economizer & ventilation
- Central plants
- Heat pumps
- Weather station
- Trim & respond advisory (GL36)
- Extended rule families (v2)
- Extended rule families (P2)
- Framework & parity docs
- Debugging & windowing
Platform concepts
| Term | Meaning |
|---|---|
telemetry_pivot |
Wide historian table: timestamp, equipment_id, FDD input columns (oa_t, sat, …) |
telemetry |
Long format (optional) — use pivot for most HVAC rules |
fault_raw |
Boolean — instantaneous fault condition (required output column) |
confirmation_seconds |
Minimum duration fault must hold before latching (API applies after SQL) |
Column names come from your Haystack assignment graph, not BACnet instance numbers.
Rule template
-- confirmation_seconds: 300 (adjust in API/UI — default 5 minutes)
-- param: example_threshold = 5.0
SELECT
timestamp,
equipment_id,
oa_t,
CASE
WHEN oa_t IS NULL THEN false
WHEN oa_t < 40.0 OR oa_t > 110.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu-id'
Always handle NULL — missing samples must not latch faults.
Command scaling (0–1 vs 0–100 %)
Normalize in assignments, or inline:
CASE WHEN fan_cmd > 1.0 THEN fan_cmd / 100.0 ELSE fan_cmd END AS fan_norm
Fault confirmation delay (default 5 minutes)
Raw rules can flicker true for one poll cycle. Open-FDD debounces after SQL returns fault_raw.
Default: confirmation_seconds: 300 (5 minutes). Change per rule in the workbench or API:
{"confirmation_seconds": 300}
curl -s -X POST http://127.0.0.1:8080/api/fdd-rules/RULE_ID/test-sql \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"sql":"SELECT timestamp, equipment_id, ... AS fault_raw FROM telemetry_pivot","confirmation_seconds":300}'
| Poll interval | confirmation_seconds |
Approx. consecutive samples |
|---|---|---|
| 60 s | 300 (default) | ~5 |
| 60 s | 600 | ~10 |
| 300 s | 900 | ~3 |
Every rule below includes -- confirmation_seconds: 300 in comments. Increase for advisory or hunting rules (e.g. FC4 PID hunting often uses 3600).
Haystack → SQL columns
Binding chain:
Driver point → Haystack point → FDD input → telemetry_pivot column
- Commission drivers (BACnet / Modbus / Haystack / JSON)
- Build Haystack model (equipment + points)
- Assignments — map FDD inputs (guide)
- Pivot exposes FDD input IDs as columns (
oa_t,sat,zone_t, …)
Discover columns: SQL FDD schema picker, Plots tab, or GET /api/fdd-schema/fdd-inputs.
| Haystack role | FDD input | Pivot column |
|---|---|---|
| OA temp sensor | oa_t |
oa_t |
| SAT sensor | sat |
sat |
| SAT setpoint | sat_sp |
sat_sp |
| Zone temp | zone_t |
zone_t |
Replace equip:your-ahu-id with your scoped equipment ref in production.
Test & activate workflow
- Save rule:
POST /api/fdd-ruleswithreview_status: draft - Test:
POST /api/fdd-rules/{id}/test-sqlwithconfirmation_seconds: 300 - Integrator approves in UI
- Activate:
POST /api/fdd-rules/{id}/activate
Link rules in FDD Wires: driver_point → model_point → fdd_input → sql_rule → confirmation → fault_output.
Builder API for simple thresholds:
curl -s -X POST http://127.0.0.1:8080/api/fdd-rules/builder-sql \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"input":"oa_t","operator":"range","low":40,"high":110,"equipment_id":"equip:validation"}' | jq '.sql'
Sensor validation
Data-quality faults (bounds, flatline, rate-of-change, mixing envelope). Gate with equipment state (fan on, occupied) where appropriate.
Default bounds
Tune per site in SQL literals or assignment transforms.
| Sensor kind | Min | Max | Flatline tol | Max Δ/hr | Code |
|---|---|---|---|---|---|
| Zone temp (°F) | 55 | 90 | 0.10 | 4.0 | VAV-C |
| Supply air temp | 50 | 110 | 0.15 | 8.0 | AHU-C |
| Return air temp | 55 | 95 | 0.10 | 3.0 | AHU-D |
| Mixed air temp | 40 | 110 | 0.15 | 6.0 | AHU-D |
| Outdoor air temp | −40 | 130 | 0.10 | 12.0 | BLD-B |
| Duct static (inH₂O) | −0.5 | 3.0 | 0.02 | 0.5 | AHU-A |
| Relative humidity (%) | 0 | 100 | 1.0 | 15.0 | DC-C |
| CHW temp | 40 | 90 | 0.10 | 4.0 | CH-D |
| Hot water temp | 70 | 200 | 0.15 | 6.0 | CH-D |
| CO₂ (ppm, occupied) | 400 | 1000 | 5.0 | 200 | VAV-B |
SV-1 — Zone temperature out of range
Code: VAV-C · confirmation_seconds: 300
SELECT timestamp, equipment_id, zone_t,
CASE
WHEN zone_t IS NULL THEN false
WHEN zone_t < 55.0 OR zone_t > 90.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
AND occ_mode = 'occupied'
SV-2 — OA temperature out of range
Code: BLD-B · confirmation_seconds: 300
SELECT timestamp, equipment_id, oa_t,
CASE
WHEN oa_t IS NULL THEN false
WHEN oa_t < 40.0 OR oa_t > 110.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
SV-3 — OA humidity out of range
Code: DC-C · confirmation_seconds: 300
SELECT timestamp, equipment_id, oa_h,
CASE
WHEN oa_h IS NULL THEN false
WHEN oa_h < 10.0 OR oa_h > 95.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
SV-4 — Mixing envelope (prefer over RAT-only)
When OAT, RAT, and MAT are present, use envelope checks — see FC2 and FC3.
SV-5 — Stale data (no recent samples)
Code: BLD-D · confirmation_seconds: 300
SELECT equipment_id,
MAX(timestamp) AS last_ts,
true AS fault_raw
FROM telemetry_pivot
GROUP BY equipment_id
HAVING MAX(timestamp) < NOW() - INTERVAL '30' MINUTE
Adapt interval syntax to your DataFusion version; test in /sql-fdd before activate.
SV-6 — Flatline & rate-of-change
Rolling flatline/spike detection is limited in edge SQL. Use confirmation_seconds with bounds rules, or run full rolling logic in the Pandas cookbook.
Air handling units
Reference: Open-FDD AHU fault conditions FC1–FC15 (GL36-inspired). Default confirmation_seconds: 300 unless noted.
Full metadata for FC1–FC15 and all P0 rules: P0 rule catalog.
Shared params (tune per site):
| Param | Default | Used in |
|---|---|---|
mix_tol |
1.15 °F | FC2, FC3, FC5, FC8, FC12 |
supply_tol |
1.15 °F | FC5, FC8, FC12, FC13 |
ahu_min_oa_dpr |
0.0–1.0 | FC4, FC6, FC8–FC15 |
delta_supply_fan |
0.55 °F | FC5, FC8, FC12, FC14, FC15 |
fan_on_min |
0.01 | Most FC rules |
FC1 — Duct static below SP at full fan (GL36 Rule A)
| Field | Value | |
|---|---|---|
| id | FC1 | |
| taxonomy | control.loop.ahu.duct_static_low |
|
| severity | 3 · confirmation_seconds | 300 |
| required_points | duct_static, duct_static_sp, fan_cmd |
Code: AHU-A · param: duct_static_err=0.12, fan_hi=0.87
SELECT timestamp, equipment_id, duct_static, duct_static_sp, fan_cmd,
CASE
WHEN duct_static IS NULL OR duct_static_sp IS NULL OR fan_cmd IS NULL THEN false
WHEN duct_static < duct_static_sp - 0.12
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END >= 0.87 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC2 — MAT below OAT/RAT envelope (GL36 Rule B)
Code: AHU-D · confirmation_seconds: 600 · param: mix_tol=1.15
SELECT timestamp, equipment_id, mat, oat, rat, fan_cmd,
CASE
WHEN mat IS NULL OR oat IS NULL OR rat IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN mat - 1.15 < LEAST(rat - 1.15, oat - 1.15) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC3 — MAT above OAT/RAT envelope (GL36 Rule C)
Code: AHU-D · confirmation_seconds: 600
SELECT timestamp, equipment_id, mat, oat, rat, fan_cmd,
CASE
WHEN mat IS NULL OR oat IS NULL OR rat IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN mat - 1.15 > GREATEST(rat + 1.15, oat + 1.15) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC4 — PID hunting (operating-state oscillation)
Code: AHU-PID-HUNT · confirmation_seconds: 3600 (recommended) · param: delta_os_max=5
Counts excessive operating-mode transitions per hour (heating-only, economizer-only, economizer+mech, mech-only). High transition count indicates PID loops hunting.
Edge SQL — hourly mode-transition count via window (simplified; full resample logic in Pandas FC4):
-- confirmation_seconds: 3600
-- param: delta_os_max = 5, ahu_min_oa_dpr = 0.05
WITH base AS (
SELECT
timestamp,
equipment_id,
CASE
WHEN htg_valve_pct > 0 AND clg_valve_pct = 0
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END > 0
AND oa_damper_pct = 0.05 THEN 'htg'
WHEN htg_valve_pct = 0 AND clg_valve_pct = 0
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END > 0
AND oa_damper_pct > 0.05 THEN 'econ'
WHEN htg_valve_pct = 0 AND clg_valve_pct > 0
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END > 0
AND oa_damper_pct > 0.05 THEN 'econ_mech'
WHEN htg_valve_pct = 0 AND clg_valve_pct > 0
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END > 0
AND oa_damper_pct = 0.05 THEN 'mech'
ELSE 'other'
END AS os_mode
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
),
transitions AS (
SELECT *,
CASE WHEN os_mode <> LAG(os_mode) OVER (ORDER BY timestamp)
AND LAG(os_mode) OVER (ORDER BY timestamp) IS NOT NULL
THEN 1 ELSE 0 END AS mode_change
FROM base
)
SELECT timestamp, equipment_id,
CASE
WHEN SUM(mode_change) OVER (
ORDER BY timestamp
RANGE BETWEEN INTERVAL '1' HOUR PRECEDING AND CURRENT ROW
) > 5 THEN true
ELSE false
END AS fault_raw
FROM transitions
Replace 0.05 with your site’s ahu_min_oa_dpr. Test window syntax in /sql-fdd — DataFusion versions differ on RANGE intervals.
FC5 — SAT cold when heating commanded (GL36 Rule D)
Code: AHU-B · confirmation_seconds: 600 · param: delta_supply_fan=0.55
SELECT timestamp, equipment_id, sat, mat, htg_valve_pct, fan_cmd,
CASE
WHEN sat IS NULL OR mat IS NULL OR htg_valve_pct IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN CASE WHEN htg_valve_pct > 1.0 THEN htg_valve_pct/100.0 ELSE htg_valve_pct END <= 0.01 THEN false
WHEN sat + 1.15 <= mat - 1.15 + 0.55 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC6 — Estimated OA fraction mismatch
Code: AHU-OA-FRAC · confirmation_seconds: 600 · param: airflow_err=0.15, oat_rat_delta_min=5.0
Requires vav_total_flow historian column (summed VAV airflow or fan AFMS).
SELECT timestamp, equipment_id, mat, oat, rat, vav_total_flow,
htg_valve_pct, clg_valve_pct, oa_damper_pct, fan_cmd,
CASE
WHEN mat IS NULL OR oat IS NULL OR rat IS NULL OR vav_total_flow IS NULL THEN false
WHEN ABS(rat - oat) < 5.0 THEN false
WHEN ABS(oat - rat) < 0.01 THEN false
WHEN (
ABS(
GREATEST((mat - rat) / NULLIF(oat - rat, 0), 0)
- (ahu_min_cfm_design / NULLIF(vav_total_flow, 0))
) > 0.15
AND ABS(rat - oat) >= 5.0
AND (
(CASE WHEN htg_valve_pct > 1.0 THEN htg_valve_pct/100.0 ELSE htg_valve_pct END > 0
AND CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END > 0)
OR
(CASE WHEN htg_valve_pct > 1.0 THEN htg_valve_pct/100.0 ELSE htg_valve_pct END = 0
AND CASE WHEN clg_valve_pct > 1.0 THEN clg_valve_pct/100.0 ELSE clg_valve_pct END > 0
AND oa_damper_pct = 0.05)
)
) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Tune ahu_min_cfm_design and 0.05 (ahu_min_oa_dpr) per site.
FC7 — SAT low with full heating (GL36 Rule E)
Code: AHU-C · confirmation_seconds: 600 · param: sat_err=1.0
SELECT timestamp, equipment_id, sat, sat_sp, htg_valve_pct, fan_cmd,
CASE
WHEN sat IS NULL OR sat_sp IS NULL OR htg_valve_pct IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN sat < sat_sp - 1.0
AND CASE WHEN htg_valve_pct > 1.0 THEN htg_valve_pct/100.0 ELSE htg_valve_pct END > 0.9 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC8 — SAT above blend in economizer mode (GL36 Rule F)
Code: AHU-E · confirmation_seconds: 600 · param: ahu_min_oa_dpr=0.05
SELECT timestamp, equipment_id, sat, mat, oa_damper_pct, clg_valve_pct,
CASE
WHEN sat IS NULL OR mat IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN oa_damper_pct <= 0.05 OR clg_valve_pct >= 0.1 THEN false
WHEN ABS(sat - 0.55 - mat) > SQRT(POWER(1.15, 2) + POWER(1.15, 2)) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC9 — OAT too warm for free cooling (GL36 Rule G)
Code: AHU-E · confirmation_seconds: 600
SELECT timestamp, equipment_id, oat, sat_sp, oa_damper_pct, clg_valve_pct,
CASE
WHEN oat IS NULL OR sat_sp IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN oa_damper_pct <= 0.05 OR clg_valve_pct >= 0.1 THEN false
WHEN (oat - 1.15) > (sat_sp - 0.55 + 1.15) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC10 — OAT/MAT mismatch + mech cooling (GL36 Rule H)
Code: AHU-E · confirmation_seconds: 600
SELECT timestamp, equipment_id, mat, oat, oa_damper_pct, clg_valve_pct,
CASE
WHEN mat IS NULL OR oat IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN clg_valve_pct <= 0.01 OR oa_damper_pct <= 0.9 THEN false
WHEN ABS(mat - oat) > SQRT(POWER(1.15, 2) + POWER(1.15, 2)) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC11 — OAT/MAT mismatch economizer-only (GL36 Rule I)
Code: AHU-E · confirmation_seconds: 600
SELECT timestamp, equipment_id, oat, sat_sp, oa_damper_pct, clg_valve_pct,
CASE
WHEN oat IS NULL OR sat_sp IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN clg_valve_pct <= 0.01 OR oa_damper_pct <= 0.9 THEN false
WHEN (oat + 1.15) < (sat_sp - 0.55 - 1.15) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC12 — SAT above blend in cooling (GL36 Rule J)
Code: AHU-B · confirmation_seconds: 600
SELECT timestamp, equipment_id, sat, mat, oa_damper_pct, clg_valve_pct,
CASE
WHEN sat IS NULL OR mat IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN clg_valve_pct <= 0.01 THEN false
WHEN (
(sat - 1.15 - 0.55 > mat + 1.15 AND oa_damper_pct = 0.05)
OR (sat - 1.15 - 0.55 > mat + 1.15 AND oa_damper_pct > 0.9)
) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC13 — SAT above SP at full cooling (GL36 Rule K)
Code: AHU-C · confirmation_seconds: 600 · param: sat_err=1.0
SELECT timestamp, equipment_id, sat, sat_sp, oa_damper_pct, clg_valve_pct,
CASE
WHEN sat IS NULL OR sat_sp IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN clg_valve_pct <= 0.01 THEN false
WHEN sat > sat_sp + 1.0
AND (oa_damper_pct = 0.05 OR oa_damper_pct > 0.9) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC14 — CHW coil ΔT when inactive (GL36 Rule L)
Code: CH-C · confirmation_seconds: 600
SELECT timestamp, equipment_id,
clg_coil_enter_t, clg_coil_leave_t, oa_damper_pct, htg_valve_pct, clg_valve_pct, fan_cmd,
CASE
WHEN clg_coil_enter_t IS NULL OR clg_coil_leave_t IS NULL THEN false
WHEN (clg_coil_enter_t - clg_coil_leave_t) >= SQRT(POWER(1.15, 2) + POWER(1.15, 2)) + 0.55
AND (
(oa_damper_pct > 0.05 AND clg_valve_pct < 0.1)
OR (htg_valve_pct > 0 AND fan_cmd > 0)
) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
FC15 — HW coil ΔT when inactive (GL36 Rule M)
Code: AHU-B · confirmation_seconds: 600
SELECT timestamp, equipment_id,
htg_coil_enter_t, htg_coil_leave_t, oa_damper_pct, htg_valve_pct, clg_valve_pct, fan_cmd,
CASE
WHEN htg_coil_enter_t IS NULL OR htg_coil_leave_t IS NULL THEN false
WHEN (htg_coil_enter_t - htg_coil_leave_t) >= SQRT(POWER(1.15, 2) + POWER(1.15, 2)) + 0.55
AND (
(oa_damper_pct > 0.05 AND clg_valve_pct < 0.1)
OR (clg_valve_pct > 0.01 AND oa_damper_pct = 0.05)
OR (clg_valve_pct > 0.01 AND oa_damper_pct > 0.9)
) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
AHU — Additional patterns
SAT deviation from setpoint
Code: SAT_DEVIATION_HIGH · confirmation_seconds: 600 · param: err=5.0
SELECT timestamp, equipment_id, sat, sat_sp,
CASE
WHEN sat IS NULL OR sat_sp IS NULL THEN false
WHEN ABS(sat - sat_sp) > 5.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Duct static pressure high
Code: DUCT_STATIC_HIGH · confirmation_seconds: 300 · param: margin=0.25
SELECT timestamp, equipment_id, duct_static, duct_static_sp,
CASE
WHEN duct_static IS NULL OR duct_static_sp IS NULL THEN false
WHEN duct_static > duct_static_sp + 0.25 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Heating and cooling simultaneous
Code: HEAT_COOL_SIMULTANEOUS · confirmation_seconds: 300
SELECT timestamp, equipment_id, htg_valve_pct, clg_valve_pct,
CASE
WHEN htg_valve_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN htg_valve_pct > 10.0 AND clg_valve_pct > 10.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Fan off but duct still warm
Code: FAN_OFF_DUCT_WARM · confirmation_seconds: 600 · param: delta=15.0
SELECT timestamp, equipment_id, fan_cmd, duct_t, oa_t,
CASE
WHEN fan_cmd IS NULL OR duct_t IS NULL OR oa_t IS NULL THEN false
WHEN fan_cmd = false AND duct_t > oa_t + 15.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
VAV zones
VAV-1 — Zone temperature comfort band
Code: ZONE_TEMP_OUT_OF_BAND · confirmation_seconds: 900 · param: lo=68, hi=76
SELECT timestamp, equipment_id, zone_t,
CASE
WHEN zone_t IS NULL THEN false
WHEN zone_t < 68.0 OR zone_t > 76.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
VAV-2 — Night setback miss
Code: NIGHT_SETBACK_MISS · confirmation_seconds: 1800 · param: unocc_max=78
SELECT timestamp, equipment_id, zone_t, occ_mode,
CASE
WHEN zone_t IS NULL OR occ_mode IS NULL THEN false
WHEN occ_mode = 'unoccupied' AND zone_t > 78.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
VAV-3 — Excessive reheat during warm weather
Code: VAV-REHEAT-WARM · confirmation_seconds: 300 · param: oat_cutoff=78, reheat_min=0.52
SELECT timestamp, equipment_id, oa_t, reheat_valve_pct,
CASE
WHEN oa_t IS NULL OR reheat_valve_pct IS NULL THEN false
WHEN oa_t > 78.0
AND CASE WHEN reheat_valve_pct > 1.0 THEN reheat_valve_pct/100.0 ELSE reheat_valve_pct END > 0.52 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
VAV-4 — Damper stuck at full open
Code: VAV-DPR-100 · confirmation_seconds: 900 · param: full_open=97.5
Use confirmation to approximate sustained full-open (rolling min is easier in Pandas VAV-4):
SELECT timestamp, equipment_id, damper_pct,
CASE
WHEN damper_pct IS NULL THEN false
WHEN damper_pct > 97.5 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
Economizer & ventilation
ECON-1 — Economizer stuck closed
Code: ECONOMIZER_STUCK_CLOSED · confirmation_seconds: 600
SELECT timestamp, equipment_id, fan_cmd, oa_damper_pct, oa_t,
CASE
WHEN fan_cmd IS NULL OR oa_damper_pct IS NULL OR oa_t IS NULL THEN false
WHEN fan_cmd = true AND oa_damper_pct < 5.0 AND oa_t > 55.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
ECON-2 — Economizing when outdoor unfavorable
Code: ECON-WHEN-SHOULDNT · confirmation_seconds: 300 · param: oat_cutoff=63, dpr_min=0.42
SELECT timestamp, equipment_id, oa_t, oa_damper_pct,
CASE
WHEN oa_t IS NULL OR oa_damper_pct IS NULL THEN false
WHEN oa_t > 63.0 AND oa_damper_pct > 0.42 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
ECON-3 — Mech cooling when econ available
Code: MECH-WHEN-ECON · confirmation_seconds: 300
SELECT timestamp, equipment_id, oa_t, oa_damper_pct, clg_valve_pct,
CASE
WHEN oa_t IS NULL OR oa_damper_pct IS NULL OR clg_valve_pct IS NULL THEN false
WHEN oa_t < 63.0 AND oa_damper_pct < 0.32 AND clg_valve_pct > 0.01 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
ECON-4 — Low estimated OA fraction
Code: LOW-OA-FRAC · confirmation_seconds: 600 · param: oa_min_pct=21
SELECT timestamp, equipment_id, mat, rat, oat, fan_cmd,
CASE
WHEN mat IS NULL OR rat IS NULL OR oat IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN ABS(rat - oat) <= 2.2 THEN false
WHEN ((mat - rat) / NULLIF(oat - rat, 0) * 100.0) < 21.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
ECON-5 — Preheat over-conditioning
Code: PREHEAT-WASTE · confirmation_seconds: 600 · param: excess_tol=2.2
SELECT timestamp, equipment_id, preheat_leave_t, sat_sp, oa_t, htg_valve_pct,
CASE
WHEN preheat_leave_t IS NULL OR sat_sp IS NULL OR oa_t IS NULL OR htg_valve_pct IS NULL THEN false
WHEN htg_valve_pct <= 0.01 THEN false
WHEN (
(oa_t > sat_sp AND preheat_leave_t - oa_t > 2.2)
OR (oa_t < sat_sp AND preheat_leave_t - sat_sp > 2.2)
) THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Central plants
CHW-1 — Low chilled-water delta-T
Code: CHW-DT-001 · confirmation_seconds: 900 · param: min_dt=4.0
SELECT timestamp, equipment_id, chw_supply_t, chw_return_t, chw_pump_cmd,
CASE
WHEN chw_supply_t IS NULL OR chw_return_t IS NULL THEN false
WHEN chw_pump_cmd IS NULL OR chw_pump_cmd = false THEN false
WHEN (chw_return_t - chw_supply_t) < 4.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
CHW-2 — DP below SP at max pump speed
Code: CHW-DP-001 · confirmation_seconds: 300 · param: dp_margin=2.2, pmp_hi=0.87
SELECT timestamp, equipment_id, chw_dp, chw_dp_sp, chw_pump_cmd,
CASE
WHEN chw_dp IS NULL OR chw_dp_sp IS NULL OR chw_pump_cmd IS NULL THEN false
WHEN chw_dp < chw_dp_sp - 2.2
AND CASE WHEN chw_pump_cmd > 1.0 THEN chw_pump_cmd/100.0 ELSE chw_pump_cmd END >= 0.87 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
CHW-3 — Plant supply temp outside deadband
Code: CHW-TEMP-DB · confirmation_seconds: 300 · param: sp_band=2.2
SELECT timestamp, equipment_id, chw_supply_t, chw_supply_t_sp, chw_pump_cmd,
CASE
WHEN chw_supply_t IS NULL OR chw_supply_t_sp IS NULL OR chw_pump_cmd IS NULL THEN false
WHEN chw_pump_cmd <= 0.01 THEN false
WHEN chw_supply_t < chw_supply_t_sp - 2.2 OR chw_supply_t > chw_supply_t_sp + 2.2 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
CHW-4 — Flow high at max pump
Code: CHW-FLOW-HIGH · confirmation_seconds: 300 · param: flow_hi=1100
SELECT timestamp, equipment_id, chw_flow, chw_pump_cmd,
CASE
WHEN chw_flow IS NULL OR chw_pump_cmd IS NULL THEN false
WHEN chw_flow > 1100.0
AND CASE WHEN chw_pump_cmd > 1.0 THEN chw_pump_cmd/100.0 ELSE chw_pump_cmd END >= 0.87 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
Validate with POST /api/fdd/inject-scenario before enabling on live plant points.
Heat pumps
HP-1 — Discharge cold when heating
Code: HP-D · confirmation_seconds: 600 · param: min_sat=85, zone_cold=69
SELECT timestamp, equipment_id, sat, zone_t, fan_cmd,
CASE
WHEN sat IS NULL OR zone_t IS NULL OR fan_cmd IS NULL THEN false
WHEN fan_cmd <= 0.01 THEN false
WHEN zone_t < 69.0 AND sat < 85.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-hp'
Weather station
WX-1 — Temperature spike between readings
Code: WX-SPIKE · confirmation_seconds: 300 · param: spike_limit=16.0
Edge SQL — compare to previous sample:
SELECT timestamp, equipment_id, oa_t,
CASE
WHEN oa_t IS NULL THEN false
WHEN ABS(oa_t - LAG(oa_t) OVER (ORDER BY timestamp)) > 16.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:weather-station'
WX-2 — Gust lower than sustained wind
Code: WX-GUST · confirmation_seconds: 300
SELECT timestamp, equipment_id, wind_gust, wind_speed,
CASE
WHEN wind_gust IS NULL OR wind_speed IS NULL THEN false
WHEN wind_gust < wind_speed THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:weather-station'
Trim & respond advisory (GL36)
Advisory faults for sequence health — use confirmation_seconds: 1800 or higher.
TRIM-1 — Duct static trim advisory
Code: AHU-TRIM-ADV
SELECT timestamp, equipment_id, duct_static, vav_press_req_sum,
CASE
WHEN duct_static IS NULL OR vav_press_req_sum IS NULL THEN false
WHEN duct_static > 0.80 AND vav_press_req_sum < 1.0 AND duct_static > 1.35 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
TRIM-2 — Chiller plant enable advisory
Code: CHW-ENABLE-ADV
SELECT timestamp, equipment_id, chw_valve_req_count,
CASE
WHEN chw_valve_req_count IS NULL THEN false
WHEN chw_valve_req_count < 2 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
TRIM-3 — HWST trim advisory
Code: HW-TRIM-ADV
SELECT timestamp, equipment_id, hw_supply_t, hw_reset_req_sum,
CASE
WHEN hw_supply_t IS NULL OR hw_reset_req_sum IS NULL THEN false
WHEN hw_supply_t < 120.0 AND hw_reset_req_sum > 2.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-boiler-plant'
TRIM-4 — CHW plant reset advisory
Code: CHW-RESET-ADV
SELECT timestamp, equipment_id, chw_supply_t, chw_reset_req_sum,
CASE
WHEN chw_supply_t IS NULL OR chw_reset_req_sum IS NULL THEN false
WHEN chw_supply_t < 45.0 AND chw_reset_req_sum < 1.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
Extended rule families (v2)
Standards-first rules from public FDD / re-tuning literature. Each has a matching Pandas section. Metadata follows the rule schema. Use prerequisite macros inline.
RESET-1 — SAT reset not tracking outdoor air
| Field | Value |
|---|---|
| taxonomy_path | reset.ahu.sat_oa_reset_missing |
| severity | 2 · confirmation_seconds: 900 |
| required_points | sat_sp, oat, fan_status |
| prerequisites | macro.fan_proven_on, macro.reset_enabled |
Intent: Supply air temperature setpoint should reset with outdoor air when reset is enabled (ASHRAE GL36 / AIRCx common finding).
-- confirmation_seconds: 900
-- param: sat_reset_err_max = 3.0 (site-adjustable °F)
SELECT timestamp, equipment_id, sat_sp, oat, fan_status,
CASE
WHEN sat_sp IS NULL OR oat IS NULL OR fan_status IS NULL THEN false
WHEN fan_status = false THEN false
WHEN COALESCE(reset_enable, true) = false THEN false
WHEN ABS(sat_sp - (52.0 + 0.25 * (oat - 65.0))) > 3.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Validation: normal cooling day → false; fixed SAT SP on hot day → true; missing sat_sp → false.
SCHED-1 — Equipment running while unoccupied
| Field | Value |
|---|---|
| taxonomy_path | schedule.ahu.unoccupied_runtime |
| severity | 2 · confirmation_seconds: 1800 |
| required_points | fan_status, occ_mode |
-- confirmation_seconds: 1800
SELECT timestamp, equipment_id, fan_status, occ_mode,
CASE
WHEN fan_status IS NULL OR occ_mode IS NULL THEN false
WHEN occ_mode = 'unoccupied' AND fan_status = true THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
OVR-1 — Persistent manual override
| Field | Value |
|---|---|
| taxonomy_path | override.ahu.persistent_manual |
| severity | 2 · confirmation_seconds: 3600 |
| required_points | override_active |
-- confirmation_seconds: 3600
SELECT timestamp, equipment_id, override_active,
CASE
WHEN override_active IS NULL THEN false
WHEN override_active = true THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
CMD-1 — Fan command vs status mismatch
| Field | Value |
|---|---|
| taxonomy_path | command.status.ahu.fan_cmd_status |
| severity | 3 · confirmation_seconds: 600 |
| required_points | fan_cmd, fan_status |
-- confirmation_seconds: 600
SELECT timestamp, equipment_id, fan_cmd, fan_status,
CASE
WHEN fan_cmd IS NULL OR fan_status IS NULL THEN false
WHEN (CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END >= 0.05)
<> fan_status THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
OA-1 — Low estimated outdoor air fraction
| Field | Value |
|---|---|
| taxonomy_path | ventilation.ahu.low_oa_fraction |
| severity | 2 · confirmation_seconds: 900 |
| required_points | oa_t, ra_t, mat, fan_status |
-- confirmation_seconds: 900
-- param: min_oa_frac = 0.15
SELECT timestamp, equipment_id, oa_t, ra_t, mat, fan_status,
CASE
WHEN oa_t IS NULL OR ra_t IS NULL OR mat IS NULL OR fan_status IS NULL THEN false
WHEN fan_status = false THEN false
WHEN ABS(ra_t - oa_t) < 0.5 THEN false
WHEN ((mat - ra_t) / NULLIF(oa_t - ra_t, 0)) < 0.15 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
VLV-1 — Cooling valve leakage
| Field | Value |
|---|---|
| taxonomy_path | actuator.leakage.ahu.clg_valve |
| severity | 2 · confirmation_seconds: 900 |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, clg_valve_pct, sat, sat_sp,
CASE
WHEN clg_valve_pct IS NULL OR sat IS NULL OR sat_sp IS NULL THEN false
WHEN CASE WHEN clg_valve_pct > 1.0 THEN clg_valve_pct/100.0 ELSE clg_valve_pct END > 0.05 THEN false
WHEN sat < sat_sp - 2.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
DMP-1 — OA damper leakage
| Field | Value |
|---|---|
| taxonomy_path | actuator.leakage.ahu.oa_damper |
| severity | 2 · confirmation_seconds: 900 |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, oa_damper_pct, oa_t, mat,
CASE
WHEN oa_damper_pct IS NULL OR oa_t IS NULL OR mat IS NULL THEN false
WHEN CASE WHEN oa_damper_pct > 1.0 THEN oa_damper_pct/100.0 ELSE oa_damper_pct END > 0.05 THEN false
WHEN ABS(mat - oa_t) < 2.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
VAV-5 — Airflow sensor bias
| Field | Value |
|---|---|
| taxonomy_path | terminal.vav.airflow_sensor_bias |
| severity | 2 · confirmation_seconds: 900 |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, zone_flow, damper_pct,
CASE
WHEN zone_flow IS NULL OR damper_pct IS NULL THEN false
WHEN zone_flow > 50.0
AND CASE WHEN damper_pct > 1.0 THEN damper_pct/100.0 ELSE damper_pct END < 0.10 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
PLANT-1 — CHW DP reset missing
| Field | Value |
|---|---|
| taxonomy_path | reset.plant.chw.dp_reset_missing |
| severity | 2 · confirmation_seconds: 900 |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, chw_dp_sp, chw_load_pct, chw_pump_cmd,
CASE
WHEN chw_dp_sp IS NULL OR chw_load_pct IS NULL OR chw_pump_cmd IS NULL THEN false
WHEN chw_pump_cmd <= 0.01 THEN false
WHEN chw_load_pct < 0.40 AND chw_dp_sp > 18.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-chiller-plant'
SP-HIGH — Occupied zone setpoint too high
-- SP-HIGH confirmation_seconds: 900 param: hi = 76
SELECT timestamp, equipment_id, zone_t_sp, occ_mode,
CASE
WHEN zone_t_sp IS NULL OR occ_mode IS NULL THEN false
WHEN occ_mode <> 'occupied' THEN false
WHEN zone_t_sp > 76.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot WHERE equipment_id = 'equip:your-vav';
SP-LOW — Occupied zone setpoint too low
-- SP-LOW confirmation_seconds: 900 param: lo = 68
SELECT timestamp, equipment_id, zone_t_sp, occ_mode,
CASE
WHEN zone_t_sp IS NULL OR occ_mode IS NULL THEN false
WHEN occ_mode <> 'occupied' THEN false
WHEN zone_t_sp < 68.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot WHERE equipment_id = 'equip:your-vav';
KPI-1 — Performance score (advisory)
Aggregate confirmed faults by family — see benchmark strategy.
Extended rule families (P2)
Next-priority rules from roadmap. Matching Pandas P2 sections. Metadata in P0 catalog.
VAV-6 — Reheat active when cooling available
| Field | Value |
|---|---|
| taxonomy_path | terminal.vav.reheat_with_cooling |
| severity | 2 · confirmation_seconds: 300 |
| required_points | reheat_valve_pct, clg_available, oa_t |
-- confirmation_seconds: 300
SELECT timestamp, equipment_id, reheat_valve_pct, clg_available, oa_t,
CASE
WHEN reheat_valve_pct IS NULL OR oa_t IS NULL THEN false
WHEN COALESCE(clg_available, false) = false THEN false
WHEN oa_t >= 65.0 THEN false
WHEN CASE WHEN reheat_valve_pct > 1.0 THEN reheat_valve_pct/100.0 ELSE reheat_valve_pct END > 0.25 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
VAV-7 — Minimum airflow violation
| Field | Value |
|---|---|
| taxonomy_path | terminal.vav.min_airflow_violation |
| severity | 2 · confirmation_seconds: 900 |
| required_points | zone_flow, min_flow_sp, fan_cmd |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, zone_flow, min_flow_sp, fan_cmd,
CASE
WHEN zone_flow IS NULL OR min_flow_sp IS NULL OR fan_cmd IS NULL THEN false
WHEN CASE WHEN fan_cmd > 1.0 THEN fan_cmd/100.0 ELSE fan_cmd END <= 0.01 THEN false
WHEN zone_flow < min_flow_sp THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-vav'
TOWER-1 — Cooling tower approach too high
| Field | Value |
|---|---|
| taxonomy_path | plant.performance.tower.approach_high |
| severity | 2 · confirmation_seconds: 900 |
| required_points | tower_leaving_t, wb_t, tower_fan_cmd |
-- confirmation_seconds: 900
-- param: max_approach = 7.0 (°F, site-adjustable)
SELECT timestamp, equipment_id, tower_leaving_t, wb_t, tower_fan_cmd,
CASE
WHEN tower_leaving_t IS NULL OR wb_t IS NULL OR tower_fan_cmd IS NULL THEN false
WHEN tower_fan_cmd <= 0.01 THEN false
WHEN (tower_leaving_t - wb_t) > 7.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-cooling-tower'
CTRL-2 — Generic control loop hunting (duct static)
| Field | Value |
|---|---|
| taxonomy_path | control.loop.generic.hunting |
| severity | 2 · confirmation_seconds: 3600 |
| required_points | duct_static, fan_cmd |
-- confirmation_seconds: 3600
-- param: hunt_reversals = 8 (sign changes per hour)
WITH w AS (
SELECT timestamp, equipment_id, duct_static,
LAG(duct_static) OVER (ORDER BY timestamp) AS prev,
CASE
WHEN duct_static > LAG(duct_static) OVER (ORDER BY timestamp) THEN 1
WHEN duct_static < LAG(duct_static) OVER (ORDER BY timestamp) THEN -1
ELSE 0
END AS dir
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
)
SELECT timestamp, equipment_id,
CASE
WHEN COUNT(CASE WHEN dir <> LAG(dir) OVER (ORDER BY timestamp) AND dir <> 0 THEN 1 END)
OVER (ORDER BY timestamp ROWS BETWEEN 59 PRECEDING AND CURRENT ROW) > 8 THEN true
ELSE false
END AS fault_raw
FROM w
Simplified edge variant — use Pandas CTRL-2 for full hourly resample.
SV-7 — Wrong-units heuristic (magnitude check)
| Field | Value |
|---|---|
| taxonomy_path | sensor.quality.generic.wrong_units |
| severity | 2 · confirmation_seconds: 300 |
| required_points | oa_t (or any bounded sensor) |
-- confirmation_seconds: 300
-- Flags values implausible for °F (e.g. 750 = 0.1°C scaling error ×10)
SELECT timestamp, equipment_id, oa_t,
CASE
WHEN oa_t IS NULL THEN false
WHEN oa_t > 200.0 OR oa_t < -60.0 THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
OA-2 — DCV minimum outdoor air not met
| Field | Value |
|---|---|
| taxonomy_path | ventilation.ahu.dcv_minimum_oa |
| severity | 2 · confirmation_seconds: 900 |
| required_points | oa_flow, oa_flow_min_sp, occ_mode, fan_status |
-- confirmation_seconds: 900
SELECT timestamp, equipment_id, oa_flow, oa_flow_min_sp, occ_mode, fan_status,
CASE
WHEN oa_flow IS NULL OR oa_flow_min_sp IS NULL OR occ_mode IS NULL THEN false
WHEN fan_status = false OR occ_mode <> 'occupied' THEN false
WHEN oa_flow < oa_flow_min_sp THEN true
ELSE false
END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'
Framework & parity docs
| Doc | Purpose |
|---|---|
| Public taxonomy | Equipment classes, rule families |
| Rule schema | Declarative source-of-truth |
| Gap matrix | Coverage vs public literature |
| Parity matrix | SQL ↔ Pandas audit |
| Roadmap | Priority-ranked expansion |
| Prerequisite macros | Reusable guards |
| Benchmark strategy | Scenarios & regression |
| Doc template | Per-rule documentation |
Debugging & windowing
| Historian lookback | Rolling / confirmation | |
|---|---|---|
| Set by | SQL FDD test window, validation tab, API | confirmation_seconds (default 300) |
| Typical | 1–24 h for test | 5 min default debounce |
Debug workflow
- Plots — confirm columns exist and values move
- SQL FDD — run SELECT without
fault_rawfirst - Test SQL — add
fault_raw, setconfirmation_seconds: 300 - Validation tab — overlay faults on live trends
- API —
GET /api/agent/validatefor historian parity
Common failures
| Symptom | Check |
|---|---|
| Zero rows | Wrong equipment_id or empty pivot |
| Always false | NULL inputs; wrong column name |
| Always true | Missing NULL guard; threshold too tight |
| No faults on dashboard | Rule not activated; confirmation too long |
Compare with Pandas
Export the same historian window, run the matching Pandas section, diff flagged timestamps — should align within one poll period.
Next: Pandas cookbook — duplicate rules for analyst workflows outside Open-FDD