DUTs

E_DeviceState

{attribute 'qualified_only'}
TYPE E_DeviceState :
(
    INVALID := 0,
    INIT := EC_DEVICE_STATE_INIT,
    PREOP := EC_DEVICE_STATE_PREOP,
    BOOTSTRAP := EC_DEVICE_STATE_BOOTSTRAP,
    SAFEOP := EC_DEVICE_STATE_SAFEOP,
    OP := EC_DEVICE_STATE_OP
) WORD := INVALID;
END_TYPE
Related:

E_LeakLocation

{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_LeakLocation :
(
    NO_LEAK := 0,
    LOC1 := 1,
    LOC2 := 2,
    LOC3 := 4,
    LOC4 := 8,
    LOC5 := 16,
    LOC6 := 32
) BYTE;
END_TYPE

ST_DeviceDiagnostic

// PLC Terminal device diagnostics
TYPE ST_DeviceDiagnostic :
STRUCT
    {attribute 'pytmc' := '
        pv: TYPE
        io: i
    '}
    sType : STRING[15];
    {attribute 'pytmc' := '
        pv: ADDRESS
        io: i
    '}
    nAddr : WORD;
    {attribute 'pytmc' := '
        pv: NAME
        io: i
    '}
    sName : STRING[31];
    {attribute 'pytmc' := '
        pv: DEVICE_STATE
        io: i
        field: ZRVL 0
        field: ONVL 1
        field: TWVL 2
        field: THVL 3
        field: FRVL 4
        field: SXVL 8
        field: ZRST INVALID
        field: ONST INIT
        field: TWST PREOP
        field: THST BOOTSTRAP
        field: FRST SAFEOP
        field: SXST OP
        field: ZRSV MAJOR
        field: ONSV MAJOR
        field: TWSV MAJOR
        field: THSV MAJOR
        field: FRSV MAJOR
        field: FVSV MINOR
        field: SXSV NO_ALARM
        field: UNSV MAJOR
    '}
    nDeviceState : ENUM;
    {attribute 'pytmc' := '
        pv: DEVICE_STATE_FLAGS
        io: i
    '}
    nDeviceStateFlags : BYTE;
    {attribute 'pytmc' := '
        pv: LINK_STATE_FLAGS
        io: i
    '}
    nLinkStateFlags : BYTE;
END_STRUCT
END_TYPE

ST_ErrorStates

// Error states that can occur in the program
TYPE ST_ErrorStates :
STRUCT
    // Leak Alarms
    bLeakOpticalTableAmphosOn : BOOL; // Leak on the optical table with Amphos on
    bLeakOpticalTableNoLaser : BOOL; // Leak on the optical table with no laser on
    bLeakOpcpaCarbideOn : BOOL; // Leak inside the OPCPA with Carbide on
    bLeakUnderTable : BOOL; // Leak under the table
    bLeakInsideRacks : BOOL; // Leak inside the racks

    // Temperature loop alarms
    bChillerLoop01 : BOOL; // Temperature too high in loop 01
    bChillerLoop02 : BOOL; // Temperature too high in loop 02
    bChillerLoop03 : BOOL; // Temperature too high in loop 03
    bChillerLoop04 : BOOL; // Temperature too high in loop 04

    // Beam Errors
    bAmphosBeam : BOOL; // Amphos beam error
    bOpcpaBeam : BOOL; // OPCPA beam error

    (*
    // Temperature monitors
    bTempMon01 : BOOL; // Temperature too high at location 01
    bTempMon02 : BOOL; // Temperature too high at location 02
    bTempMon03 : BOOL; // Temperature too high at location 03
    bTempMon04 : BOOL; // Temperature too high at location 04
    bTempMon05 : BOOL; // Temperature too high at location 05
    bTempMon06 : BOOL; // Temperature too high at location 06
    bTempMon07 : BOOL; // Temperature too high at location 07
    bTempMon08 : BOOL; // Temperature too high at location 08
    *)

    // Dump chiller
    bDumpChiller : BOOL; // Dump chiller error
    bBaseplateChiller : BOOL; // Baseplate chiller error

    (*
    // Amphos MRC
    bAmphosMRC01 : BOOL; // Amphos MRC 01 Error
    bAmphosMRC02 : BOOL; // Amphos MRC 01 Error
    bAmphosMRC03 : BOOL; // Amphos MRC 01 Error
    *)

    // Hardware Failure
    bHardwareFailure : BOOL; // A component of the EPS has failed

END_STRUCT
END_TYPE

ST_LaserSetpoints

// Setpoints for the lasers
TYPE ST_LaserSetpoints :
STRUCT
    {attribute 'pytmc' := '
        pv: MIN_VOLTAGE
        io: io
        field: EGU Volts
    '}
    nMinVoltage : LREAL; // Minimum voltage the laser will be allowed to operate at
    {attribute 'pytmc' := '
        pv: MIN_NOMINAL_VOLTAGE
        io: io
        field: EGU Volts
    '}
    nMinNominalVoltage : LREAL; // Minimum voltage for the laser to be operating nominally
END_STRUCT
END_TYPE

ST_LeakBoundaries

// Boundaries indicating leak monitor locations
TYPE ST_LeakBoundaries :
STRUCT
    {attribute 'pytmc' := '
        pv: L_MIN01
        io: io
    '}
    nLMin1 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX01
        io: io
    '}
    nLMax1 : UINT;
    {attribute 'pytmc' := '
        pv: L_MIN02
        io: io
    '}
    nLMin2 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX02
        io: io
    '}
    nLMax2 : UINT;
    {attribute 'pytmc' := '
        pv: L_MIN03
        io: io
    '}
    nLMin3 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX03
        io: io
    '}
    nLMax3 : UINT;
    {attribute 'pytmc' := '
        pv: L_MIN04
        io: io
    '}
    nLMin4 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX04
        io: io
    '}
    nLMax4 : UINT;
    {attribute 'pytmc' := '
        pv: L_MIN05
        io: io
    '}
    nLMin5 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX05
        io: io
    '}
    nLMax5 : UINT;
    {attribute 'pytmc' := '
        pv: L_MIN06
        io: io
    '}
    nLMin6 : UINT;
    {attribute 'pytmc' := '
        pv: L_MAX06
        io: io
    '}
    nLMax6 : UINT;
END_STRUCT
END_TYPE

ST_TemperatureMonitorSetpoints

// Setpoints for the temperature monitors
TYPE ST_TemperatureMonitorSetpoints :
STRUCT
    {attribute 'pytmc' := '
        pv: MAX_TEMP
        io: io
        field: EGU C
    '}
    fMaxTemperature : LREAL; // Maximum temperature in Celsius allowed before triggering error
END_STRUCT
END_TYPE

GVLs

Global_Version

{attribute 'TcGenerated'}
// This function has been automatically generated from the project information.
VAR_GLOBAL CONSTANT
    {attribute 'const_non_replaced'}
    {attribute 'linkalways'}
    stLibVersion_EPS : ST_LibVersion := (iMajor := 0, iMinor := 0, iBuild := 0, iRevision := 0, sVersion := '0.0.0');
END_VAR

GVL_EL3174

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    (* Analog scaling *)
    fRawUpperLimit : LREAL := 16#7736; // Highest raw signal that can be read
    fRawLowerLimit : LREAL := 16#0000; // Lowest raw signal that can be read
    fScaleUpperLimit : LREAL := 10; // Upper limit in volts
    fScaleLowerLimit : LREAL := 0; // Lower limit in volts
END_VAR

GVL_IO

// PLC IO from terminal modules
{attribute 'qualified_only'}
VAR_GLOBAL
    (* CX2020 *)

    (* Slot1 EL3174 *)
    {attribute 'TcLinkTo' :=    'TIIB[LPS_PLC_E01_EL3174]^AI Standard Channel 1^Value'}
    nOpcpaVoltageRaw AT %I* : INT; // Ch1

    {attribute 'TcLinkTo' :=    'TIIB[LPS_PLC_E01_EL3174]^AI Standard Channel 2^Value'}
    nAmphosVoltageRaw AT %I* : INT; // Ch2



    (* Slot2 EL3174 *)


    (* Slot3 EL9189 *)

    (* Slot4 EL9184 *)

    (*
    (* Slot5 EL3174 *)
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E05_EL3174]^AI Standard Channel 1^Value'}
    nTempMonRaw01 AT %I* : INT; // Ch1
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E05_EL3174]^AI Standard Channel 2^Value'}
    nTempMonRaw02 AT %I* : INT; // Ch2
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E05_EL3174]^AI Standard Channel 3^Value'}
    nTempMonRaw03 AT %I* : INT; // Ch3
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E05_EL3174]^AI Standard Channel 4^Value'}
    nTempMonRaw04 AT %I* : INT; // Ch4

    (* Slot6 EL3174 *)
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E06_EL3174]^AI Standard Channel 1^Value'}
    nTempMonRaw05 AT %I* : INT; // Ch1
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E06_EL3174]^AI Standard Channel 2^Value'}
    nTempMonRaw06 AT %I* : INT; // Ch2
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E06_EL3174]^AI Standard Channel 3^Value'}
    nTempMonRaw07 AT %I* : INT; // Ch3
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E06_EL3174]^AI Standard Channel 4^Value'}
    nTempMonRaw08 AT %I* : INT; // Ch4
    *)

    (* Slot7 EL1259 Inputs *)
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:AMPHOS:POWER_STATE
        io: i
        field: ZNAM "OFF"
        field: ONAM "ON"
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 1^Status^Input state'}
    bAmphosOn AT %I* : BOOL; // Ch1
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 2^Status^Input state'}
    bLeakDetected AT %I* : BOOL; // Ch2
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 3^Status^Input state'}
    bLoopTempSW01 AT %I* : BOOL; // Ch3 Loop 01 NC Temp switch
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 4^Status^Input state'}
    bLoopTempSW02 AT %I* : BOOL; // Ch4 Loop 02 NC Temp switch
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 5^Status^Input state'}
    bLoopTempSW03 AT %I* : BOOL; // Ch5 Loop 03 NC Temp switch
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 6^Status^Input state'}
    bLoopTempSW04 AT %I* : BOOL; // Ch6 Loop 04 NC Temp switch

    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 7^Status^Input state'}
    bBaseplateChillerFlow AT %I* : BOOL; // Ch7 Baseplate chiller NC flow sensor


    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTI Inputs 10x Channel 8^Status^Input state'}
    bDumpChillerFlow AT %I* : BOOL; // Ch2 Dump chiller NC flow sensor


    (* Slot7 EL1259 Outputs *)
    (*
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:AMPHOS_ATTENUATOR:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E06]^Channel 1^Output'}
    bAmphosAttenuatorEnable AT %Q* : BOOL; // Ch1


    bAmphosAttenuatorEnableManualOperation AT %Q* : BOOL := TRUE;
    *)

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:AMPHOS_SHUTTER:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E07_EL1259]^MTO Outputs 10x Channel 1^Ctrl^Manual output state'}
    bAmphosShutterEnable AT %Q* : BOOL; // Ch1


    bAmphosShutterEnableManualOperation AT %Q* : BOOL := TRUE;





    (* Slot8 EL9184 *)

    (* Slot9 EL1259 *)

    (* Slot10 EL9184 *)

    (* Slot11 ES2624 *)
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:DUMP_CHILLER_RELAY:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E11_EL2624]^Channel 1^Output'}
    bDumpChillerRelay AT %Q* : BOOL; // Ch1 Dump chiller NC relay

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:BASEPLATE_CHILLER_RELAY:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E11_EL2624]^Channel 2^Output'}
    bBaseplateChillerRelay AT %Q* : BOOL; // Ch2 Baseplate chiller NC relay

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:AMPHOS_RELAY:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E11_EL2624]^Channel 3^Output'}
    bAmphosRelay AT %Q* : BOOL; // Ch3 Amphos Interlock relay

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:CARBIDE_SHUTTER:ENABLE
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM DISABLED
        field: ONAM ENABLED
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E11_EL2624]^Channel 4^Output'}
    bCarbideShutterEnable AT %Q* : BOOL; // Ch4 HASS


    bCarbideShutterEnableManualOperation AT %Q* : BOOL := TRUE;

    (* Slot12 EL6022 *)

    (* Slot13 EL9505 *)

    (* Slot14 EL1124 *)
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:OPCPA:POWER_STATE
        io: i
        field: ZNAM "OFF"
        field: ONAM "ON"
    '}
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E14_EL1124]^Channel 1^Input'}
    bCarbideOn AT %I* : BOOL; // Ch1


    (*
    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E14_EL1124]^Channel 2^Input'}
    bAmphosMRC01 AT %I* : BOOL; // Ch2 NC Amphos MRC switch

    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E14_EL1124]^Channel 3^Input'}
    bAmphosMRC02 AT %I* : BOOL; // Ch3 NC Amphos MRC switch

    {attribute 'TcLinkTo' := 'TIIB[LPS_PLC_E14_EL1124]^Channel 4^Input'}
    bAmphosMRC03 AT %I* : BOOL; // Ch4 NC Amphos MRC switch
    *)

