The PLC program from the old repo's 04-plc/, flattened into one folder and
checked against the running system.
Verified during the move:
- build.py regenerates register-map.csv byte-identically (69 points)
- polled the live PLC: the SIMULATION build is what is deployed and
running, %MW21=2 wet weather, values moving, run hours accumulating
- addresses, %MW HR1024 segmentation and %QW17/%QW7 signedness all
match the map
Corrections against the old repo:
- 10_globals.st header cited WRPS-CTL-002 (the FDS); it means CTL-003
- build.py wrote the map to its parent directory; now beside itself
- deploy/README.md was a single-file folder; now DEPLOY.md
- dropped the empty editor-devices/remote/
- README no longer claims the simulation build is uncompiled - it is
the one running
Two open items are now stated plainly rather than buried:
- none of the 20 acceptance tests in CTL-003 have ever been run
- the OpenPLC Editor lived only on the retired dev-ubuntu host, so
there is currently NO route to deploy a new program (DEPLOY.md 0)
Documents the setpoint distinction: IO_MUX seeds %MW defaults once at
first scan, operators retune them live, and that tuning exists only in
the container volume - a restart reverts it.
97 lines
3.1 KiB
Smalltalk
97 lines
3.1 KiB
Smalltalk
(* =====================================================================
|
|
23_fb_headroom.st - FB_HEADROOM, section 4.4
|
|
|
|
Pure calculation. No control action, no alarms.
|
|
|
|
Reference condition, WRPS-PRO-001 7.3 and pass 1 test 14:
|
|
level 4.00 m, inflow 165 L/s, one pump at 120 L/s
|
|
NetInflow = 45.0 L/s -> %QW7 = 450
|
|
VolToSpill = (6.0 - 4.0) * 120 = 240 m3
|
|
TimeToSpill = 240 * 1000 / 45 = 5333.3 s -> %QW5 = 5333
|
|
===================================================================== *)
|
|
|
|
FUNCTION_BLOCK FB_HEADROOM
|
|
VAR_INPUT
|
|
Level : REAL; (* m *)
|
|
Inflow : REAL; (* L/s, unfiltered *)
|
|
TotalDischarge : REAL; (* L/s *)
|
|
END_VAR
|
|
|
|
VAR_OUTPUT
|
|
InflowFilt : REAL; (* L/s, 30 s lag *)
|
|
NetInflow : REAL; (* L/s *)
|
|
VolToSpill : REAL; (* m3 *)
|
|
VolToLSHH : REAL; (* m3 *)
|
|
TimeToSpill : INT; (* s, 32767 = drawing down *)
|
|
TimeToLSHH : INT; (* s, 32767 = drawing down *)
|
|
END_VAR
|
|
|
|
VAR CONSTANT
|
|
SCAN_S : REAL := 0.1; (* must match TASK INTERVAL *)
|
|
TAU_S : REAL := 30.0; (* inflow filter time constant *)
|
|
AREA_M2 : REAL := 120.0;
|
|
SPILL_M : REAL := 6.000;
|
|
LSHH_M : REAL := 5.500;
|
|
MIN_NET : REAL := 0.5; (* L/s, below this the figure is
|
|
meaningless - drawing down *)
|
|
NO_TIME : INT := 32767;
|
|
MAX_TIME : REAL := 32767.0;
|
|
END_VAR
|
|
|
|
VAR
|
|
Primed : BOOL := FALSE;
|
|
t : REAL;
|
|
END_VAR
|
|
|
|
(* --- Inflow filter. First-order lag, 30 s.
|
|
|
|
Primed on the first scan rather than ramping from zero: without
|
|
this the reference figure in test 14 would take ~2 minutes to
|
|
settle and would not reproduce exactly on demand. ---------------- *)
|
|
IF NOT Primed THEN
|
|
InflowFilt := Inflow;
|
|
Primed := TRUE;
|
|
ELSE
|
|
InflowFilt := InflowFilt + (Inflow - InflowFilt) * SCAN_S / TAU_S;
|
|
END_IF;
|
|
|
|
NetInflow := InflowFilt - TotalDischarge;
|
|
|
|
VolToSpill := (SPILL_M - Level) * AREA_M2;
|
|
VolToLSHH := (LSHH_M - Level) * AREA_M2;
|
|
IF VolToSpill < 0.0 THEN
|
|
VolToSpill := 0.0;
|
|
END_IF;
|
|
IF VolToLSHH < 0.0 THEN
|
|
VolToLSHH := 0.0;
|
|
END_IF;
|
|
|
|
(* --- Time to spill weir ------------------------------------------- *)
|
|
IF NetInflow <= MIN_NET THEN
|
|
TimeToSpill := NO_TIME;
|
|
ELSE
|
|
t := VolToSpill * 1000.0 / NetInflow;
|
|
IF t >= MAX_TIME THEN
|
|
TimeToSpill := NO_TIME;
|
|
ELSIF t < 0.0 THEN
|
|
TimeToSpill := 0;
|
|
ELSE
|
|
TimeToSpill := REAL_TO_INT(t);
|
|
END_IF;
|
|
END_IF;
|
|
|
|
(* --- Time to LSHH -------------------------------------------------- *)
|
|
IF NetInflow <= MIN_NET THEN
|
|
TimeToLSHH := NO_TIME;
|
|
ELSE
|
|
t := VolToLSHH * 1000.0 / NetInflow;
|
|
IF t >= MAX_TIME THEN
|
|
TimeToLSHH := NO_TIME;
|
|
ELSIF t < 0.0 THEN
|
|
TimeToLSHH := 0;
|
|
ELSE
|
|
TimeToLSHH := REAL_TO_INT(t);
|
|
END_IF;
|
|
END_IF;
|
|
|
|
END_FUNCTION_BLOCK
|