(* =====================================================================
   20_fb_pump.st - FB_PUMP, section 4.1

   One instance per pump.  Owns everything about a single unit:
   trips, availability, minimum run/off timers, run hours.

   Additions to the brief's input list, each required to produce a
   specified output and documented in README.md:
     MinOffBypass    - LSHH override of the minimum-off timer (4.1)
     ServiceInterval - %MW10, needed for ServiceDue (4.1)
     ResetHours      - command 5, "reset run hours / service done" (3.3)
   ===================================================================== *)

FUNCTION_BLOCK FB_PUMP
VAR_INPUT
    RunRequest      : BOOL;
    SpeedRef        : REAL;     (* Hz, common to all running units      *)
    ThermalOK       : BOOL;     (* TE-31x, TRUE = healthy               *)
    SealLeak        : BOOL;     (* MSE-31x, TRUE = leak                 *)
    Vibration       : REAL;     (* VE-31x, mm/s                         *)
    DischPressure   : REAL;     (* PIT-31x, kPa                         *)
    ResetTrip       : BOOL;
    Lockout         : BOOL;
    MinOffBypass    : BOOL;
    ServiceInterval : REAL;     (* hours                                *)
    ResetHours      : BOOL;
END_VAR

VAR_OUTPUT
    RunCmd          : BOOL;
    Running         : BOOL;
    Available       : BOOL;
    Tripped         : BOOL;
    State           : INT;      (* enum, section 3.2                    *)
    RunHours        : REAL;
    ServiceDue      : BOOL;
    VibAlarm        : BOOL;     (* 7.1 mm/s, alarm only - bitmask 10-12 *)
    SealAlarm       : BOOL;     (* does not affect availability         *)
END_VAR

VAR CONSTANT
    SCAN_S      : REAL := 0.1;      (* must match TASK INTERVAL          *)
    VIB_ALARM   : REAL := 7.1;      (* mm/s                              *)
    VIB_TRIP    : REAL := 11.0;     (* mm/s                              *)
    NOFLOW_KPA  : REAL := 150.0;
END_VAR

VAR
    MinRunTmr   : TON;
    MinOffTmr   : TON;
    NoFlowTmr   : TON;
    HasRun      : BOOL;     (* pump has run at least once since restart *)
    StartOK     : BOOL;
END_VAR

(* --- 1. Trip reset.  Runs first so that a reset issued while the
       initiating condition is still present re-trips immediately
       rather than latching clear. --------------------------------- *)
IF ResetTrip THEN
    Tripped := FALSE;
END_IF;

(* --- 2. Trip conditions.  All latch; they clear only on reset. ---- *)
IF NOT ThermalOK THEN
    Tripped := TRUE;                            (* TE-31x, section 5   *)
END_IF;

IF Vibration > VIB_TRIP THEN
    Tripped := TRUE;                            (* VE-31x > 11.0 mm/s  *)
END_IF;

(* No-flow: 20 s after RunCmd goes true, low discharge pressure trips
   the unit.  Monitored continuously once the window has elapsed, not
   sampled once, so a loss of flow while running is also caught. *)
NoFlowTmr(IN := RunCmd, PT := T#20s);
IF NoFlowTmr.Q AND (DischPressure < NOFLOW_KPA) THEN
    Tripped := TRUE;
END_IF;

(* --- 3. Alarms that do not affect availability, section 4.1 ------- *)
VibAlarm  := Vibration > VIB_ALARM;
SealAlarm := SealLeak;

(* --- 4. Availability.  A seal leak is deliberately absent here:
       it raises an alarm only, per WRPS-PRO-001 5.5. --------------- *)
Available := ThermalOK AND NOT Tripped AND NOT Lockout;

(* --- 5. Minimum run / minimum off timers.
       MinOff is gated on HasRun so that a cold start is not blocked
       for 5 minutes after a runtime restart. ---------------------- *)
MinRunTmr(IN := RunCmd, PT := T#5m);
MinOffTmr(IN := (NOT RunCmd) AND HasRun, PT := T#5m);

StartOK := (NOT HasRun) OR MinOffTmr.Q OR MinOffBypass;

(* --- 6. Run command ---------------------------------------------- *)
IF Tripped OR Lockout OR NOT ThermalOK THEN
    RunCmd := FALSE;
ELSIF RunCmd THEN
    (* running: honour minimum run before accepting a stop *)
    IF (NOT RunRequest) AND MinRunTmr.Q THEN
        RunCmd := FALSE;
    END_IF;
ELSE
    IF RunRequest AND StartOK THEN
        RunCmd := TRUE;
        HasRun := TRUE;
    END_IF;
END_IF;

Running := RunCmd;

(* --- 7. Run hours.  Scan-time increments while running only. ------ *)
IF ResetHours THEN
    RunHours := 0.0;
END_IF;
IF Running THEN
    RunHours := RunHours + SCAN_S / 3600.0;
END_IF;
ServiceDue := RunHours >= ServiceInterval;

(* --- 8. Published state, section 3.2 ------------------------------ *)
IF Tripped THEN
    State := 6;                                 (* Tripped             *)
ELSIF Lockout THEN
    State := 7;                                 (* Maintenance lockout *)
ELSIF NOT ThermalOK THEN
    State := 0;                                 (* Unavailable         *)
ELSIF Running AND NOT RunRequest THEN
    State := 4;                                 (* Min-run inhibit     *)
ELSIF RunCmd AND NOT NoFlowTmr.Q THEN
    State := 2;                                 (* Start delay         *)
ELSIF Running THEN
    State := 3;                                 (* Running             *)
ELSIF HasRun AND NOT MinOffTmr.Q THEN
    State := 5;                                 (* Min-off inhibit     *)
ELSE
    State := 1;                                 (* Available, stopped  *)
END_IF;

END_FUNCTION_BLOCK