END_VAR

GVL_Laser

// Setpoints for lasers
{attribute 'qualified_only'}
VAR_GLOBAL PERSISTENT
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:AMPHOS'}
    stAmphosSP : ST_LaserSetpoints; // Amphos setpoints
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:OPCPA'}
    stOpcpaSP : ST_LaserSetpoints; // OPCPA setpoints
END_VAR
Related:

GVL_PLC

// PLC global variables
{attribute 'qualified_only'}
VAR_GLOBAL
    AMSNetId AT %I* : AMSNETID;
END_VAR

GVL_TemperatureMonitor

// Temperature monitor setpoints
{attribute 'qualified_only'}
VAR_GLOBAL PERSISTENT
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON01'}
    stTempMonSP01 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON02'}
    stTempMonSP02 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON03'}
    stTempMonSP03 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON04'}
    stTempMonSP04 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON05'}
    stTempMonSP05 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON06'}
    stTempMonSP06 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON07'}
    stTempMonSP07 : ST_TemperatureMonitorSetpoints;
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON08'}
    stTempMonSP08 : ST_TemperatureMonitorSetpoints;
END_VAR
Related:

GVL_TraceTek

// Global variables for the TraceTek leak detector
{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    (* Modbus address *)
    nModbusSlaveAddress : BYTE := 199; // Modbus slave address of the TraceTek device
END_VAR
VAR_GLOBAL PERSISTENT
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:LEAK'}
    stLeakMonitorBoundaries : ST_LeakBoundaries; // Boundaries of possible leak locations
END_VAR
Related:

POUs

F_AmphosAttenuatorEnable

// Interlock logic for the Amphos attenuator
FUNCTION F_AmphosAttenuatorEnable : BOOL
VAR_INPUT
    bAmphosOn : BOOL; // Whether the Amphos is turned on
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Currently detected errors
END_VAR
VAR
END_VAR
IF stErrors.bHardwareFailure // Close attenuator if there is a hardware failure
    OR NOT bAmphosOn // Close attenuator if Amphos is not on
    OR stErrors.bLeakOpticalTableAmphosOn OR stErrors.bLeakOpcpaCarbideOn (* OR stErrors.bLeakUnderTable *) // Close attenuator if there is a leak on the optical table with amphos on, there is a leak in the OPCPA with the carbide on, or there is a leak under the table
    (* OR stErrors.bAmphosMRC01 // Close attenuator if there is an AmphosMRC01 error *)
THEN
    F_AmphosAttenuatorEnable := FALSE;
ELSE
    F_AmphosAttenuatorEnable := TRUE;
END_IF

END_FUNCTION
Related:

F_AmphosEnable

// Interlock logic for the Amphos relay
FUNCTION F_AmphosEnable : BOOL
VAR_INPUT
    fAmphosVoltage : LREAL; // The Amphos voltage
    bEnabled : BOOL; // Whether the amphos enable signal is currently on
    bMasterOverride : BOOL; //Master manual override control input
END_VAR
VAR_IN_OUT CONSTANT
    stAmphosSetpoints : ST_LaserSetpoints; // The Amphos setpoints
    stErrors : ST_ErrorStates; // Currently detected errors
END_VAR
IF (stErrors.bHardwareFailure // Disable Amphos if there is a hardware failure
    OR stErrors.bAmphosBeam  // Disable Amphos if there is a beam error
    OR stErrors.bAmphosBeam AND NOT bEnabled) // Once amphos is disabled, keep it disabled until beam error is reset
    AND NOT bMasterOverride //if master override is TRUE, ignore all errors
THEN
    F_AmphosEnable := FALSE;
ELSE
    F_AmphosEnable := TRUE;
END_IF

END_FUNCTION
Related:

F_AmphosShutterEnable

// Interlock logic for the Amphos shutter
FUNCTION F_AmphosShutterEnable : BOOL
VAR_INPUT
    bAmphosOn : BOOL; // Whether the amphos is on
    fOpcpaVoltage : LREAL; // The voltage of the OPCPA
    bLoopTempOverride01 : BOOL; // Whether the chiller loop error is overriden
    bLoopTempOverride02 : BOOL; // Whether the chiller loop error is overriden
    bLoopTempOverride03 : BOOL; // Whether the chiller loop error is overriden
    bLoopTempOverride04 : BOOL; // Whether the chiller loop error is overriden
    bMasterOverride : BOOL; //Master manual override control input
    bAmpShutterClosed : BOOL; //Master Amphos shutter control. TRUE = Closed
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Currently detected errors
    stOpcpaSetpoints : ST_LaserSetpoints; // OPCPA setpoints
END_VAR
VAR
END_VAR
IF (stErrors.bHardwareFailure // Close shutter if there is a hardware failure
    OR stErrors.bLeakOpticalTableAmphosOn OR stErrors.bLeakOpcpaCarbideOn (* OR stErrors.bLeakUnderTable *) // Close shutter if there is a leak on the optical table with the amphos on, a leak under the table, or a leak inside the OPCPA with Carbide on
    OR bAmphosOn AND (
        (NOT bLoopTempOverride01 AND stErrors.bChillerLoop01) OR
        (NOT bLoopTempOverride02 AND stErrors.bChillerLoop02) OR
        (NOT bLoopTempOverride03 AND stErrors.bChillerLoop03) OR
        (NOT bLoopTempOverride04 AND stErrors.bChillerLoop04)) // Close shutter if the amphos is on and the temperature is too high in any loop. Ignore if operator override is enabled
    OR (stErrors.bOpcpaBeam) // Close shutter if in OPCPA beam error state and OPCPA voltage too low
    (*OR stErrors.bTempMon01 OR stErrors.bTempMon02 OR stErrors.bTempMon03 OR stErrors.bTempMon04 OR stErrors.bTempMon05 OR
        stErrors.bTempMon06 OR stErrors.bTempMon07 OR stErrors.bTempMon08 // Close shutter if temperature too high at any monitor location *)
    OR stErrors.bDumpChiller) // Close shutter if there is a Dump Chiller error
    (* OR stErrors.bAmphosMRC02 OR stErrors.bAmphosMRC03 // Close shutter if there are errors on the additional MRCs *)
    AND NOT bMasterOverride //if master override is TRUE, ignore all errors
THEN
    F_AmphosShutterEnable := FALSE;
ELSIF bMasterOverride AND bAmpShutterClosed THEN
    F_AmphosShutterEnable := FALSE;
ELSIF bMasterOverride AND NOT bAmpShutterClosed THEN
    F_AmphosShutterEnable := TRUE;
ELSE
    F_AmphosShutterEnable := TRUE;
END_IF

END_FUNCTION
Related:

F_BaseplateChillerEnable

// Interlock logic for the Baseplate Chiller
FUNCTION F_BaseplateChillerEnable : BOOL
VAR_INPUT
    bMasterOverride : BOOL; //Master manual override control input
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Currently detected errors
END_VAR
VAR
END_VAR
IF (stErrors.bHardwareFailure // Disable baseplate chiller if there is a hardware failure
    (* OR stErrors.bLeakUnderTable *) OR stErrors.bLeakOpcpaCarbideOn) // Disable baseplate chiller if there is a leak under the table
    AND NOT bMasterOverride //if master override is TRUE, ignore all errors
THEN
    F_BaseplateChillerEnable := FALSE;
ELSE
    F_BaseplateChillerEnable := TRUE;
END_IF

END_FUNCTION
Related:

F_CarbideShutterEnable

// Interlock logic for the Carbide Shutter
FUNCTION F_CarbideShutterEnable : BOOL
VAR_INPUT
    bMasterOverride : BOOL; //Master manual override control input
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Currently detected errors
END_VAR
VAR
END_VAR
IF (stErrors.bHardwareFailure // Close carbide shutter if there is a hardware failure
    OR stErrors.bLeakOpcpaCarbideOn (* OR stErrors.bLeakUnderTable *) ) // Close carbide shutter if leak in OPCPA with carbide on or if there is a leak under the table
    AND NOT bMasterOverride //if master override is TRUE, ignore all errors
THEN
    F_CarbideShutterEnable := FALSE;
ELSE
    F_CarbideShutterEnable := TRUE;
END_IF

END_FUNCTION
Related:

F_DumpChillerEnable

// Interlock logic for the Dump Chiller
FUNCTION F_DumpChillerEnable : BOOL
VAR_INPUT
    bMasterOverride : BOOL; //Master manual override control input
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Currently detected errors
END_VAR
VAR
END_VAR
IF (stErrors.bHardwareFailure // Disable dump chiller if there's a hardware failure
    OR stErrors.bLeakOpticalTableAmphosOn OR stErrors.bLeakOpticalTableNoLaser (* OR stErrors.bLeakUnderTable *) ) // Disable dump chiller for these leak conditions
    AND NOT bMasterOverride //if master override is TRUE, ignore all errors
THEN
    F_DumpChillerEnable := FALSE;
ELSE
    F_DumpChillerEnable := TRUE;
END_IF

END_FUNCTION
Related:

FB_AmphosMRC

// Monitors the Amphos MRC input signal for errors
FUNCTION_BLOCK FB_AmphosMRC
VAR_INPUT
    bAmphosOn : BOOL; // Whether the Amphos is turned on
    bMRC : BOOL; // Error signal. Signal should normally be high
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL; // Whether an error was detected. Error should go back to normal on its own
END_VAR
VAR
END_VAR
(* Set error flag if amphos is on and the MRC has an error *)
bError := bAmphosOn AND NOT bMRC;

END_FUNCTION_BLOCK

FB_BaseplateChiller

// Monitors the baseplate chiller from the flow sensor
FUNCTION_BLOCK FB_BaseplateChiller
VAR_INPUT
    bAmphosOn : BOOL; // Whether the amphos is turned on
    bBaseplateChillerFlow : BOOL; // Signal from the flow sensor. Normally high.
    bMasterLatchReset : BOOL; //Master latched error reset
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL; // Whether an error was detected. Should latch until reset
END_VAR
VAR
END_VAR
(* Latch error if the baseplate chiller flow detects an error *)
IF bAmphosOn AND NOT bBaseplateChillerFlow THEN
    bError := TRUE;
END_IF

IF bMasterLatchReset THEN
    bError := FALSE;
END_IF

END_FUNCTION_BLOCK

// Reset latched errors
METHOD Reset
VAR_INPUT
END_VAR
bError := FALSE;
END_METHOD

FB_ChillerLoop

// Monitors the chiller loop from the flow sensor
FUNCTION_BLOCK FB_ChillerLoop
VAR_INPUT
    bLoopTempSW : BOOL; // NC Temperature switch. Indicates when temperature is too high
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL := FALSE; // Whether an error was detected. Goes back to normal on its own
    {attribute 'pytmc' := '
        pv: OVERRIDE
        io: io
        field: ZNAM "OFF"
        field: ONAM "ON"
    '}
    bOperatorOverride : BOOL := FALSE; // If TRUE, allows the EPS to keep running despite the chiller loop error
END_VAR
(* Set error flag if loop temperature monitor detects an error *)
bError := NOT bLoopTempSW;

(* Reset the operator override if the system returns to normal tempreature range *)
IF bLoopTempSW THEN
    bOperatorOverride := FALSE;
END_IF

END_FUNCTION_BLOCK

FB_DumpChiller

// Monitors the dump chiller from the flow sensor
FUNCTION_BLOCK FB_DumpChiller
VAR_INPUT
    bAmphosOn : BOOL; // Whether the amphos is turned on
    bDumpChillerFlow : BOOL; // Signal from the flow sensor. Normally high.
    bMasterLatchReset : BOOL; //Master latched error reset
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL; // Whether an error was detected. Latches until reset
END_VAR
VAR
END_VAR
(* Latch error if the dump chiller flow detects an error and the amphos is on *)
IF bAmphosOn AND NOT bDumpChillerFlow THEN
    bError := TRUE;
END_IF

IF bMasterLatchReset THEN
    bError := FALSE;
END_IF

END_FUNCTION_BLOCK

// Reset latched errors
METHOD Reset
VAR_INPUT
END_VAR
bError := FALSE;
END_METHOD

FB_EcatDiag

(*
Ecat bus diagnostic tool
2015-11-4 Alex Wallace
This function block checks the states of all slaves on the ecat bus network,
it could be modified to export the states of the slaves on an individual basis,
but for now it sets the output boolean true if all slaves are OP and false otherwise.
To start the block provide a falling edge on the first pass boolean input.

2018-05-05 Margaret Ghaly
Function block has been modified to retrieve the Device State of the Ethercat Master.
It also exports the states and information of each individual configured Slave.
And saves them in the array q_aEcConfSlaveInfo.

2019-08-28 Nathan Telles
Customized for EPS project using PyTMC. Export all states to a structure which is
linked to EPICS records. Remove unecessary diagnostics.
*)
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_EcatDiag
VAR_INPUT
    {attribute 'naming' := 'omit'}
    AMSNetId : AMSNETID; //Link to the AMSNETID name in the ethercat master info.
    //i_xFirstPass: BOOL; //Hook to system first pass boolean for proper intialization (must be true for the first cycle of the PLC)
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL;
    {attribute 'pytmc' := '
        pv: SLAVES_OK
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM ERROR
        field: ONAM OK
    '}
    bAllSlaveStatesGood: BOOL; // Set to True if all Slaves are in OP State
    {attribute 'pytmc' := '
        pv: MASTER_OK
        io: i
        field: OSV NO_ALARM
        field: ZSV MAJOR
        field: ZNAM ERROR
        field: ONAM OK
    '}
    bMasterStateGood : BOOL;
END_VAR
VAR
    {attribute 'pytmc' := '
        pv: MASTER_DEV_STATE
        io: i
    '}
    nMasterDevState: WORD; // The Device State of the Master
    {attribute 'pytmc' := '
        pv: MASTER_STATE
        io: i
    '}
    nMasterState: WORD; // The State of the Master
    {attribute 'pytmc' := '
        pv: AMS_ID
        io: i
    '}
    sNetId: STRING(23); //NetId string

    astTermStates: ARRAY[1..MAX_SLAVES] OF ST_EcSlaveState; //ECAT Slave States Buffer
    astEcConfSlaveInfo: ARRAY[1..MAX_SLAVES] OF ST_EcSlaveConfigData; //ECAT Slave Configs Buffer

    fbGetAllSlaveStates: FB_EcGetAllSlaveStates; //Acquires the ECAT Slave States puts them into astTermStates
    fbGetMasterDevState: FB_EcGetMasterDevState; //Acquires ECAT Master State
    fbGetMasterState: FB_EcGetMasterState; //Acquires ECAT Master State
    fbGetConfSlaves: FB_EcGetConfSlaves; //Acquires the ECAT slave configuration of the bus (how many, what kind, etc)

    {attribute 'naming' := 'omit'}
    ftReset: F_TRIG; //Reset trigger sensor
    {attribute 'naming' := 'omit'}
    ftMasterDevReset: F_TRIG; //Retrigger sensor for GetMasterDevState
    {attribute 'naming' := 'omit'}
    ftMasterReset: F_TRIG; //Retrigger sensor for GetMasterState

    {attribute 'pytmc' := 'pv: SLAVE01'}
    stSlave01 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE02'}
    stSlave02 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE03'}
    stSlave03 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE04'}
    stSlave04 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE05'}
    stSlave05 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE06'}
    stSlave06 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE07'}
    stSlave07 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE08'}
    stSlave08 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE09'}
    stSlave09 : ST_DeviceDiagnostic;
    {attribute 'pytmc' := 'pv: SLAVE10'}
    stSlave10 : ST_DeviceDiagnostic;

    aSlaves : ARRAY [1..MAX_SLAVES] OF POINTER TO ST_DeviceDiagnostic := [ADR(stSlave01),ADR(stSlave02),
        ADR(stSlave03),ADR(stSlave04),ADR(stSlave05),ADR(stSlave06),ADR(stSlave07),ADR(stSlave08),
        ADR(stSlave09), ADR(stSlave10)];

    nIterator: UINT;
    bInit : BOOL := TRUE;

    {attribute 'pytmc' := '
        pv: SLAVES_COUNT
        io: i
    '}
    nSlaves : UINT;
END_VAR
VAR CONSTANT
    MAX_SLAVES : UINT := 10;
END_VAR
sNetId := F_CreateAmsNetId(AMSNetId);

//Query the state of all terminals, collect in astTermStates
ftReset(CLK:=fbGetAllSlaveStates.bBusy OR bInit);
fbGetAllSlaveStates.bExecute := ftReset.Q;
fbGetAllSlaveStates(sNetId:=sNetId, pStateBuf := ADR(astTermStates), cbBufLen:=SIZEOF(astTermStates));
//Keep checking...


//Cycle through each entry in the array and check if we have anyone not in OP and that the link state is good.
// If so, then set our global IO bad boolean.
IF fbGetAllSlaveStates.nSlaves > 0 THEN
    bAllSlaveStatesGood := TRUE;
    FOR nIterator := 1 TO MAX_SLAVES DO
        IF nIterator > fbGetAllSlaveStates.nSlaves // Slave is missing
        OR NOT( (astTermStates[nIterator].deviceState = EC_DEVICE_STATE_OP) AND (astTermStates[nIterator].linkState = EC_LINK_STATE_OK))
        THEN
            bAllSlaveStatesGood := FALSE;
        END_IF

        IF nIterator <= fbGetAllSlaveStates.nSlaves THEN
            aSlaves[nIterator]^.nDeviceState := astTermStates[nIterator].deviceState AND EC_DEVICE_STATE_MASK;
            aSlaves[nIterator]^.nDeviceStateFlags := SHR(astTermStates[nIterator].deviceState, 4);
            aSlaves[nIterator]^.nLinkStateFlags :=astTermStates[nIterator].linkState;
        ELSE
            // Missing slaves should be set to invalid
            aSlaves[nIterator]^.nDeviceState := E_DeviceState.INVALID;
        END_IF
    END_FOR
END_IF

// Read the EtherCAT state of the master. If the call is successful,
//the State output variable of type WORD contains the requested status information.
ftMasterDevReset(CLK:=fbGetMasterDevState.bBusy OR bInit);
fbGetMasterDevState(sNetId:= sNetId, bExecute:=ftMasterDevReset.Q, nDevState => nMasterDevState);

ftMasterReset(CLK:=fbGetMasterState.bBusy OR bInit);
fbGetMasterState(sNetId:= sNetId, bExecute:=ftMasterDevReset.Q, state => nMasterState);

bMasterStateGood:= (fbGetMasterState.state = BYTE_TO_UINT(EC_DEVICE_STATE_OP) AND fbGetMasterDevState.nDevState = 0);

//This function is used to read a list of all configured slaves from the EtherCat master object Directory
//needs to run only once
fbGetConfSlaves(bExecute := bInit, sNetId :=sNetId, pArrEcConfSlaveInfo := ADR(astEcConfSlaveInfo),cbBufLen := SIZEOF(astEcConfSlaveInfo));
nSlaves:=fbGetConfSlaves.nSlaves;

IF  NOT (fbGetConfSlaves.bBusy) THEN
    FOR nIterator := 1 TO MIN(MAX_SLAVES, fbGetConfSlaves.nSlaves) DO
        IF nIterator > fbGetConfSlaves.nSlaves THEN // Missing terminal
            aSlaves[nIterator]^.nAddr := 0;
            aSlaves[nIterator]^.sName := '';
            aSlaves[nIterator]^.sType := '';
        ELSE
            aSlaves[nIterator]^.nAddr :=astEcConfSlaveInfo[nIterator].nAddr;
            aSlaves[nIterator]^.sName :=astEcConfSlaveInfo[nIterator].sName;
            aSlaves[nIterator]^.sType :=astEcConfSlaveInfo[nIterator].sType;
        END_IF
    END_FOR
    fbGetConfSlaves.bExecute := FALSE;
END_IF


bInit := FALSE;
bError := fbGetMasterDevState.bError OR fbGetMasterState.bError OR fbGetAllSlaveStates.bError OR
    fbGetConfSlaves.bError OR NOT bAllSlaveStatesGood OR NOT bMasterStateGood;

END_FUNCTION_BLOCK
Related:

FB_ErrorTriggers

// Detects rising-edge errors
FUNCTION_BLOCK FB_ErrorTriggers
VAR_INPUT
    nLeakLocations : BYTE;
END_VAR
VAR_IN_OUT CONSTANT
    stErrors : ST_ErrorStates; // Errors that have been detected
END_VAR
VAR_OUTPUT
    stNewErrors : St_ErrorStates; // Rising-edge errors
END_VAR
VAR
    // R_TRIG function blocks for detecting new errors
    fbLeakTrig : R_TRIG;
    fbLeakOpticalTableAmphosOnTrig : R_TRIG;
    fbLeakOpticalTableNoLaserTrig : R_TRIG;
    fbLeakOpcpaCarbideOnTrig : R_TRIG;
    fbLeakUnderTableTrig : R_TRIG;
    fbLeakInsideRacksTrig : R_TRIG;
    fbAmphosBeamTrig : R_TRIG;
    fbOpcpaBeamTrig : R_TRIG;
    fbDumpChillerTrig : R_TRIG;
    fbBaseplateChillerTrig : R_TRIG;
END_VAR
fbLeakTrig(
    CLK := nLeakLocations <> E_LeakLocation.NO_LEAK,
);
fbLeakOpticalTableAmphosOnTrig(
    CLK := stErrors.bLeakOpticalTableAmphosOn,
    Q => stNewErrors.bLeakOpticalTableAmphosOn
);
fbLeakOpticalTableNoLaserTrig(
    CLK := stErrors.bLeakOpticalTableNoLaser,
    Q => stNewErrors.bLeakOpticalTableNoLaser
);
fbLeakOpcpaCarbideOnTrig(
    CLK := stErrors.bLeakOpcpaCarbideOn,
    Q => stNewErrors.bLeakOpcpaCarbideOn
);
fbLeakUnderTableTrig(
    CLK := stErrors.bLeakUnderTable,
    Q => stNewErrors.bLeakUnderTable
);
fbLeakInsideRacksTrig(
    CLK := stErrors.bLeakInsideRacks,
    Q => stNewErrors.bLeakInsideRacks
);

fbAmphosBeamTrig(
    CLK := stErrors.bAmphosBeam,
    Q => stNewErrors.bAmphosBeam
);
fbOpcpaBeamTrig(
    CLK := stErrors.bOpcpaBeam,
    Q => stNewErrors.bOpcpaBeam
);

fbDumpChillerTrig(
    CLK := stErrors.bDumpChiller,
    Q => stNewErrors.bDumpChiller
);
fbBaseplateChillerTrig(
    CLK := stErrors.bBaseplateChiller,
    Q => stNewErrors.bBaseplateChiller
);

END_FUNCTION_BLOCK

// Returns whether a rising-edge leak error has occured
METHOD NewLeakError : BOOL
VAR_INPUT
END_VAR
NewLeakError := fbLeakTrig.Q OR fbLeakOpticalTableAmphosOnTrig.Q OR fbLeakOpticalTableNoLaserTrig.Q OR fbLeakOpcpaCarbideOnTrig.Q OR fbLeakUnderTableTrig.Q OR fbLeakInsideRacksTrig.Q;
END_METHOD

// Reset latched amphos errors
METHOD ResetAmphosTrigger
VAR_INPUT
END_VAR
fbAmphosBeamTrig(
    CLK := FALSE,
    Q => stNewErrors.bAmphosBeam
);
END_METHOD

METHOD ResetBaseplateChillerTrigger
VAR_INPUT
END_VAR
fbBaseplateChillerTrig(
    CLK := FALSE,
    Q => stNewErrors.bBaseplateChiller
);
END_METHOD

METHOD ResetDumpChillerTrigger
VAR_INPUT
END_VAR
fbDumpChillerTrig(
    CLK := FALSE,
    Q => stNewErrors.bDumpChiller
);
END_METHOD

// Reset latched leak errors
METHOD ResetLeakTriggers
VAR_INPUT
END_VAR
fbLeakOpticalTableAmphosOnTrig(
    CLK := FALSE,
    Q => stNewErrors.bLeakOpticalTableAmphosOn
);
fbLeakOpticalTableNoLaserTrig(
    CLK := FALSE,
    Q => stNewErrors.bLeakOpticalTableNoLaser
);
fbLeakOpcpaCarbideOnTrig(
    CLK := FALSE,
    Q => stNewErrors.bLeakOpcpaCarbideOn
);
fbLeakUnderTableTrig(
    CLK := FALSE,
    Q => stNewErrors.bLeakUnderTable
);
fbLeakInsideRacksTrig(
    CLK := FALSE,
    Q => stNewErrors.bLeakInsideRacks
);
END_METHOD

// Reset latched OPCPA errors
METHOD ResetOpcpaTrigger
VAR_INPUT
END_VAR
fbOpcpaBeamTrig(
    CLK := FALSE,
    Q => stNewErrors.bOpcpaBeam
);
END_METHOD
Related:

FB_Laser

// Monitors the lasers for under voltage events
FUNCTION_BLOCK FB_Laser
VAR_INPUT
    nVoltageRaw : INT; // The raw signal collected from the ADC
    bAmphosOn : BOOL; // Whether the amphos is turned on
    bMasterLatchReset : BOOL; //Master latched error reset
END_VAR
VAR_IN_OUT CONSTANT
    stSetpoints : ST_LaserSetpoints; // Setpoints for the laser
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: VOLTAGE
        io: i
        field: HOPR 10.0
        field: LOPR 0.0
        field: LSV MINOR
        field: LLSV MAJOR
        field: EGU Volts
    '}
    fVoltage : LREAL; // Voltage calculated from the raw ADC input

    {attribute 'pytmc' := '
        pv: SCALED_PWR
        io: i
        field: EGU Watts
    '}
    fPower : LREAL; // Value to be scaled from fVoltage to give meter reading in terms of watts

    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL; // Whether an error was detected. Latches until reset

    bBelowNominal : BOOL; //fVoltage is below the nominal setpoint
END_VAR
(* Scale the analog signal to voltage *)
fVoltage := F_Scale(
    fRawData := nVoltageRaw,
    fRawDataLowerOffLimit := GVL_EL3174.fRawLowerLimit,
    fRawDataUpperOffLimit := GVL_EL3174.fRawUpperLimit,
    fScaleDataLowerOffLimit := GVL_EL3174.fScaleLowerLimit,
    fScaleDataUpperOffLimit := GVL_EL3174.fScaleUpperLimit);

fPower := fVoltage; //Value is scaled by user set ASLO and AOFF fields (for display only)

IF bAmphosOn AND (fVoltage < stSetpoints.nMinNominalVoltage) THEN
    bBelowNominal := TRUE;
END_IF

(* Set error if amphos is below nominal voltage *)
IF  bAmphosOn AND (fVoltage < stSetpoints.nMinVoltage) THEN
    bError := TRUE;
END_IF

IF bMasterLatchReset THEN
    bBelowNominal := FALSE;
    bError := FALSE;
END_IF

END_FUNCTION_BLOCK

// Reset latched errors
METHOD Reset
VAR_INPUT
END_VAR
bError := FALSE;
END_METHOD
Related:

FB_LeakMonitor

// Detect leaks and leak locations
FUNCTION_BLOCK FB_LeakMonitor
VAR_INPUT
    bLeakDetected : BOOL; // Whether a leak was detected
    bAmphosOn : BOOL; // Whether the Amphos is on
    bCarbideOn : BOOL; // Whether the Carbide is on
    nLocationResistance: UINT; // Used for calculating the location of the leak
    bMasterLatchReset : BOOL; //Master latched error reset
END_VAR
VAR_IN_OUT CONSTANT
    stBound : ST_LeakBoundaries; // Setpoints for calculating leak locations from location resistance
END_VAR
VAR_OUTPUT

    {attribute 'pytmc' := '
        pv: LOW_RES
        io: i
    '}
    bLowResistance : BOOL := FALSE;

    {attribute 'pytmc' := '
        pv: LEAKS
        io: i
    '}
    nLeakLocations : BYTE := E_LeakLocation.NO_LEAK; // Locations of leaks
    {attribute 'pytmc' := '
        pv: LOTAO_ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bLeakOpticalTableAmphosOn : BOOL := FALSE; // Leak on the optical table with Amphos on
    {attribute 'pytmc' := '
        pv: LOTNL_ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bLeakOpticalTableNoLaser : BOOL := FALSE; // Leak on the optical table with no laser on
    {attribute 'pytmc' := '
        pv: LOCO_ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bLeakOpcpaCarbideOn : BOOL := FALSE; // Leak inside the OPCPA with Carbide on
    {attribute 'pytmc' := '
        pv: LUT_ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bLeakUnderTable : BOOL := FALSE; // Leak under the table
    {attribute 'pytmc' := '
        pv: LIR_ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bLeakInsideRacks : BOOL := FALSE; // Leak inside the racks
END_VAR
IF (nLocationResistance < 200) THEN
    bLowResistance := TRUE;
ELSE
    bLowResistance := FALSE;
END_IF

IF (bLeakDetected OR bLowResistance) THEN
    (* Find leak location for PV. Stays until reset *)
    IF stBound.nLMin1 <= nLocationResistance AND nLocationResistance < stBound.nLMax1 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC1;
    END_IF

    IF stBound.nLMin2 <= nLocationResistance AND nLocationResistance < stBound.nLMax2 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC2;
    END_IF

    IF stBound.nLMin3 <= nLocationResistance AND nLocationResistance < stBound.nLMax3 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC3;
    END_IF

    IF stBound.nLMin4 <= nLocationResistance AND nLocationResistance < stBound.nLMax4 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC4;
    END_IF

    IF stBound.nLMin5 <= nLocationResistance AND nLocationResistance < stBound.nLMax5 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC5;
    END_IF

    IF stBound.nLMin6 <= nLocationResistance AND nLocationResistance < stBound.nLMax6 THEN
        nLeakLocations := nLeakLocations OR E_LeakLocation.LOC6;
    END_IF

    //CURRENT SETUP : LOC1 = OPCPA, LOC2 = rest of optical table (values decrease toward higher LOC#)
    (* Latch Optical table leak errors *)
    IF (stBound.nLMin2 <= nLocationResistance AND nLocationResistance < stBound.nLMax1) THEN
        IF bAmphosOn THEN
            bLeakOpticalTableAmphosOn := TRUE;
        ELSIF NOT bCarbideOn THEN
            bLeakOpticalTableNoLaser := TRUE;
        END_IF
    END_IF

    (* Latch carbide leak errors *)
    IF (bCarbideOn AND stBound.nLMin1 <= nLocationResistance AND nLocationResistance < stBound.nLMax1) THEN
        bLeakOpcpaCarbideOn := TRUE;
    END_IF

    (* Latch leak under table errors *)
    IF (stBound.nLMin5 <= nLocationResistance AND nLocationResistance < stBound.nLMax5) THEN
        bLeakUnderTable := TRUE;
    END_IF

    (* Latch leak inside rack errors *)
    IF (stBound.nLMin6 <= nLocationResistance AND nLocationResistance < stBound.nLMax6) THEN
        bLeakInsideRacks := TRUE;
    END_IF

END_IF

IF bMasterLatchReset THEN
    nLeakLocations := E_LeakLocation.NO_LEAK;
    bLeakOpticalTableAmphosOn := FALSE;
    bLeakOpcpaCarbideOn := FALSE;
    bLeakOpticalTableNoLaser := FALSE;
    bLeakUnderTable := FALSE;
    bLeakInsideRacks := FALSE;
END_IF

END_FUNCTION_BLOCK

// Reset latched errors
METHOD Reset
VAR_INPUT
END_VAR
nLeakLocations := E_LeakLocation.NO_LEAK;
bLeakOpticalTableAmphosOn := FALSE;
bLeakOpticalTableNoLaser := FALSE;
bLeakOpcpaCarbideOn := FALSE;
bLeakUnderTable := FALSE;
bLeakInsideRacks := FALSE;
END_METHOD
Related:

FB_TemperatureMonitor

// Monitors temperature sensors
FUNCTION_BLOCK FB_TemperatureMonitor
VAR_INPUT
    nTempMonRaw : INT; // Raw signal from the ADC
    bAmphosOn : BOOL; // Whether the Amphos is on
END_VAR
VAR_IN_OUT
    stSetpoints : ST_TemperatureMonitorSetpoints; // Temperature sensor setpoints
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: TEMP
        io: i
        field: EGU C
        field: LOPR 0.0
        field: HOPR 1000
        field: LSV MINOR
    '}
    fTemperature : LREAL; // Temperature reading in celsius
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL; // Whether a temperature event happened. Goes back to normal by itself
END_VAR
VAR_TEMP
    fMonitorVoltage : LREAL; // Voltage calculated from the raw ADC input
END_VAR
(* Scale the analog signal to voltage *)
fMonitorVoltage := F_Scale(
    fRawData := nTempMonRaw,
    fRawDataLowerOffLimit := GVL_EL3174.fRawLowerLimit,
    fRawDataUpperOffLimit := GVL_EL3174.fRawUpperLimit,
    fScaleDataLowerOffLimit := GVL_EL3174.fScaleLowerLimit,
    fScaleDataUpperOffLimit := GVL_EL3174.fScaleUpperLimit);

fTemperature := fMonitorVoltage * 100;

(* Set error flag if temperature above max allowed and the amphos is on *)
bError := fTemperature > stSetpoints.fMaxTemperature AND bAmphosOn;

END_FUNCTION_BLOCK
Related:

FB_TraceTekModbus

// Communicates with the TraceTek TTSIM-1A hardware over Modbus to get more information about a leak
FUNCTION_BLOCK FB_TraceTekModbus
VAR_INPUT
    nModbusSlaveAddress : BYTE; // Modbus address of slave to be read from
    bLeakDetected : BOOL; // Whether a leak has been detected
    bResetError : BOOL; // Reset signal for error
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: LOC_RES
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    nLocationResistance: UINT; // Resistance measured in the detection wire
    {attribute 'pytmc' := '
        pv: ERROR
        io: i
        field: OSV MAJOR
        field: ZSV NO_ALARM
        field: ZNAM OK
        field: ONAM ERROR
    '}
    bError : BOOL := FALSE; // Flag for Modbus communication errors
    nErrorId : MODBUS_ERRORS := MODBUS_ERRORS.MODBUSERROR_NO_ERROR; // Description of Modbus error
END_VAR
VAR
    aModbusBuffer : WORD := 16#FFFF;  // "Infinite" until real value is read from device
    fbModbusMaster : ModbusRtuMaster_KL6x22B := (
        Quantity := 1,
        MBAddr := 1,
        cbLength := SIZEOF(aModbusBuffer),
        pMemoryAddr := ADR(aModbusBuffer),
        Timeout := T#5S
    );
    fbPoll : TON := (PT := T#0S); // Poll rate for reading modbus. Start at 0 so that modbus is read on first call
END_VAR
VAR_STAT
    tPollRate : TIME := T#500MS;
END_VAR
// Advance poll rate timer
fbPoll(IN := TRUE);

// Update modbus status
fbModbusMaster.Execute := FALSE;
fbModbusMaster.ReadInputRegs();

IF bResetError THEN // Reset modbus error
    bError := FALSE;
    nErrorId := MODBUS_ERRORS.MODBUSERROR_NO_ERROR;
ELSIF fbModbusMaster.Error OR NOT fbModbusMaster.BUSY THEN
    // Allow fb error state to go back to normal by checking modbus error state after reading has finished
    bError := fbModbusMaster.Error;
    nErrorId := fbModbusMaster.ErrorId;
END_IF

(* If the modbus interface isn't busy and there is a leak, try reading
the leak location from the TraceTek. Even if there is no leak, maintain
the modbus connection by checking every few seconds using the fbPoll rate *)
IF NOT fbModbusMaster.BUSY AND (bLeakDetected OR fbPoll.Q) THEN
    fbPoll(IN := FALSE, PT := tPollRate);
    fbModbusMaster.UnitID := nModbusSlaveAddress;
    fbModbusMaster.Execute := TRUE;
    fbModbusMaster.ReadInputRegs();
END_IF

// Copy the location resitance from the buffer to the FB output
nLocationResistance := WORD_TO_UINT(aModbusBuffer);

END_FUNCTION_BLOCK

MAIN

PROGRAM MAIN
VAR
    stErrors : ST_ErrorStates; // Triggered error states
    fbErrorTriggers : FB_ErrorTriggers; // Brand new errors this cycle

    // Leak Monitor
    {attribute 'pytmc' := 'pv: @(PREFIX):TTSIM:DEV01'}
    fbTraceTekModbus : FB_TraceTekModbus; // Modbus connection to TraceTek
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:LEAK:RESET'}
    bLeakMonitorReset : BOOL; // Leak monitor reset flag
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:LEAK'}
    fbLeakMonitor : FB_LeakMonitor; // Leak detection

    // Chiller Loop temperature monitors
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMP01'}
    fbChillerLoop01 : FB_ChillerLoop; // Chiller loop 01
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMP02'}
    fbChillerLoop02 : FB_ChillerLoop; // Chiller loop 02
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMP03'}
    fbChillerLoop03 : FB_ChillerLoop; // Chiller loop 03
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMP04'}
    fbChillerLoop04 : FB_ChillerLoop; // Chiller loop 04

    // Laser monitors
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:AMPHOS'}
    fbAmphos : FB_Laser; // Amphos laser
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:AMPHOS:RESET
        io: o
    '}
    bAmphosReset : BOOL; // Amphos laser error unlatch flag
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:OPCPA'}
    fbOpcpa : FB_Laser; // OPCPA laser
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:OPCPA:RESET
        io: o
    '}
    bOpcpaReset : BOOL; // OPCPA laser error unlatch flag


    (*
    // Temperature monitors
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON01'}
    fbTempMon01 : FB_TemperatureMonitor; // System temperature monitor 01
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON02'}
    fbTempMon02 : FB_TemperatureMonitor; // System temperature monitor 02
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON03'}
    fbTempMon03 : FB_TemperatureMonitor; // System temperature monitor 03
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON04'}
    fbTempMon04 : FB_TemperatureMonitor; // System temperature monitor 04
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON05'}
    fbTempMon05 : FB_TemperatureMonitor; // System temperature monitor 05
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON06'}
    fbTempMon06 : FB_TemperatureMonitor; // System temperature monitor 06
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON07'}
    fbTempMon07 : FB_TemperatureMonitor; // System temperature monitor 07
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:TEMPMON08'}
    fbTempMon08 : FB_TemperatureMonitor; // System temperature monitor 08
    *)

    // Chillers
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:DUMP_CHILLER'}
    fbDumpChiller : FB_DumpChiller; // Flow sensor monitor for the dump chiller
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:DUMP_CHILLER:RESET
        io: o
    '}
    bDumpChillerReset : BOOL; // Dump chiller error unlatch flag

    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:BASEPLATE_CHILLER'}
    fbBaseplateChiller : FB_BaseplateChiller; // Flow sensor monitor for the baseplate chiller
    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:BASEPLATE_CHILLER:RESET
        io: o
    '}
    bBaseplateChillerReset : BOOL; // Baseplate chiller error unlatch flag

    (*
    // Amphos MRCs
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:AMPHOS_MRC01'}
    fbAmphosMRC01 : FB_AmphosMRC; // MRC monitor 01
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:AMPHOS_MRC02'}
    fbAmphosMRC02 : FB_AmphosMRC; // MRC monitor 02
    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:AMPHOS_MRC03'}
    fbAmphosMRC03 : FB_AmphosMRC; // MRC monitor 03
    *)

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:MASTER_OVERRIDE
        io: o
    '}
    bMasterOverride : BOOL := TRUE; // Master override control for safety system.

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:MASTER_AMP_SHUTTER
        io: o
    '}
    bAmpShutterClosed : BOOL; // Master Amphos Shutter control

    {attribute 'pytmc' := '
        pv: @(PREFIX):LPS:MASTER_LATCH_RESET
        io: o
    '}
    bMasterLatchReset : BOOL; //Master release of latched error conditions

    bLatchStart : BOOL := FALSE; //Latch at start of loop.

    {attribute 'pytmc' := 'pv: @(PREFIX):LPS:PLC'}
    fbDiag : FB_EcatDiag; // Ethercat diagnostics
END_VAR
(* Ensure master latch is TRUE for at least one full cycle of the PLC logic *)
IF bMasterLatchReset AND NOT bLatchStart THEN
    bLatchStart := TRUE;
END_IF

(*******************************************************************
        Update diagnostics. Check for hardware failures
*******************************************************************)
(* Check PLC hardware for failures *)
fbDiag(AMSNetId := GVL_PLC.AMSNetId);
stErrors.bHardwareFailure := (fbDiag.bError OR fbTraceTekModbus.bError) AND NOT bMasterOverride;

(*******************************************************************
Feed each device function block inputs from the PLC IO. Collect error states detected
by the function blocks into stErrors. These collected errors are then used later for
making decisions in the interlock logic.
*******************************************************************)
(* Detects leaks using the location resitance from the TraceTek *)
fbLeakMonitor(
    nLocationResistance := fbTraceTekModbus.nLocationResistance,
    bAmphosOn := GVL_IO.bAmphosOn,
    bCarbideOn := GVL_IO.bCarbideOn,
    bLeakDetected := GVL_IO.bLeakDetected,
    stBound := GVL_TraceTek.stLeakMonitorBoundaries,
    bLeakOpticalTableAmphosOn => stErrors.bLeakOpticalTableAmphosOn,
    bLeakOpticaltableNoLaser => stErrors.bLeakOpticalTableNoLaser,
    bLeakOpcpaCarbideOn => stErrors.bLeakOpcpaCarbideOn,
    bLeakUnderTable => stErrors.bLeakUnderTable,
    bLeakInsideRacks => stErrors.bLeakInsideRacks,
    bMasterLatchReset := bMasterLatchReset
);

(* Monitors for error events on chiller loop 1 *)
fbChillerLoop01(
    bLoopTempSW := GVL_IO.bLoopTempSW01,
    bError => stErrors.bChillerLoop01
);

(* Monitors for error events on chiller loop 2 *)
fbChillerLoop02(
    bLoopTempSW := GVL_IO.bLoopTempSW02,
    bError => stErrors.bChillerLoop02
);

(* Monitors for error events on chiller loop 3 *)
fbChillerLoop03(
    bLoopTempSW := GVL_IO.bLoopTempSW03,
    bError => stErrors.bChillerLoop03
);

(* Monitors for error events on chiller loop 4 *)
fbChillerLoop04(
    bLoopTempSW := GVL_IO.bLoopTempSW04,
    bError => stErrors.bChillerLoop04
);

(* Monitors the Amphos laser *)
fbAmphos(
    nVoltageRaw := GVL_IO.nAmphosVoltageRaw,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_Laser.stAmphosSP,
    bError => stErrors.bAmphosBeam,
    bMasterLatchReset := bMasterLatchReset
);

(* Monitors the OPCPA laser *)
fbOpcpa(
    nVoltageRaw := GVL_IO.nOpcpaVoltageRaw,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_Laser.stOpcpaSP,
    bError => stErrors.bOpcpaBeam,
    bMasterLatchReset := bMasterLatchReset
);

(*
(* Monitors system temperature from sensor 1 *)
fbTempMon01(
    nTempMonRaw := GVL_IO.nTempMonRaw01,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP01,
    bError => stErrors.bTempMon01
);

(* Monitors system temperature from sensor 2 *)
fbTempMon02(
    nTempMonRaw := GVL_IO.nTempMonRaw02,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP02,
    bError => stErrors.bTempMon02
);

(* Monitors system temperature from sensor 3 *)
fbTempMon03(
    nTempMonRaw := GVL_IO.nTempMonRaw03,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP03,
    bError => stErrors.bTempMon03
);

(* Monitors system temperature from sensor 4 *)
fbTempMon04(
    nTempMonRaw := GVL_IO.nTempMonRaw04,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP04,
    bError => stErrors.bTempMon04
);

(* Monitors system temperature from sensor 5 *)
fbTempMon05(
    nTempMonRaw := GVL_IO.nTempMonRaw05,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP05,
    bError => stErrors.bTempMon05
);

(* Monitors system temperature from sensor 6 *)
fbTempMon06(
    nTempMonRaw := GVL_IO.nTempMonRaw06,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP06,
    bError => stErrors.bTempMon06
);

(* Monitors system temperature from sensor 7 *)
fbTempMon07(
    nTempMonRaw := GVL_IO.nTempMonRaw07,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP07,
    bError => stErrors.bTempMon07
);

(* Monitors system temperature from sensor 8 *)
fbTempMon08(
    nTempMonRaw := GVL_IO.nTempMonRaw08,
    bAmphosOn := GVL_IO.bAmphosOn,
    stSetpoints := GVL_TemperatureMonitor.stTempMonSP08,
    bError => stErrors.bTempMon08
);
*)

(* Flow sensor monitor for the dump chiller *)
fbDumpChiller(
    bAmphosOn := GVL_IO.bAmphosOn,
    bDumpChillerFlow := GVL_IO.bDumpChillerFlow,
    bError => stErrors.bDumpChiller,
    bMasterLatchReset := bMasterLatchReset
);

(* Flow sensor monitor for the baseplate chiller *)
fbBaseplateChiller(
    bAmphosOn := GVL_IO.bAmphosOn,
    bBaseplateChillerFlow := GVL_IO.bBaseplateChillerFlow,
    bError => stErrors.bBaseplateChiller,
    bMasterLatchReset := bMasterLatchReset
);

(*
(* Detects problems with MRC 1 *)
fbAmphosMRC01(
    bAmphosOn := GVL_IO.bAmphosOn,
    bMRC := GVL_IO.bAmphosMRC01,
    bError => stErrors.bAmphosMRC01
);

(* Detects problems with MRC 2 *)
fbAmphosMRC02(
    bAmphosOn := GVL_IO.bAmphosOn,
    bMRC := GVL_IO.bAmphosMRC02,
    bError => stErrors.bAmphosMRC02
);

(* Detects problems with MRC 3 *)
fbAmphosMRC03(
    bAmphosOn := GVL_IO.bAmphosOn,
    bMRC := GVL_IO.bAmphosMRC03,
    bError => stErrors.bAmphosMRC03
);
*)

(*******************************************************************
Now that each of the input signals have been checked and errors stored,
run the interlock logic.
*******************************************************************)
(* Enable Amphos attenuator if interlock logic passes *)
(*
GVL_IO.bAmphosAttenuatorEnable := F_AmphosAttenuatorEnable(
    bAmphosOn := GVL_IO.bAmphosOn,
    stErrors := stErrors
);
Deprecated Logic -- Not Currently Used*)

(* Enable Amphos shutter if interlock logic passes *)
GVL_IO.bAmphosShutterEnable := F_AmphosShutterEnable(
    bAmphosOn := GVL_IO.bAmphosOn,
    fOpcpaVoltage := fbOpcpa.fVoltage,
    bLoopTempOverride01 := fbChillerLoop01.bOperatorOverride,
    bLoopTempOverride02 := fbChillerLoop02.bOperatorOverride,
    bLoopTempOverride03 := fbChillerLoop03.bOperatorOverride,
    bLoopTempOverride04 := fbChillerLoop04.bOperatorOverride,
    stOpcpaSetpoints := GVL_Laser.stOpcpaSP,
    stErrors := stErrors,
    bMasterOverride := bMasterOverride,
    bAmpShutterClosed := bAmpShutterClosed
);

(* Enable Carbide shutter if interlock logic passes *)
GVL_IO.bCarbideShutterEnable := F_CarbideShutterEnable(
    stErrors := stErrors,
    bMasterOverride := bMasterOverride
);

(* Enable Dump chiller if interlock logic passes *)
GVL_IO.bDumpChillerRelay := F_DumpChillerEnable(
    stErrors := stErrors,
    bMasterOverride := bMasterOverride
);

(* Enable Baseplate chiller if interlock logic passes *)
GVL_IO.bBaseplateChillerRelay := F_BaseplateChillerEnable(
    stErrors := stErrors,
    bMasterOverride := bMasterOverride
);

(* Enable Amphos if interlock logic passes *)
GVL_IO.bAmphosRelay := F_AmphosEnable(
    fAmphosVoltage := fbAmphos.fVoltage,
    bEnabled := GVL_IO.bAmphosRelay,
    stAmphosSetpoints := GVL_Laser.stAmphosSP,
    stErrors := stErrors,
    bMasterOverride := bMasterOverride
);

(*******************************************************************
Safety related tasks have now been finished. Now try slower tasks such as
communicating with external hardware.
*******************************************************************)
(* Read from TraceTek hardware to get leak details. Errors are
handled later when diagnostics are run *)
fbTraceTekModbus(
    nModbusSlaveAddress := GVL_TraceTek.nModbusSlaveAddress,
    bLeakDetected := GVL_IO.bLeakDetected
);

(*******************************************************************
As per the specification, the reset PV is not checked until after the EPS has taken action.
Check the reset flags to see if they have been set and the errors can be unlatched.
*******************************************************************)
(* Check for rising-edge errors *)
fbErrorTriggers(stErrors := stErrors, nLeakLocations := fbLeakMonitor.nLeakLocations);

(* Check for new leak errors *)
IF fbErrorTriggers.NewLeakError() THEN
    bLeakMonitorReset := FALSE; // If there is a new error turn off reset
ELSIF bLeakMonitorReset THEN
    // If there are no new errors and reset is on, reset the errors
    fbLeakMonitor.Reset();
    fbErrorTriggers.ResetLeakTriggers();
END_IF

(* Check for new Amphos beam errors *)
IF fbErrorTriggers.stNewErrors.bAmphosBeam THEN
    bAmphosReset := FALSE; // If there is a new error turn off reset
ELSIF bAmphosReset THEN
    // If there are no new errors and reset is on, reset the errors
    fbAmphos.Reset();
    fbErrorTriggers.ResetAmphosTrigger();
END_IF

(* Check for new OPCPA errors *)
IF fbErrorTriggers.stNewErrors.bOpcpaBeam THEN
    bOpcpaReset := FALSE; // If there is a new error turn off reset
ELSIF bOpcpaReset THEN
    // If there are no new errors and reset is on, reset the errors
    fbOpcpa.Reset();
    fbErrorTriggers.ResetOpcpaTrigger();
END_IF

(* Check for new Beam Dump chiller errors *)
IF fbErrorTriggers.stNewErrors.bDumpChiller THEN
    bDumpChillerReset := FALSE; // If there is a new error turn off reset
ELSIF bDumpChillerReset THEN
    fbDumpChiller.Reset();
    fbErrorTriggers.ResetDumpChillerTrigger();
END_IF

(* Check for new Baseplate chiller errors *)
IF fbErrorTriggers.stNewErrors.bBaseplateChiller THEN
    bBaseplateChillerReset := FALSE; // If there is a new error turn off reset
ELSIF bBaseplateChillerReset THEN
    // If there are no new errors and reset is on, reset the errors
    fbBaseplateChiller.Reset();
    fbErrorTriggers.ResetBaseplateChillerTrigger();
END_IF

(* Remove Master Latch Reset after one scan *)
IF bMasterLatchReset AND bLatchStart THEN
    bMasterLatchReset := FALSE;
    bLatchStart := FALSE;
END_IF

END_PROGRAM
Related: