s7-SCL-scripts/s7-1500/cylinder_monitor/FB_CylinderMonitor.scl

582 lines
32 KiB
Plaintext
Raw Normal View History

(*═══════════════════════════════════════════════════════════════════════════════
FB_CylinderMonitor
S7-1500 SCL — Universal Pneumatic/Hydraulic Cylinder Monitor
TIA Portal V18+ | Optimized Block Access
─────────────────────────────────────────────────────────────────────────────
PURPOSE:
Wraps any cylinder (pneumatic or hydraulic) with:
• Configurable sensor topology (NONE / ONE_FWD / ONE_BWD / TWO)
• Timeout fault generation per motion direction
• Sensor conflict detection
• Intermediate-position detection
• HMI-ready status word, fault code, and text description
• Output gate: only passes actuator command when FB is healthy
→ plug CylCmd_Safe into your sequencer actuator output instead of
the raw command bit
─────────────────────────────────────────────────────────────────────────────
SENSOR TOPOLOGY (SensorConfig input INT):
┌──────┬────────────────────────────────────────────────────────────────────┐
│ 0 │ NONE No position sensors. Advance purely by external timer. │
│ │ No position fault possible. ConflictFault impossible. │
│ 1 │ ONE_FWD One sensor at FORWARD (extended) end only. │
│ │ FWD cmd → timeout if Sen_Fwd not TRUE within T_Timeout │
│ │ BWD cmd → no feedback; only intermediate state reported │
│ 2 │ ONE_BWD One sensor at BACKWARD (retracted) end only. │
│ │ BWD cmd → timeout if Sen_Bwd not TRUE within T_Timeout │
│ │ FWD cmd → no feedback; only intermediate state reported │
│ 3 │ TWO Sensor at BOTH ends (most common in PLC applications) │
│ │ FWD cmd → timeout if Sen_Fwd not TRUE within T_Timeout │
│ │ BWD cmd → timeout if Sen_Bwd not TRUE within T_Timeout │
│ │ BOTH TRUE simultaneously → ConflictFault │
└──────┴────────────────────────────────────────────────────────────────────┘
─────────────────────────────────────────────────────────────────────────────
CYLINDER STATE (CylState output INT):
0 IDLE No command active
1 MOVING_FWD FWD command active, waiting for sensor
2 AT_FWD FWD sensor confirmed (or FWD cmd active, NONE config)
3 MOVING_BWD BWD command active, waiting for sensor
4 AT_BWD BWD sensor confirmed (or BWD cmd active, NONE config)
5 INTERMEDIATE No sensor active when both sensors expected (TWO config)
6 FAULTED Timeout or conflict — CylCmd_Safe output gated OFF
─────────────────────────────────────────────────────────────────────────────
FAULT CODES (FaultCode output INT):
0 No fault
1 FWD timeout — Sen_Fwd did not confirm within T_Timeout after FWD cmd
2 BWD timeout — Sen_Bwd did not confirm within T_Timeout after BWD cmd
3 Conflict — Sen_Fwd AND Sen_Bwd both TRUE at the same time
4 Lost — Cylinder left known position without a command (TWO only)
─────────────────────────────────────────────────────────────────────────────
OUTPUT GATING STRATEGY:
CylCmd_FwdSafe / CylCmd_BwdSafe output ONLY when:
• Input command is TRUE
• FB is NOT faulted
• No conflict detected
Connect these "safe" outputs directly to your DQ outputs or sequencer.
─────────────────────────────────────────────────────────────────────────────
INTEGRATION WITH FB_WeldSequencer:
Each cylinder gets one FB_CylinderMonitor instance.
The sequencer reads Cyl.Fault_Active to trigger a pause or stop.
The sequencer reads Cyl.AtFwd / Cyl.AtBwd for step advance condition.
See FC_CylIntegration at the bottom of this file for wiring example.
═══════════════════════════════════════════════════════════════════════════════*)
FUNCTION_BLOCK "FB_CylinderMonitor"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
//── CYLINDER IDENTITY ────────────────────────────────────────────────────
CylName : String[32]; // Human-readable name for HMI messages
// e.g. 'Clamp Cylinder' or 'Weld Head'
//── SENSOR CONFIGURATION ─────────────────────────────────────────────────
SensorConfig : Int; // 0=NONE 1=ONE_FWD 2=ONE_BWD 3=TWO
T_Timeout : Time; // Fault if position not reached in time
// Typical: T#2S for fast pneumatics
// T#5S for slow hydraulics
//── COMMANDS ─────────────────────────────────────────────────────────────
Cmd_Fwd : Bool; // Command cylinder to extend (FWD)
Cmd_Bwd : Bool; // Command cylinder to retract (BWD)
Cmd_Reset : Bool; // Clear fault (rising edge)
//── SENSOR INPUTS ────────────────────────────────────────────────────────
Sen_Fwd : Bool; // Forward / extended position sensor
Sen_Bwd : Bool; // Backward / retracted position sensor
// Leave FALSE if not wired (NONE/ONE)
//── ENABLE ───────────────────────────────────────────────────────────────
Enable : Bool; // FALSE = ignore commands, hold state
// Use for E-Stop or mode control
END_VAR
VAR_OUTPUT
//── SAFE GATED OUTPUTS ───────────────────────────────────────────────────
// Connect THESE to your DQ / actuator solenoid, not the raw Cmd_Fwd/Bwd
CylCmd_FwdSafe : Bool; // Gated FWD command (only if healthy)
CylCmd_BwdSafe : Bool; // Gated BWD command (only if healthy)
//── POSITION STATUS ──────────────────────────────────────────────────────
AtFwd : Bool; // Cylinder is confirmed at FWD position
AtBwd : Bool; // Cylinder is confirmed at BWD position
Intermediate : Bool; // Cylinder between positions (TWO config)
Moving : Bool; // Command active, position not yet confirmed
//── FAULT STATUS ─────────────────────────────────────────────────────────
Fault_Active : Bool; // TRUE = any fault present
Fault_FwdTimeout : Bool; // FWD timeout fault bit
Fault_BwdTimeout : Bool; // BWD timeout fault bit
Fault_Conflict : Bool; // Sensor conflict fault bit
Fault_Lost : Bool; // Lost position fault bit
//── CODES AND HMI ────────────────────────────────────────────────────────
FaultCode : Int; // Numeric fault code (0 = OK, 1-4 see header)
CylState : Int; // Cylinder state code (0-6, see header)
HMI_StatusText : String[80]; // Ready-to-display operator message
HMI_FaultText : String[80]; // Ready-to-display fault message for HMI
END_VAR
VAR
//── TIMERS ───────────────────────────────────────────────────────────────
_timerFwd : TON; // Starts on FWD command; fault on .Q
_timerBwd : TON; // Starts on BWD command; fault on .Q
//── EDGE DETECTION ───────────────────────────────────────────────────────
_prevReset : Bool;
_prevCmdFwd : Bool;
_prevCmdBwd : Bool;
//── INTERNAL STATE ───────────────────────────────────────────────────────
_faulted : Bool; // Internal faulted latch
_faultCode : Int; // Internal fault code
_cylState : Int; // Internal state code
_lastKnownState : Int; // Last confirmed position (for lost detection)
END_VAR
VAR_TEMP
rReset : Bool;
rCmdFwd : Bool;
rCmdBwd : Bool;
t_Cfg : Int; // Local copy of SensorConfig for readability
END_VAR
VAR CONSTANT
//── SENSOR CONFIG CONSTANTS ──────────────────────────────────────────────
CFG_NONE : Int := 0;
CFG_ONE_FWD : Int := 1;
CFG_ONE_BWD : Int := 2;
CFG_TWO : Int := 3;
//── CYLINDER STATE CONSTANTS ─────────────────────────────────────────────
CYL_IDLE : Int := 0;
CYL_MOVING_FWD : Int := 1;
CYL_AT_FWD : Int := 2;
CYL_MOVING_BWD : Int := 3;
CYL_AT_BWD : Int := 4;
CYL_INTERMEDIATE : Int := 5;
CYL_FAULTED : Int := 6;
//── FAULT CODE CONSTANTS ─────────────────────────────────────────────────
FAULT_NONE : Int := 0;
FAULT_FWD_TIMEOUT : Int := 1;
FAULT_BWD_TIMEOUT : Int := 2;
FAULT_CONFLICT : Int := 3;
FAULT_LOST : Int := 4;
END_VAR
BEGIN
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 1 — EDGE DETECTION ░░
═══════════════════════════════════════════════════════════════════════════*)
rReset := Cmd_Reset AND NOT _prevReset;
rCmdFwd := Cmd_Fwd AND NOT _prevCmdFwd;
rCmdBwd := Cmd_Bwd AND NOT _prevCmdBwd;
_prevReset := Cmd_Reset;
_prevCmdFwd := Cmd_Fwd;
_prevCmdBwd := Cmd_Bwd;
t_Cfg := SensorConfig;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 2 — FAULT RESET ░░
═══════════════════════════════════════════════════════════════════════════*)
IF rReset THEN
_faulted := FALSE;
_faultCode := FAULT_NONE;
Fault_FwdTimeout := FALSE;
Fault_BwdTimeout := FALSE;
Fault_Conflict := FALSE;
Fault_Lost := FALSE;
// Force both timers off on reset
_timerFwd(IN := FALSE, PT := T_Timeout);
_timerBwd(IN := FALSE, PT := T_Timeout);
END_IF;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 3 — SENSOR CONFLICT CHECK (highest priority, every scan) ░░
Only possible with TWO sensor config
═══════════════════════════════════════════════════════════════════════════*)
IF (t_Cfg = CFG_TWO) AND Sen_Fwd AND Sen_Bwd THEN
_faulted := TRUE;
_faultCode := FAULT_CONFLICT;
Fault_Conflict := TRUE;
END_IF;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 4 — TIMEOUT TIMERS ░░
Timer IN = command active AND sensor NOT yet confirmed AND not faulted
This ensures timers only run during active motion, not in hold.
Timers auto-reset when command drops or sensor arrives.
═══════════════════════════════════════════════════════════════════════════*)
// ── FWD timeout timer ─────────────────────────────────────────────────────
// Active when: FWD commanded AND sensor not confirmed AND config allows it
CASE t_Cfg OF
CFG_ONE_FWD, CFG_TWO:
// Timer runs while FWD commanded but Sen_Fwd not yet TRUE
_timerFwd(
IN := Cmd_Fwd AND NOT Sen_Fwd AND NOT _faulted AND Enable,
PT := T_Timeout
);
ELSE:
// NONE or ONE_BWD — no FWD sensor, no FWD timeout possible
_timerFwd(IN := FALSE, PT := T_Timeout);
END_CASE;
// ── BWD timeout timer ─────────────────────────────────────────────────────
CASE t_Cfg OF
CFG_ONE_BWD, CFG_TWO:
_timerBwd(
IN := Cmd_Bwd AND NOT Sen_Bwd AND NOT _faulted AND Enable,
PT := T_Timeout
);
ELSE:
_timerBwd(IN := FALSE, PT := T_Timeout);
END_CASE;
// ── Trigger faults on timer expiry ─────────────────────────────────────
IF _timerFwd.Q AND NOT _faulted THEN
_faulted := TRUE;
_faultCode := FAULT_FWD_TIMEOUT;
Fault_FwdTimeout := TRUE;
END_IF;
IF _timerBwd.Q AND NOT _faulted THEN
_faulted := TRUE;
_faultCode := FAULT_BWD_TIMEOUT;
Fault_BwdTimeout := TRUE;
END_IF;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 5 — LOST POSITION DETECTION (TWO sensor config only) ░░
If both sensors go FALSE when we expect one to be TRUE,
the cylinder has moved without a command (mechanical failure, etc.)
═══════════════════════════════════════════════════════════════════════════*)
IF t_Cfg = CFG_TWO AND NOT _faulted THEN
// We had a confirmed position...
IF _lastKnownState = CYL_AT_FWD OR _lastKnownState = CYL_AT_BWD THEN
// ...but now neither sensor is active and no command is running
IF NOT Sen_Fwd AND NOT Sen_Bwd AND NOT Cmd_Fwd AND NOT Cmd_Bwd THEN
_faulted := TRUE;
_faultCode := FAULT_LOST;
Fault_Lost := TRUE;
END_IF;
END_IF;
END_IF;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 6 — CYLINDER STATE MACHINE ░░
═══════════════════════════════════════════════════════════════════════════*)
IF _faulted THEN
_cylState := CYL_FAULTED;
ELSIF NOT Enable THEN
// Disabled — stay in IDLE regardless
_cylState := CYL_IDLE;
ELSIF Cmd_Fwd AND NOT Cmd_Bwd THEN
// FWD command active
CASE t_Cfg OF
CFG_NONE:
// No sensors — trust the command, immediately "at FWD"
_cylState := CYL_AT_FWD;
CFG_ONE_FWD, CFG_TWO:
IF Sen_Fwd THEN
_cylState := CYL_AT_FWD;
_lastKnownState := CYL_AT_FWD;
ELSE
_cylState := CYL_MOVING_FWD;
END_IF;
CFG_ONE_BWD:
// FWD cmd but sensor is only on BWD side — report moving until BWD clears
IF NOT Sen_Bwd THEN
_cylState := CYL_AT_FWD; // Best we can say: not at BWD
ELSE
_cylState := CYL_MOVING_FWD;
END_IF;
END_CASE;
ELSIF Cmd_Bwd AND NOT Cmd_Fwd THEN
// BWD command active
CASE t_Cfg OF
CFG_NONE:
_cylState := CYL_AT_BWD;
CFG_ONE_BWD, CFG_TWO:
IF Sen_Bwd THEN
_cylState := CYL_AT_BWD;
_lastKnownState := CYL_AT_BWD;
ELSE
_cylState := CYL_MOVING_BWD;
END_IF;
CFG_ONE_FWD:
IF NOT Sen_Fwd THEN
_cylState := CYL_AT_BWD;
ELSE
_cylState := CYL_MOVING_BWD;
END_IF;
END_CASE;
ELSIF NOT Cmd_Fwd AND NOT Cmd_Bwd THEN
// No command — check resting state
CASE t_Cfg OF
CFG_NONE:
_cylState := CYL_IDLE;
CFG_ONE_FWD:
IF Sen_Fwd THEN
_cylState := CYL_AT_FWD;
ELSE
_cylState := CYL_IDLE; // Could be at BWD, we don't know
END_IF;
CFG_ONE_BWD:
IF Sen_Bwd THEN
_cylState := CYL_AT_BWD;
ELSE
_cylState := CYL_IDLE;
END_IF;
CFG_TWO:
IF Sen_Fwd AND NOT Sen_Bwd THEN
_cylState := CYL_AT_FWD;
_lastKnownState := CYL_AT_FWD;
ELSIF Sen_Bwd AND NOT Sen_Fwd THEN
_cylState := CYL_AT_BWD;
_lastKnownState := CYL_AT_BWD;
ELSIF NOT Sen_Fwd AND NOT Sen_Bwd THEN
_cylState := CYL_INTERMEDIATE;
END_IF;
END_CASE;
ELSIF Cmd_Fwd AND Cmd_Bwd THEN
// Both commands simultaneously → command conflict (programming error)
// Treat same as conflict fault
_faulted := TRUE;
_faultCode := FAULT_CONFLICT;
Fault_Conflict := TRUE;
_cylState := CYL_FAULTED;
END_IF;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 7 — SAFE OUTPUT GATING ░░
Only energise actuator outputs when healthy and enabled.
This is the key safety pattern: raw command passes through only
if the FB is not faulted.
═══════════════════════════════════════════════════════════════════════════*)
CylCmd_FwdSafe := Cmd_Fwd AND NOT _faulted AND Enable;
CylCmd_BwdSafe := Cmd_Bwd AND NOT _faulted AND Enable;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 8 — STATUS BOOL OUTPUTS ░░
═══════════════════════════════════════════════════════════════════════════*)
AtFwd := (_cylState = CYL_AT_FWD);
AtBwd := (_cylState = CYL_AT_BWD);
Intermediate := (_cylState = CYL_INTERMEDIATE);
Moving := (_cylState = CYL_MOVING_FWD) OR (_cylState = CYL_MOVING_BWD);
Fault_Active := _faulted;
FaultCode := _faultCode;
CylState := _cylState;
(*═══════════════════════════════════════════════════════════════════════════
░░ SECTION 9 — HMI TEXT GENERATION ░░
Pre-formatted strings the HMI can display directly.
String 80 chars max. Use CylName to identify which cylinder.
═══════════════════════════════════════════════════════════════════════════*)
// ── Status text ───────────────────────────────────────────────────────────
CASE _cylState OF
CYL_IDLE:
HMI_StatusText := CONCAT(CylName, ': Idle / No command');
CYL_MOVING_FWD:
HMI_StatusText := CONCAT(CylName, ': Moving → FWD ...');
CYL_AT_FWD:
HMI_StatusText := CONCAT(CylName, ': At FWD position ✓');
CYL_MOVING_BWD:
HMI_StatusText := CONCAT(CylName, ': Moving ← BWD ...');
CYL_AT_BWD:
HMI_StatusText := CONCAT(CylName, ': At BWD position ✓');
CYL_INTERMEDIATE:
HMI_StatusText := CONCAT(CylName, ': INTERMEDIATE — between sensors');
CYL_FAULTED:
HMI_StatusText := CONCAT(CylName, ': *** FAULT — see fault message ***');
ELSE:
HMI_StatusText := CONCAT(CylName, ': Unknown state');
END_CASE;
// ── Fault text ────────────────────────────────────────────────────────────
CASE _faultCode OF
FAULT_NONE:
HMI_FaultText := '';
FAULT_FWD_TIMEOUT:
HMI_FaultText := CONCAT(CylName,
': FWD TIMEOUT — check cylinder, solenoid and FWD sensor');
FAULT_BWD_TIMEOUT:
HMI_FaultText := CONCAT(CylName,
': BWD TIMEOUT — check cylinder, solenoid and BWD sensor');
FAULT_CONFLICT:
HMI_FaultText := CONCAT(CylName,
': SENSOR CONFLICT — FWD + BWD both active. Check wiring/sensors');
FAULT_LOST:
HMI_FaultText := CONCAT(CylName,
': LOST POSITION — cylinder moved without command. Check mechanics');
ELSE:
HMI_FaultText := CONCAT(CylName, ': Unknown fault');
END_CASE;
END_FUNCTION_BLOCK
(*═══════════════════════════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════════════════════════
FB_CylFaultCollector
Aggregates faults from up to 8 cylinder monitors into one summary word.
The welding sequencer reads CylFaultAny to trigger a machine pause or stop.
HMI reads CylFaultWord bitmask + individual fault strings for alarm display.
═══════════════════════════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════════════════════════*)
FUNCTION_BLOCK "FB_CylFaultCollector"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
//── FAULT INPUTS FROM UP TO 8 CYLINDER MONITORS ─────────────────────────
Cyl1_Fault : Bool;
Cyl2_Fault : Bool;
Cyl3_Fault : Bool;
Cyl4_Fault : Bool;
Cyl5_Fault : Bool;
Cyl6_Fault : Bool;
Cyl7_Fault : Bool;
Cyl8_Fault : Bool;
//── FAULT CODES (from each FB_CylinderMonitor.FaultCode) ────────────────
Cyl1_FaultCode : Int;
Cyl2_FaultCode : Int;
Cyl3_FaultCode : Int;
Cyl4_FaultCode : Int;
Cyl5_FaultCode : Int;
Cyl6_FaultCode : Int;
Cyl7_FaultCode : Int;
Cyl8_FaultCode : Int;
//── FAULT TEXTS (from each FB_CylinderMonitor.HMI_FaultText) ────────────
Cyl1_FaultText : String[80];
Cyl2_FaultText : String[80];
Cyl3_FaultText : String[80];
Cyl4_FaultText : String[80];
//── MACHINE RESPONSE CONFIGURATION ──────────────────────────────────────
// For each fault type, define machine response:
// 0 = Warning only (continue running, amber lamp)
// 1 = Pause machine (Cmd_Pause equivalent)
// 2 = Stop machine (Cmd_Stop equivalent)
Response_Timeout : Int; // Response for timeout faults (codes 1, 2)
Response_Conflict : Int; // Response for conflict fault (code 3)
Response_Lost : Int; // Response for lost position (code 4)
END_VAR
VAR_OUTPUT
//── AGGREGATE OUTPUTS (wire to FB_WeldSequencer inputs) ──────────────────
CylFaultAny : Bool; // OR of all faults — any cylinder faulted
CylFaultWord : Word; // Bitmask: bit0=Cyl1...bit7=Cyl8
Cmd_MachinePause : Bool; // → connect to FB_WeldSequencer.Cmd_Pause
Cmd_MachineStop : Bool; // → connect to FB_WeldSequencer.Cmd_Stop
Cmd_Warning : Bool; // Amber warning lamp (non-stopping fault)
//── FIRST FAULT REPORTING (for HMI alarm banner) ─────────────────────────
FirstFaultCode : Int; // Code of first active fault
FirstFaultText : String[80]; // Text of first active fault
ActiveFaultCount : Int; // How many cylinders currently faulted
END_VAR
VAR
_faultBits : Array[1..8] OF Bool;
_faultCodes : Array[1..8] OF Int;
_i : Int;
_faultCount : Int;
_firstFound : Bool;
_anyPause : Bool;
_anyStop : Bool;
_anyWarn : Bool;
_requiredResponse : Int;
END_VAR
BEGIN
// ── Collect all faults into arrays ────────────────────────────────────────
_faultBits[1] := Cyl1_Fault; _faultCodes[1] := Cyl1_FaultCode;
_faultBits[2] := Cyl2_Fault; _faultCodes[2] := Cyl2_FaultCode;
_faultBits[3] := Cyl3_Fault; _faultCodes[3] := Cyl3_FaultCode;
_faultBits[4] := Cyl4_Fault; _faultCodes[4] := Cyl4_FaultCode;
_faultBits[5] := Cyl5_Fault; _faultCodes[5] := Cyl5_FaultCode;
_faultBits[6] := Cyl6_Fault; _faultCodes[6] := Cyl6_FaultCode;
_faultBits[7] := Cyl7_Fault; _faultCodes[7] := Cyl7_FaultCode;
_faultBits[8] := Cyl8_Fault; _faultCodes[8] := Cyl8_FaultCode;
// ── Reset aggregates ──────────────────────────────────────────────────────
CylFaultWord := 0;
CylFaultAny := FALSE;
_faultCount := 0;
_firstFound := FALSE;
FirstFaultCode := 0;
FirstFaultText := '';
_anyPause := FALSE;
_anyStop := FALSE;
_anyWarn := FALSE;
// ── Loop all 8 slots ──────────────────────────────────────────────────────
FOR _i := 1 TO 8 DO
IF _faultBits[_i] THEN
CylFaultAny := TRUE;
_faultCount := _faultCount + 1;
// Set bitmask bit (bit 0 = Cyl1)
CylFaultWord := WORD_TO_WORD(CylFaultWord OR SHL(IN := WORD#16#0001,
N := UINT_TO_INT(INT_TO_UINT(_i - 1))));
// First fault capture for HMI banner
IF NOT _firstFound THEN
_firstFound := TRUE;
FirstFaultCode := _faultCodes[_i];
CASE _i OF
1: FirstFaultText := Cyl1_FaultText;
2: FirstFaultText := Cyl2_FaultText;
3: FirstFaultText := Cyl3_FaultText;
4: FirstFaultText := Cyl4_FaultText;
ELSE: FirstFaultText := 'Cylinder fault (see fault word)';
END_CASE;
END_IF;
// Determine required machine response for this fault code
CASE _faultCodes[_i] OF
1, 2: _requiredResponse := Response_Timeout;
3: _requiredResponse := Response_Conflict;
4: _requiredResponse := Response_Lost;
ELSE: _requiredResponse := 2; // Unknown → Stop
END_CASE;
CASE _requiredResponse OF
0: _anyWarn := TRUE;
1: _anyPause := TRUE;
2: _anyStop := TRUE;
END_CASE;
END_IF;
END_FOR;
ActiveFaultCount := _faultCount;
Cmd_MachinePause := _anyPause AND NOT _anyStop; // Stop takes priority over pause
Cmd_MachineStop := _anyStop;
Cmd_Warning := _anyWarn AND NOT _anyPause AND NOT _anyStop;
END_FUNCTION_BLOCK