DUTs

ST_EpicsMotorMSTA

TYPE ST_EpicsMotorMSTA :
STRUCT

    (* DIRECTION: last raw direction; (0:Negative, 1:Positive) *)
    bPositiveDirection : BIT;
    (* DONE: motion is complete. *)
    bDone : BIT;
    (* PLUS_LS: plus limit switch has been hit. *)
    bPlusLimitSwitch : BIT;
    (* HOMELS: state of the home limit switch. *)
    bHomeLimitSwitch : BIT;
    (* Unused *)
    bUnused0 : BIT;
    (* POSITION: closed-loop position control is enabled. *)
    bClosedLoop : BIT;
    (* SLIP_STALL: Slip/Stall detected (eg. fatal following error) *)
    bSlipStall : BIT;
    (* HOME: if at home position. *)
    bHome : BIT;
    (* PRESENT: encoder is present. *)
    bEncoderPresent : BIT;
    (* PROBLEM: driver stopped polling, or hardware problem *)
    bHardwareProblem : BIT;
    (* MOVING: non-zero velocity present. *)
    bMoving : BIT;
    (* GAIN_SUPPORT: motor supports closed-loop position control. *)
    bGainSupport : BIT;
    (* COMM_ERR: Controller communication error. *)
    bCommError : BIT;
    (* MINUS_LS: minus limit switch has been hit. *)
    bMinusLimitSwitch : BIT;
    (* HOMED: the motor has been homed. *)
    bHomed : BIT;

END_STRUCT
END_TYPE
Related:

ST_ShutterControl

TYPE ST_ShutterControl :
STRUCT

    {attribute 'pytmc' := '
        pv: REQ
        io: io
        field: DESC LSS Shutter Open Request
    '}
    bOpenRequest AT %Q* : BOOL;
    {attribute 'pytmc' := '
        pv: OPN
        io: i
        field: DESC LSS Shutter Open
    '}
    bOpenStatus AT %I* : BOOL;
    {attribute 'pytmc' := '
        pv: CLS
        io: i
        field: DESC LSS Shutter Closed
    '}
    bCloseStatus AT %I* : BOOL;
    {attribute 'pytmc' := '
        pv: LSS
        io: i
        field: DESC LSS Permission Status
    '}
    bLssStatus AT %I* : BOOL;

END_STRUCT
END_TYPE

ST_SourceDataStore

TYPE ST_SourceDataStore :
STRUCT
    {attribute 'pytmc' := 'pv: Name; io: input'}
    sName : STRING;

    (*  Do not pragma valve; Use original PVs to access valve state *)
    fbSourceValve : REFERENCE TO FB_VGC;

    {attribute 'pytmc' := '
        pv: Valid
        io: input
        field: ZNAM Data Invalid
        field: ONAM Data Valid
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bDataValid : BOOL;

    {attribute 'pytmc' := '
        pv: EntryValveReady
        io: input
        field: ZNAM Not ready
        field: ONAM Ready
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bEntryValveReady : BOOL;

    {attribute 'pytmc' := '
        pv: ChecksOK
        io: input
        field: ZNAM Failed
        field: ONAM Passed
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bChecksOK : BOOL;

    {attribute 'pytmc' := '
        pv: InPosition
        io: input
        field: ZNAM FALSE
        field: ONAM TRUE
        field: DESC Linear stage in position for dest
    '}
    bInPosition : BOOL;

    {attribute 'pytmc' := 'pv: Linear'}
    fbLinear : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: Rotary'}
    fbRotary : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: Goniometer'}
    fbGoniometer : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: NFCenterX'}
    fbNFCentroidX : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: NFCenterY'}
    fbNFCentroidY : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: FFCenterX'}
    fbFFCentroidX : FB_RangeComparison;
    {attribute 'pytmc' := 'pv: FFCenterY'}
    fbFFCentroidY : FB_RangeComparison;

END_STRUCT
END_TYPE
Related:

GVLs

GVL_BTPS

{attribute 'qualified_only'}
VAR_GLOBAL
    (*
    Motor - Linear
        LAS:BTS:MCS2:01:m1 (ioc-las-bts-mcs1, las_bts_mcs2_01_m1)
        LAS:BTS:MCS2:01:m4 (ioc-las-bts-mcs1, las_bts_mcs2_01_m4)
        LAS:BTS:MCS2:01:m7 (ioc-las-bts-mcs1, las_bts_mcs2_01_m7)
        LAS:BTS:MCS2:01:m13
        LAS:BTS:MCS2:01:m10
        LAS:BTS:MCS2:01:m16
    *)
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS1:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m1
    '}
    fbLS1Linear : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS5:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m4
    '}
    fbLS5Linear : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS8:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m7
    '}
    fbLS8Linear : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS3:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m13
    '}
    fbLS3Linear : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS4:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m10
    '}
    fbLS4Linear : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS6:Lin
        link: @(SIM)LAS:BTS:MCS2:01:m16
    '}
    fbLS6Linear : FB_EpicsMotorMonitor;

    (*
        Motor - Rotary
            LAS:BTS:MCS2:01:m2 (ioc-las-bts-mcs1, las_bts_mcs2_01_m2)
            LAS:BTS:MCS2:01:m6 (ioc-las-bts-mcs1, las_bts_mcs2_01_m6)
            LAS:BTS:MCS2:01:m8 (ioc-las-bts-mcs1, las_bts_mcs2_01_m8)
            LAS:BTS:MCS2:01:m14
            LAS:BTS:MCS2:01:m12
            LAS:BTS:MCS2:01:m17
    *)
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS1:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m2
    '}
    fbLS1Rotary : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS5:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m6
    '}
    fbLS5Rotary : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS8:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m8
    '}
    fbLS8Rotary : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS3:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m14
    '}
    fbLS3Rotary : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS4:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m12
    '}
    fbLS4Rotary : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS6:Rot
        link: @(SIM)LAS:BTS:MCS2:01:m17
    '}
    fbLS6Rotary : FB_EpicsMotorMonitor;

    (*
        Motor - Goniometer
            LAS:BTS:MCS2:01:m3 (ioc-las-bts-mcs1, las_bts_mcs2_01_m3)
            LAS:BTS:MCS2:01:m5 (ioc-las-bts-mcs1, las_bts_mcs2_01_m5)
            LAS:BTS:MCS2:01:m9 (ioc-las-bts-mcs1, las_bts_mcs2_01_m9)
    *)
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS1:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m3
    '}
    fbLS1Goniometer : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS5:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m5
    '}
    fbLS5Goniometer : FB_EpicsMotorMonitor;
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS8:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m9
    '}
    fbLS8Goniometer : FB_EpicsMotorMonitor;

//1um / 800nm Lines
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS3:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m15
    '}
    fbLS3Goniometer : FB_EpicsMotorMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS4:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m11
    '}
    fbLS4Goniometer : FB_EpicsMotorMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS6:Gon
        link: @(SIM)LAS:BTS:MCS2:01:m18
    '}
    fbLS6Goniometer : FB_EpicsMotorMonitor;

(* Near field camera stats plugins, per bay *)
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS1:NF
        link: @(SIM)LAS:LHN:BAY1:CAM:01:Stats2:
    '}
    fbLS1NearFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS5:NF
        link: @(SIM)LAS:LHN:BAY3:CAM:01:Stats2:
    '}
    fbLS5NearFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS8:NF
        link: @(SIM)LAS:LHN:BAY4:CAM:01:Stats2:
    '}
    fbLS8NearFieldCamStats : FB_EpicsStatsMonitor;
// 1um/ 800 nm line
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS3:NF
        link: @(SIM)LAS:LHN:BAY2:CAM:01:Stats2:
    '}
    fbLS3NearFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS4:NF
        link: @(SIM)LAS:LHN:BAY2:CAM:01:Stats2:
    '}
    fbLS4NearFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS6:NF
        link: @(SIM)LAS:LHN:BAY3:CAM:01:Stats2:
    '}
    fbLS6NearFieldCamStats : FB_EpicsStatsMonitor;

    (* Far-field camera stats plugins, per bay *)
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS1:FF
        link: @(SIM)LAS:LHN:BAY1:CAM:02:Stats2:
    '}
    fbLS1FarFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS5:FF
        link: @(SIM)LAS:LHN:BAY3:CAM:02:Stats2:
    '}
    fbLS5FarFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS8:FF
        link: @(SIM)LAS:LHN:BAY4:CAM:02:Stats2:
    '}
    fbLS8FarFieldCamStats : FB_EpicsStatsMonitor;
//1um and 800nm Lines
    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS3:FF
        link: @(SIM)LAS:LHN:BAY2:CAM:02:Stats2:
    '}
    fbLS3FarFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS4:FF
        link: @(SIM)LAS:LHN:BAY2:CAM:02:Stats2:
    '}
    fbLS4FarFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Chk:LS6:FF
        link: @(SIM)LAS:LHN:BAY3:CAM:02:Stats2:
    '}
    fbLS6FarFieldCamStats : FB_EpicsStatsMonitor;

    {attribute 'pytmc' := 'pv: LTLHN:LS1:BTPS'}
    fbSafetyLS1 : FB_ShutterSafety;
    {attribute 'pytmc' := 'pv: LTLHN:LS5:BTPS'}
    fbSafetyLS5 : FB_ShutterSafety;
    {attribute 'pytmc' := 'pv: LTLHN:LS8:BTPS'}
    fbSafetyLS8 : FB_ShutterSafety;
    {attribute 'pytmc' := 'pv: LTLHN:LS3:BTPS'}
    fbSafetyLS3 : FB_ShutterSafety;
    {attribute 'pytmc' := 'pv: LTLHN:LS4:BTPS'}
    fbSafetyLS4 : FB_ShutterSafety;
    {attribute 'pytmc' := 'pv: LTLHN:LS6:BTPS'}
    fbSafetyLS6 : FB_ShutterSafety;

    {attribute 'pytmc' := '
        pv: LTLHN:LD
        expand: %d
    '}
    fbDestinations : ARRAY [1..GVL_BTPS_Constants.nDestinations] OF FB_DestinationDataStore;

END_VAR
Related:

GVL_BTPS_Constants

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    nSources : UINT := 8;
    (* Destinations

        LTLHN:LD2:VGC:01 (ioc-las-bts) - TMO - IP3
        LTLHN:LD4:VGC:01 (ioc-las-bts) - RIX - ChemRIXS
        LTLHN:LD6:VGC:01 (ioc-las-bts) - RIX - qRIXS
        LTLHN:LD8:VGC:01 (ioc-las-bts) - TMO - IP1
        LTLHN:LD9:VGC:01 (ioc-las-bts) - Laser Lab
        LTLHN:LD10:VGC:01 (ioc-las-bts) - TMO - IP2
        LTLHN:LD14:VGC:01 (ioc-las-bts) - XPP

    *)
    nDestinations: UINT := 14;
END_VAR

GVL_BTPS_Retain

{attribute 'qualified_only'}
VAR_GLOBAL PERSISTENT

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Config:MaxFrameTime
        field: DESC Maximum delay from frame to frame
        field: EGU sec
        io: io
    '}
    fMaximumFrameTime : LREAL := 5.0;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Config:MinPixelChange
        field: DESC Minimum px change to be considered valid
        field: EGU px
        io: io
    '}
    fMinPixelChange : LREAL := 0.05;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Config:SystemOverride
        field: DESC Full system-level override
        io: io
    '}
    bSystemOverride : BOOL;

    {attribute 'pytmc' := '
        pv: LTLHN:BTPS:Config:Maintenance
        io: io
        field: DESC System undergoing maintenance
        field: ZNAM Normal operation
        field: ONAM Maintenance
        field: ZSV NO_ALARM
        field: OSV MAJOR
    '}
    bMaintenanceMode : BOOL;

END_VAR

GVL_BTS_VAC

//{attribute 'qualified_only'}
VAR_GLOBAL
    (* Valves *)
    g_FFOut : FB_HardwareFFOutput;

    {attribute 'pytmc'        :=    ' pv: LXLHN:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_12]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_12]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_14]^Channel 2^Output'}
    fb_PP_VGC : FB_VGC; // Gate valve, vacuum switchbox

    {attribute 'pytmc'        :=    ' pv: LXLHN:VVC:01 '}
    {attribute 'TcLinkTo' :=        '.q_xOPN_DO                     :=      TIIB[EL2004_02_05]^Channel 3^Output'}
    fb_PP_VVC : FB_VVC; // Turbo N2 purge


    {attribute 'pytmc'        :=    ' pv: LXLHN:VRC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1002_02_01]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1002_02_01]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_04]^Channel 1^Output'}
    fb_PP_VRC_1 : FB_VRC; // O-ring diff pump valve

    {attribute 'pytmc'        :=    ' pv: LXLHN:VRC:02 '}
    {attribute 'TcLinkTo' :=        '.q_xOPN_DO                     :=      TIIB[EL2004_02_04]^Channel 3^Output'}
                                (*'.i_xOpnLS                        :=      TIIB[EL1002_02_02]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1002_02_02]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_04]^Channel 3^Output'}           *)
    fb_PP_VRC_2 : FB_VVC; // Foreline valve //valve has bad readouts so using VVC

    {attribute 'pytmc'        :=    ' pv: LXLHN:VRC:03 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1002_02_03]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1002_02_03]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_04]^Channel 4^Output'}
    fb_PP_VRC_3 : FB_VRC; // Slow pump valve

    st_VP_VVC_1 : ST_VVC;
    {attribute 'pytmc'        :=    ' pv: LXLHN:VVC:02 '}
    {attribute 'TcLinkTo' :=        '.q_xOPN_DO                     :=      TIIB[EL2004_02_05]^Channel 1^Output'}
    fb_VP_VVC_1 : FB_VVC; // Vent valve, Switchbox

    st_VP_VVC_2 : ST_VVC;
    {attribute 'pytmc'        :=    ' pv: LXLHN:VVC:03 '}
    {attribute 'TcLinkTo' :=        '.q_xOPN_DO                     :=      TIIB[EL2004_02_05]^Channel 2^Output'}
    fb_VP_VVC_2 : FB_VVC; // Vent valve, Slow, Switchbox

// Vent Valve addition
    st_VP_VVC_4 : ST_VVC;
    {attribute 'pytmc'        :=    ' pv: LXLHN:VVC:04 '}
    {attribute 'TcLinkTo' :=        '.q_xOPN_DO                     :=      TIIB[EL2004_02_15]^Channel 1^Output'}
    fb_VP_VVC_4 : FB_VVC;

    {attribute 'pytmc'        :=    ' pv: LXLHN:VRC:04 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_03_17]^Channel 1^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_03_17]^Channel 2^Input'}
    fb_PP_VRC_4 : FB_VRC; // Turbo Isolation Valve


    (* Pumps *)
    {attribute 'pytmc'        := '
        pv: LXLHN:PTM:01
        io: io
    '}
    {attribute 'TcLinkTo' :=        '.i_xAtSpd                      :=      TIIB[EL1004_00_03]^Channel 3^Input;
                                 .i_xRemote                 :=      TIIB[EL1004_00_03]^Channel 2^Input;
                                 .i_xFaultNC                :=      TIIB[EL1004_00_03]^Channel 1^Input;
                                 .q_RunDO                   :=      TIIB[EL2004_00_04]^Channel 1^Output;
                                 .q_xRemote                 :=      TIIB[EL2004_00_04]^Channel 2^Output;
                                 .q_PumpingStation  :=      TIIB[EL2004_00_04]^Channel 3^Output'}
    fb_PP_PTM : FB_PTM_Pfeiffer; // Turbo pump

    // Serial port
    st_Pfeiffer_CTRL_PTM            : ARRAY[1..20] OF ST_PfeifferControl;
    st_Pfeiffer_RBK_PTM                     : ARRAY[1..20] OF ST_PfeifferStatus;
    PTM_COM                                         : FB_PFEIFFER_COM;

    {attribute 'pytmc'        := '
        pv: LXLHN:PMF:01
        io: io
    '}
    {attribute 'TcLinkTo' :=        '.i_xWarning            :=      TIIB[EL1004_00_01]^Channel 4^Input;
                                 .i_xAlarm                  :=      TIIB[EL1004_00_01]^Channel 3^Input;
                                 .i_xRemote                 :=      TIIB[EL1004_00_01]^Channel 2^Input;
                                 .i_xIsRun                  :=      TIIB[EL1004_00_01]^Channel 1^Input;
                                 .q_xRunDO                  :=      TIIB[EL2794_00_02]^Channel 1^Output;
                                 .q_xLspdDO                 :=      TIIB[EL2794_00_02]^Channel 2^Output;
                                 .q_xResetDo                :=      TIIB[EL2794_00_02]^Channel 3^Output'}
    fb_PP_PMF : FB_KashiyamaPump; // Roughing Pump

    {attribute 'pytmc'    :=        ' pv: LXLHN:PIP:01'}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_08]^AI Standard Channel 3^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_07]^Channel 3^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_06]^Channel 3^Output'}
    fb_PP_PIP : FB_PIP_Gamma; // Ion Pump, SW

    (* Gauges *)
    {attribute 'pytmc'        :=    ' pv: LXLHN:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_10]^AI Standard Channel 3^Value '}
    fb_PP_GPI_1 : FB_MKS275; // Switchbox pumpline ATM gauge

    {attribute 'pytmc'        :=    ' pv: LXLHN:GPI:02 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_11]^AI Standard Channel 1^Value '}
    fb_PP_GPI_2 : FB_MKS275; // Foreline gauge

    {attribute 'pytmc'        :=    ' pv: LXLHN:GPI:03 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_10]^AI Standard Channel 2^Value '}
    fb_DP_GPI : FB_MKS275; // Switchbox gauge

    {attribute 'pytmc'        :=    ' pv: LXLHN:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_09]^AI Standard Channel 2^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_06]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_06]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_02]^Channel 2^Output     '}
    fb_PP_GCC : FB_MKS500; // Switchbox pumpline Cold Cathode gauge

    {attribute 'pytmc'        :=    ' pv: LXLHN:GCC:02 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_09]^AI Standard Channel 4^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_07]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_07]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_02]^Channel 4^Output     '}
    fb_DP_GCC : FB_MKS500; // Switchbox Cold Cathode gauge

    (* Transport Tubes *)
    // LS1
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS1:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_06]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_06]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_08]^Channel 1^Output'}
    fb_LS1_VGC : FB_VGC; // Gate valve, Input LS1 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS1:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_08]^AI Standard Channel 2^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_07]^Channel 2^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_06]^Channel 2^Output'}
    fb_LS1_PIP : FB_PIP_Gamma; // Ion Pump, input tube LS1
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS1:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_10]^AI Standard Channel 4^Value '}
    fb_LS1_GPI : FB_MKS275; // Input tube LS1 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS1:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_10]^AI Standard Channel 4^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_06]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_06]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_01]^Channel 4^Output     '}
    fb_LS1_GCC : FB_MKS500; // Input tube LS1, Cold Cathode gauge

    // LS5
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS5:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_09]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_09]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_11]^Channel 2^Output'}
    fb_LS5_VGC : FB_VGC; // Gate valve, Input LS5 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS5:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_11]^AI Standard Channel 4^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_10]^Channel 4^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_09]^Channel 4^Output'}
    fb_LS5_PIP : FB_PIP_Gamma; // Ion Pump, input tube LS5
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS5:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_13]^AI Standard Channel 1^Value '}
    fb_LS5_GPI : FB_MKS275; // Input tube LS5 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS5:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_11]^AI Standard Channel 1^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_08]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_08]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_02]^Channel 1^Output     '}
    fb_LS5_GCC : FB_MKS500; // Input tube LS5, Cold Cathode gauge

    // LS8
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS8:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_09]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_09]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_11]^Channel 3^Output'}
    fb_LS8_VGC : FB_VGC; // Gate valve, Input LS8 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS8:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_11]^AI Standard Channel 3^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_10]^Channel 3^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_09]^Channel 3^Output'}
    fb_LS8_PIP : FB_PIP_Gamma; // Ion Pump, input tube LS8
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS8:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_13]^AI Standard Channel 2^Value '}
    fb_LS8_GPI : FB_MKS275; // Input tube LS8 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS8:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_11]^AI Standard Channel 2^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_08]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_08]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_02]^Channel 2^Output     '}
    fb_LS8_GCC : FB_MKS500; // Input tube LS8, Cold Cathode gauge

    // LD2
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD2:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_12]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_12]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_14]^Channel 3^Output'}
    fb_LD2_VGC : FB_VGC; // Gate valve, Output tube LD2 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD2:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_08]^AI Standard Channel 1^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_07]^Channel 1^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_06]^Channel 1^Output'}
    fb_LD2_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD2
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD2:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_10]^AI Standard Channel 1^Value '}
    fb_LD2_GPI : FB_MKS275; // Output tube LD2 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD2:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_09]^AI Standard Channel 1^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_07]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_07]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_02]^Channel 1^Output     '}
    fb_LD2_GCC : FB_MKS500; // Output tube LD2, Cold Cathode gauge

    // LD4
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD4:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_10]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_10]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_11]^Channel 1^Output'}
    fb_LD4_VGC : FB_VGC; // Gate valve, Output tube LD4 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD4:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_11]^AI Standard Channel 1^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_10]^Channel 1^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_09]^Channel 1^Output'}
    fb_LD4_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD4
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD4:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_13]^AI Standard Channel 4^Value '}
    fb_LD4_GPI : FB_MKS275; // Output tube LD4 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD4:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_11]^AI Standard Channel 4^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_09]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_09]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_02]^Channel 4^Output     '}
    fb_LD4_GCC : FB_MKS500; // Output tube LD4, Cold Cathode gauge

    // LD6
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD6:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_13]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_13]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_14]^Channel 4^Output'}
    fb_LD6_VGC : FB_VGC; // Gate valve, Output tube LD6 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD6:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_11]^AI Standard Channel 2^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_10]^Channel 2^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_09]^Channel 2^Output'}
    fb_LD6_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD6
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD6:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_13]^AI Standard Channel 3^Value '}
    fb_LD6_GPI : FB_MKS275; // Output tube LD6 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD6:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3064_02_16]^AI Standard Channel 2^Value;
                                 .q_xHV_DIS         :=      TIIB[EL2794_02_17]^Channel 2^Input'}
    fb_LD6_GCC : FB_MKS422; // Output tube LD6, Cold Cathode gauge

    // LD8
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD8:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_07]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_07]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_08]^Channel 4^Output'}
    fb_LD8_VGC : FB_VGC; // Gate valve, Output tube LD8 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD8:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_08]^AI Standard Channel 4^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_07]^Channel 4^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_06]^Channel 4^Output'}
    fb_LD8_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD8
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD8:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_11]^AI Standard Channel 2^Value '}
    fb_LD8_GPI : FB_MKS275; // Output tube LD8 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD8:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_11]^AI Standard Channel 3^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_08]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_08]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_01]^Channel 1^Output     '}
    fb_LD8_GCC : FB_MKS500; // Output tube LD8, Cold Cathode gauge

    // LD9
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD9:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_06]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_06]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_08]^Channel 2^Output'}
    fb_LD9_VGC : FB_VGC; // Gate valve, Output tube LD9 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD9:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_14]^AI Standard Channel 2^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_13]^Channel 2^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_12]^Channel 2^Output'}
    fb_LD9_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD9
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD9:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_12]^AI Standard Channel 1^Value '}
    fb_LD9_GPI : FB_MKS275; // Output tube LD9 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD9:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_10]^AI Standard Channel 1^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_06]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_06]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_01]^Channel 1^Output     '}
    fb_LD9_GCC : FB_MKS500; // Output tube LD9, Cold Cathode gauge

    // LD10
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD10:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_07]^Channel 2^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_07]^Channel 1^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_08]^Channel 3^Output'}
    fb_LD10_VGC : FB_VGC; // Gate valve, Output tube LD10 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD10:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_14]^AI Standard Channel 1^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_13]^Channel 1^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_12]^Channel 1^Output'}
    fb_LD10_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD10
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD10:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_12]^AI Standard Channel 2^Value '}
    fb_LD10_GPI : FB_MKS275; // Output tube LD10 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD10:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_10]^AI Standard Channel 2^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_07]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_07]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_01]^Channel 2^Output     '}
    fb_LD10_GCC : FB_MKS500; // Output tube LD10, Cold Cathode gauge

    // LD14
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD14:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_10]^Channel 4^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_10]^Channel 3^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_11]^Channel 4^Output'}
    fb_LD14_VGC : FB_VGC; // Gate valve, Output tube LD14 laser Table
    {attribute 'pytmc'    :=        ' pv: LTLHN:LD14:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_00_14]^AI Standard Channel 3^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_00_13]^Channel 3^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_00_12]^Channel 3^Output'}
    fb_LD14_PIP : FB_PIP_Gamma; // Ion Pump, output tube LD14
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD14:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_12]^AI Standard Channel 3^Value '}
    fb_LD14_GPI : FB_MKS275; // Output tube LD14 ATM gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LD14:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_03_10]^AI Standard Channel 3^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_03_07]^Channel 1^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_03_07]^Channel 2^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_03_01]^Channel 3^Output     '}
    fb_LD14_GCC : FB_MKS500; // Output tube LD14, Cold Cathode gauge

    ////Bay 2 - Line Upgrade
    // LS3
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS3:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_8A]^AI Standard Channel 2^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_06]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_06]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_01]^Channel 3^Output'}
    fb_LS3_GCC : FB_MKS500; // LS3, Cold Cathode gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS3:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_11]^AI Standard Channel 4^Value '}
    fb_LS3_GPI : FB_MKS275; // LS3 Pirani gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS3:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_00_16]^Channel 1^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_00_16]^Channel 2^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_00_17]^Channel 1^Output'}
    fb_LS3_VGC : FB_VGC; // Gate valve
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS3:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_01_15]^AI Standard Channel 1^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_01_16]^Channel 1^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_01_17]^Channel 1^Output'}
    fb_LS3_PIP : FB_PIP_Gamma; // Ion Pump
    //LS4
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS4:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_8A]^AI Standard Channel 3^Value;
                                 .i_xHV_ON          :=      TIIB[EL1124_01_08]^Channel 3^Input;
                                 .i_xDisc_Active    :=      TIIB[EL1124_01_08]^Channel 4^Input;
                                 .q_xHV_DIS                 :=      TIIB[EL2624_01_01]^Channel 4^Output     '}
    fb_LS4_GCC : FB_MKS500; // LS4, Cold Cathode gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS4:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_09]^AI Standard Channel 3^Value '}
    fb_LS4_GPI : FB_MKS275; // LS4 Pirani gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS4:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_00_16]^Channel 3^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_00_16]^Channel 4^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_00_17]^Channel 2^Output'}
    fb_LS4_VGC : FB_VGC; // Gate valve
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS4:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_01_15]^AI Standard Channel 2^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_01_16]^Channel 2^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_01_17]^Channel 2^Output'}
    fb_LS4_PIP : FB_PIP_Gamma; // Ion Pump
    //LS6
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS6:GCC:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3064_02_16]^AI Standard Channel 1^Value;
                                 .q_xHV_DIS         :=      TIIB[EL2794_02_17]^Channel 1^Input'}
    fb_LS6_GCC : FB_MKS422; // LS6, Cold Cathode gauge

    {attribute 'pytmc'        :=    ' pv: LTLHN:LS6:GPI:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS_R            :=      TIIB[EL3174_01_09]^AI Standard Channel 3^Value '}
    fb_LS6_GPI : FB_MKS275; // LS6 Pirani gauge
    {attribute 'pytmc'        :=    ' pv: LTLHN:LS6:VGC:01 '}
    {attribute 'TcLinkTo' :=        '.i_xOpnLS                      :=      TIIB[EL1004_02_13]^Channel 1^Input;
                                 .i_xClsLS                  :=      TIIB[EL1004_02_13]^Channel 2^Input;
                                 .q_xOPN_DO                 :=      TIIB[EL2004_02_14]^Channel 1^Output'}
    fb_LS6_VGC : FB_VGC; // Gate valve
    {attribute 'pytmc'    :=        ' pv: LTLHN:LS6:PIP:01 '}
    {attribute 'TcLinkTo' :=        '.i_iPRESS                      :=      TIIB[EL3064_01_15]^AI Standard Channel 3^Value;
                                 .i_xSP_DI                  :=      TIIB[EL1004_01_16]^Channel 3^Input;
                                 .q_xHVEna_DO               :=      TIIB[EL2794_01_17]^Channel 3^Output'}
    fb_LS6_PIP : FB_PIP_Gamma; // Ion Pump
    //Preemptive Arbiter Content
    g_fbArbiter    :   FB_Arbiter(1);

END_VAR

GVL_COM

//{attribute 'qualified_only'}
VAR_GLOBAL
    // Define COM Ports/Buffers incl. library Tc2_SerialCom

    //LXLHN_PTM_01
    PTM_SerialRXBuffer : ComBuffer;
    PTM_SerialTXBuffer : ComBuffer;

    // Serial Terminal
    PTM_fbSerialLineControl: SerialLineControl;

    //SERIAL IO //EL6021_00_05
    {attribute      'TcLinkTo'      :=      '.Status:=TIIB[EL6021_00_05)]^COM TxPDO-Map Inputs^Status;
                                 .D[0]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 0;
                                 .D[1]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 1;
                                 .D[2]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 2;
                                 .D[3]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 3;
                                 .D[4]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 4;
                                 .D[5]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 5;
                                 .D[6]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 6;
                                 .D[7]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 7;
                                 .D[8]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 8;
                                 .D[9]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 9;
                                 .D[10]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 10;
                                 .D[11]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 11;
                                 .D[12]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 12;
                                 .D[13]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 13;
                                 .D[14]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 14;
                                 .D[15]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 15;
                                 .D[16]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 16;
                                 .D[17]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 17;
                                 .D[18]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 18;
                                 .D[19]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 19;
                                 .D[20]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 20;
                                 .D[21]:=TIIB[EL6021_00_05]^COM TxPDO-Map Inputs^Data In 21;
    '}
    PTM_stComIn             AT %I*  :       EL6inData22B;
    {attribute      'TcLinkTo'      :=      '.Ctrl:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Ctrl;
                                 .D[0]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 0;
                                 .D[1]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 1;
                                 .D[2]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 2;
                                 .D[3]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 3;
                                 .D[4]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 4;
                                 .D[5]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 5;
                                 .D[6]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 6;
                                 .D[7]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 7;
                                 .D[8]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 8;
                                 .D[9]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 9;
                                 .D[10]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 10;
                                 .D[11]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 11;
                                 .D[12]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 12;
                                 .D[13]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 13;
                                 .D[14]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 14;
                                 .D[15]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 15;
                                 .D[16]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 16;
                                 .D[17]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 17;
                                 .D[18]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 18;
                                 .D[19]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 19;
                                 .D[20]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 20;
                                 .D[21]:=TIIB[EL6021_00_05]^COM RxPDO-Map Outputs^Data Out 21;
    '}
    PTM_stComOut            AT %Q*  :       EL6outData22B;

END_VAR

GVL_Constants

VAR_GLOBAL CONSTANT
    ATM_PRESS : INT := 760; // Atmospheric pressure in Torr
    ATM_PRESS_ERR_RNG : INT := 10; // Error range that is still considered atmospheric pressure

    EPICS_STRING_SIZE : INT := 40;
    PTM_INTERLOCK_SP : REAL := 1E-2;
END_VAR

GVL_Shutters

{attribute 'qualified_only'}
VAR_GLOBAL

    {attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_01^Channel 1^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_02^Channel 1^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_02^Channel 2^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_04^Channel 1^Input;
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS1:LST'}
    fbLS1 : ST_ShutterControl;

    {attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_01^Channel 3^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_03^Channel 1^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_03^Channel 2^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_04^Channel 3^Input;
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS5:LST'}
    fbLS5 : ST_ShutterControl;

    {attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_01^Channel 4^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_03^Channel 3^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_03^Channel 4^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_04^Channel 4^Input;
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS8:LST'}
    fbLS8 : ST_ShutterControl;

// Shutter controls for 1um Line

{attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_05^Channel 1^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_06^Channel 1^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_06^Channel 2^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_07^Channel 1^Input;
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS3:LST'}
    fbLS3 : ST_ShutterControl;

// Shutter controls for 800nm Line
{attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_01^Channel 2^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_02^Channel 3^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_02^Channel 4^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_04^Channel 2^Input
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS4:LST'}
    fbLS4 : ST_ShutterControl;
{attribute 'TcLinkTo' := '
        .bOpenRequest       :=      TIID^EtherCAT1^EK1100_04_00^EL2624_04_05^Channel 3^Output;
        .bOpenStatus        :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_06^Channel 3^Input;
        .bCloseStatus       :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_06^Channel 4^Input;
        .bLssStatus         :=      TIID^EtherCAT1^EK1100_04_00^EL1004_04_07^Channel 2^Input;
    '}
    {attribute 'pytmc' := 'pv: LTLHN:LS6:LST'}
    fbLS6 : ST_ShutterControl;

END_VAR
Related:

GVL_Variables

//{attribute 'qualified_only'}
VAR_GLOBAL
    {attribute 'pytmc' := '
    pv: LXLHN:VAC:OVRD_ON
    field: ZNAM Override Off ;
    field: ONAM Override On ;
    '}
    xSystemOverrideMode_BTS : BOOL := FALSE; (* Global system override for the BTS Vacuum System *)

    {attribute 'pytmc' := 'pv: LXLHN:VAC:GCC:ENABLE'}
    xGCC_Enable_SW: BOOL := FALSE; (* Global enable for all GCCs *)
END_VAR

POUs

DIAGNOSTICS

PROGRAM DIAGNOSTICS
VAR
    //Change the PLC String Name to the actual PLC NAME
    sPLCName :STRING := 'PLC-LAS-BTS';
    //Link the sAMSNetID to the Etherat Master netID
    i_sAMSNetID AT %I*: AMSNETID;
    // DO NOT CHANGE
    sAMSNetID : STRING; //used for EPICS PV
    sLibVersion_LCLS_General : STRING;(* := stLibVersion_LCLS_General.sVersion*) ;

    fbEcatDiag:FB_EcatDiag;
    {attribute 'pytmc' := ' pv:PLC:LAS:BTS:AllSlaveStateGood '}
    bAllSlaveStateGood : BOOL;
    {attribute 'pytmc' := ' pv:PLC:LAS:BTS:MasterStateGood '}
    bMasterStateGood :BOOL;
    iMasterState: WORD;
    sMasterState:STRING;
    nSlaveNumber : UINT;
    aiSlaveStates: ARRAY[1..256] OF BYTE;
    aEcSlaveInfo :  ARRAY[1..256] OF ST_EcDevice;
END_VAR
// Instaniating a call to the fbEcatDiag
fbEcatDiag(
    I_AMSNetId:=i_sAMSNetID,
    i_xFirstPass:= _TaskInfo[1].FirstCycle,
    q_xAllSlaveStatesGood=> bAllSlaveStateGood,
    q_anTermStates=> aiSlaveStates,
    q_xMasterStateGood=>bMasterStateGood,
    q_nMasterState=> iMasterState,
    q_sMasterState=> sMasterState,
    q_astEcConfSlaveInfo=> aEcSlaveInfo,
    q_nSlaves=> nSlaveNumber);

END_PROGRAM

F_IsValveReady

FUNCTION F_IsValveReady : BOOL
VAR_IN_OUT
    fbValve : FB_VGC;
END_VAR
VAR
END_VAR
F_IsValveReady := (
    fbValve.iq_stValve.eState = E_ValvePositionState.OPEN AND
    TRUE (*
        * Placeholder for something else?
        * TODO: Do we check eVGC_State for errors?
        * TODO: Do we care if the laser is used at atmosphere?
    *)
);

END_FUNCTION

FB_DestinationDataStore

FUNCTION_BLOCK FB_DestinationDataStore
VAR_INPUT
    {attribute 'pytmc' := '
        pv: BTPS:Name; io: input
    '}
    sName : STRING;
    (*  Do not pragma valve; Use original PVs to access valve state *)
    fbDestinationValve : REFERENCE TO FB_VGC;

    {attribute 'pytmc' := '
        pv: BTPS:ExitValveReady
        io: input
        field: ZNAM Not ready
        field: ONAM Ready
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bExitValveReady : BOOL;

    {attribute 'pytmc' := '
        pv: BTPS:YieldsControl
        io: output
        field: DESC Destination yields control to others
        field: ZNAM Using beam
        field: ONAM Yielding
        field: ZSV MINOR
        field: OSV NO_ALARM
    '}
    bYieldsControl : BOOL;

    {attribute 'pytmc' := '
        pv: LS
        expand: %s:BTPS
    '}
    stSourceDataStore : ARRAY [1..GVL_BTPS_Constants.nSources] OF ST_SourceDataStore;
END_VAR
VAR_OUTPUT
    //
    bDataValid : BOOL;
END_VAR
VAR
    nIndex : UINT;
    stSource: REFERENCE TO ST_SourceDataStore;
    bInit : BOOL := FALSE;
END_VAR
IF NOT bInit THEN
    bYieldsControl := TRUE;
    bInit := TRUE;
END_IF

bExitValveReady := F_IsValveReady(fbDestinationValve);

Update(
    stDataSource:=stSourceDataStore[1],
    sName:='LS1 (From laser bay 1)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS1_VGC,
    fbLinear:=GVL_BTPS.fbLS1Linear,
    fbRotary:=GVL_BTPS.fbLS1Rotary,
    fbGoniometer:=GVL_BTPS.fbLS1Goniometer,
    fbNearField:=GVL_BTPS.fbLS1NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS1FarFieldCamStats
);

Update(
    stDataSource:=stSourceDataStore[5],
    sName:='LS5 (From laser bay 3)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS5_VGC,
    fbLinear:=GVL_BTPS.fbLS5Linear,
    fbRotary:=GVL_BTPS.fbLS5Rotary,
    fbGoniometer:=GVL_BTPS.fbLS5Goniometer,
    fbNearField:=GVL_BTPS.fbLS5NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS5FarFieldCamStats
);

Update(
    stDataSource:=stSourceDataStore[8],
    sName:='LS8 (From laser bay 4)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS8_VGC,
    fbLinear:=GVL_BTPS.fbLS8Linear,
    fbRotary:=GVL_BTPS.fbLS8Rotary,
    fbGoniometer:=GVL_BTPS.fbLS8Goniometer,
    fbNearField:=GVL_BTPS.fbLS8NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS8FarFieldCamStats
);

Update(
    stDataSource:=stSourceDataStore[3],
    sName:='LS3 (From laser bay 2)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS3_VGC,
    fbLinear:=GVL_BTPS.fbLS3Linear,
    fbRotary:=GVL_BTPS.fbLS3Rotary,
    fbGoniometer:=GVL_BTPS.fbLS3Goniometer,
    fbNearField:=GVL_BTPS.fbLS3NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS3FarFieldCamStats
);

Update(
    stDataSource:=stSourceDataStore[4],
    sName:='LS4 (From laser bay 2)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS4_VGC,
    fbLinear:=GVL_BTPS.fbLS4Linear,
    fbRotary:=GVL_BTPS.fbLS4Rotary,
    fbGoniometer:=GVL_BTPS.fbLS4Goniometer,
    fbNearField:=GVL_BTPS.fbLS4NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS4FarFieldCamStats
);

Update(
    stDataSource:=stSourceDataStore[6],
    sName:='LS6 (From laser bay 3)',
    fbSourceValve:=GVL_BTS_VAC.fb_LS6_VGC,
    fbLinear:=GVL_BTPS.fbLS6Linear,
    fbRotary:=GVL_BTPS.fbLS6Rotary,
    fbGoniometer:=GVL_BTPS.fbLS6Goniometer,
    fbNearField:=GVL_BTPS.fbLS6NearFieldCamStats,
    fbFarField:=GVL_BTPS.fbLS6FarFieldCamStats
);

END_FUNCTION_BLOCK

METHOD Update : BOOL
VAR_INPUT
    sName : STRING;
END_VAR
VAR_IN_OUT
    // The data source to update
    stDataSource : ST_SourceDataStore;

    // The source information
    fbSourceValve : FB_VGC;
    fbNearField : FB_EpicsStatsMonitor;
    fbFarField : FB_EpicsStatsMonitor;
    fbLinear : FB_EpicsMotorMonitor;
    fbRotary : FB_EpicsMotorMonitor;
    fbGoniometer : FB_EpicsMotorMonitor;

END_VAR
stDataSource.sName := sName;
stDataSource.fbSourceValve REF= fbSourceValve;

stDataSource.bEntryValveReady := F_IsValveReady(fbSourceValve);

(* Update range comparisons with their source values *)
(* Cameras *)
stDataSource.fbNFCentroidX.fValue := fbNearField.fCentroidX;
stDataSource.fbNFCentroidX.bInputValid := fbNearField.bValid AND fbNearField.bIsUpdating;
stDataSource.fbNFCentroidY.fValue := fbNearField.fCentroidY;
stDataSource.fbNFCentroidY.bInputValid := fbNearField.bValid AND fbNearField.bIsUpdating;
stDataSource.fbFFCentroidX.fValue := fbFarField.fCentroidX;
stDataSource.fbFFCentroidX.bInputValid := fbFarField.bValid AND fbFarField.bIsUpdating;
stDataSource.fbFFCentroidY.fValue := fbFarField.fCentroidY;
stDataSource.fbFFCentroidY.bInputValid := fbFarField.bValid AND fbFarField.bIsUpdating;

(* Motors *)
stDataSource.fbLinear.fValue := fbLinear.fPosition;
stDataSource.fbLinear.bInputValid := fbLinear.bValid AND fbLinear.stMSTA.bHomed (* AND fbLinear.stMSTA.bClosedLoop *);
stDataSource.fbRotary.fValue := fbRotary.fPosition;
stDataSource.fbRotary.bInputValid := fbRotary.bValid AND fbRotary.stMSTA.bHomed (* AND fbRotary.stMSTA.bClosedLoop *);
stDataSource.fbGoniometer.fValue := fbGoniometer.fPosition;
stDataSource.fbGoniometer.bInputValid := fbGoniometer.bValid AND fbGoniometer.stMSTA.bHomed (* AND fbGoniometer.stMSTA.bClosedLoop *);

(* Run the range comparisons *)
stDataSource.fbNFCentroidX();
stDataSource.fbNFCentroidY();
stDataSource.fbFFCentroidX();
stDataSource.fbFFCentroidY();
stDataSource.fbLinear();
stDataSource.fbRotary();
stDataSource.fbGoniometer();

(* Data is valid overall if *all* range comparisons say so *)
stDataSource.bDataValid := (
    stDataSource.fbNFCentroidX.bInputValid AND
    stDataSource.fbNFCentroidY.bInputValid AND
    stDataSource.fbFFCentroidX.bInputValid AND
    stDataSource.fbFFCentroidY.bInputValid AND
    stDataSource.fbLinear.bInputValid AND
    stDataSource.fbRotary.bInputValid AND
    stDataSource.fbGoniometer.bInputValid AND
    TRUE
);

(* Checks only pass if data is valid and all comparisons are true *)
stDataSource.bChecksOK := (
    stDataSource.bDataValid AND
    stDataSource.fbNFCentroidX.bInRange AND
    stDataSource.fbNFCentroidY.bInRange AND
    stDataSource.fbFFCentroidX.bInRange AND
    stDataSource.fbFFCentroidY.bInRange AND
    stDataSource.fbLinear.bInRange AND
    stDataSource.fbRotary.bInRange AND
    stDataSource.fbGoniometer.bInRange AND
    stDataSource.bEntryValveReady AND
    bExitValveReady AND
    TRUE
);

stDataSource.bInPosition := stDataSource.fbLinear.bInputValid AND stDataSource.fbLinear.bInRange;
END_METHOD
Related:

FB_EpicsMotorMonitor

(*
    EPICS Motor Record Monitoring tool

    Requires a "link" pragma to specify the motor record prefix.

*)
FUNCTION_BLOCK FB_EpicsMotorMonitor
VAR_INPUT
    bEnable : BOOL := TRUE;
END_VAR
VAR_OUTPUT
    bIsMoving : BOOL;
    fPosition : LREAL;
    nMSTA_Raw : UINT;
    stMSTA : ST_EpicsMotorMSTA;
    bValid : BOOL;
END_VAR
VAR
    {attribute 'pytmc' := '
        pv: RBV_
        link: .RBV
    '}
    fbRBVCheck : FB_LREALFromEPICS;

    {attribute 'pytmc' := '
        pv: Dmov_
        link: .DMOV
    '}
    fbMovingCheck : FB_LREALFromEPICS;

    {attribute 'pytmc' := '
        pv: Msta_
        link: .MSTA
    '}
    fbMotorStatusCheck : FB_LREALFromEPICS;
END_VAR
IF NOT bEnable THEN
    bValid := FALSE;
    RETURN;
END_IF

fbRBVCheck();
fbMovingCheck();
fbMotorStatusCheck();
bValid := (
    fbRBVCheck.bValid AND
    fbMovingCheck.bValid AND
    fbMotorStatusCheck.bValid
);

(* Moving status is DMOV; this comes in as a floating point value
     DMOV = 0 -> moving
     DMOV = 1 -> done moving, or not moving
*)
bIsMoving := ABS(fbMovingCheck.fValue) < 1e-5;
fPosition := fbRBVCheck.fValue;

nMSTA_Raw := LREAL_TO_UINT(fbMotorStatusCheck.fValue);
stMSTA.bPositiveDirection := nMSTA_Raw.0;
stMSTA.bDone := nMSTA_Raw.1;
stMSTA.bPlusLimitSwitch := nMSTA_Raw.2;
stMSTA.bHomeLimitSwitch := nMSTA_Raw.3;
stMSTA.bUnused0 := nMSTA_Raw.4;
stMSTA.bClosedLoop := nMSTA_Raw.5;
stMSTA.bSlipStall := nMSTA_Raw.6;
stMSTA.bHome := nMSTA_Raw.7;
stMSTA.bEncoderPresent := nMSTA_Raw.8;
stMSTA.bHardwareProblem := nMSTA_Raw.9;
stMSTA.bMoving := nMSTA_Raw.10;
stMSTA.bGainSupport := nMSTA_Raw.11;
stMSTA.bCommError := nMSTA_Raw.12;
stMSTA.bMinusLimitSwitch := nMSTA_Raw.13;
stMSTA.bHomed := nMSTA_Raw.14;

END_FUNCTION_BLOCK
Related:

FB_EpicsStatsMonitor

(*
    EPICS AreaDetector Stats Plugin Monitor

    Requires a "link" pragma to specify the full plugin prefix.

*)
FUNCTION_BLOCK FB_EpicsStatsMonitor
VAR_INPUT
    fMaximumFrameTime : LREAL := 0.2;
    (* Minimum change in pixels to be considered a new frame, using pixel sum (total) as a metric *)
    fMinPixelSumChange : LREAL := 1E-6;
    bEnable : BOOL := TRUE;
END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: IsUpdating
        io: input
    '}
    bIsUpdating : BOOL;
    {attribute 'pytmc' := '
        pv: CentroidX
        io: input
    '}
    fCentroidX : LREAL;
    {attribute 'pytmc' := '
        pv: CentroidY
        io: input
    '}
    fCentroidY : LREAL;
    {attribute 'pytmc' := '
        pv: Total
        io: input
        field: DESC Sum of all elements in the image
    '}
    fTotal : LREAL;
    {attribute 'pytmc' := '
        pv: ArrayCount
        io: input
    '}
    nArrayCount : UDINT;
    bValid : BOOL;
    {attribute 'pytmc' := '
        pv: FrameTime
        io: input
        field: DESC Avg time between frame updates
        field: EGU sec
    '}
    fFrameTime : LREAL;
END_VAR
VAR
    (* The previous frame times, used for averaging *)
    fFrameTimes : ARRAY [1..nFrameArraySize] OF LREAL;
    bFrameTimeValid : ARRAY [1..nFrameArraySize] OF BOOL;
    nFrameIndex : UINT;

    {attribute 'pytmc' := '
        pv: CX_
        link: CentroidX_RBV
    '}
    fbCentroidX : FB_LREALFromEPICS;

    {attribute 'pytmc' := '
        pv: CY_
        link: CentroidY_RBV
    '}
    fbCentroidY : FB_LREALFromEPICS;

    {attribute 'pytmc' := '
        pv: Total_
        link: Total_RBV
    '}
    fbTotal : FB_LREALFromEPICS;

    fLastCentroidX : LREAL;
    fLastCentroidY : LREAL;

    {attribute 'pytmc' := '
        pv: Cnt_
        link: ArrayCounter_RBV
    '}
    fbArrayCounter : FB_LREALFromEPICS;

    // Last array count value
    nLastArrayCount : UDINT;
    fLastTotal : LREAL;
    fInternalMaximumFrameTime : LREAL;
    nIdx : UINT;

    // Time of the last frame update
    tLastUpdate: TIME;
    tSinceLastFrame : TIME;
    tMaximumFrameTime : TIME;

    tonLastGoodFrame : TON;

    bInit : BOOL;
    // Did we see a frame update yet? (FALSE at startup; TRUE after first frame.)
    bSawFrame : BOOL;
    // Do we have a new frame? Has the array count updated, and position change above fNewFrameMinimumChange?
    {attribute 'pytmc' := 'pv: HaveNewFrame; io: i'}
    bHaveNewFrame : BOOL;
    // For this new frame, is it above the threshold fNewFrameMinimumChange?
    {attribute 'pytmc' := 'pv: AboveThreshold; io: i'}
    bAboveThreshold : BOOL;

    tCheckFrame : TON;

END_VAR
VAR CONSTANT
    nFrameArraySize : UINT := 10;
    fInvalidFrameTime : LREAL := 0.0;
END_VAR
IF NOT bEnable THEN
    bValid := FALSE;
    RETURN;
END_IF

IF NOT bInit THEN
    bInit := TRUE;
    fLastTotal := 0;
    bSawFrame := FALSE;
    tLastUpdate := TIME();
END_IF

fbCentroidX();
fbCentroidY();
fbTotal();
fbArrayCounter();

bValid := (
    fbTotal.bValid AND
    fbCentroidX.bValid AND
    fbCentroidY.bValid AND
    fbArrayCounter.bValid
);

fCentroidX := fbCentroidX.fValue;
fCentroidY := fbCentroidY.fValue;
fTotal := fbTotal.fValue;
nArrayCount := LREAL_TO_UDINT(fbArrayCounter.fValue);

tonLastGoodFrame.PT := T#60S;
tonLastGoodFrame(IN:=TRUE);

IF ABS(fTotal - fLastTotal) >= fMinPixelSumChange AND fMinPixelSumChange > 0.0 THEN

    fFrameTime := TIME_TO_LREAL(tonLastGoodFrame.ET) * 0.001; // ms to seconds

    tonLastGoodFrame(IN:=FALSE);
    tonLastGoodFrame(IN:=TRUE);
    bSawFrame := TRUE;

    fLastTotal := fTotal;
    fLastCentroidX := fCentroidX;
    fLastCentroidY := fCentroidY;
    nLastArrayCount := nArrayCount;

ELSIF TIME_TO_LREAL(tonLastGoodFrame.ET) * 0.001 > fMaximumFrameTime THEN
    fFrameTime := TIME_TO_LREAL(tonLastGoodFrame.ET) * 0.001; // ms to seconds
END_IF

bIsUpdating := bSawFrame AND (fFrameTime < fMaximumFrameTime);

END_FUNCTION_BLOCK

FB_RangeComparison

FUNCTION_BLOCK FB_RangeComparison
VAR_INPUT PERSISTENT
    (* The low part of the range. *)
    {attribute 'pytmc' := 'pv: Low'}
    fLow : LREAL;
    (* The high part of the range. *)
    {attribute 'pytmc' := 'pv: High'}
    fHigh : LREAL;
    (* Nominal value for this.  User-facing value. *)
    {attribute 'pytmc' := '
        pv: Nominal
        io: io
        field: DESC Nominal value for setting
    '}
    fNominal : LREAL;
    (* Are the limits inclusive? *)
    {attribute 'pytmc' := '
        pv: Inclusive
        field: ZNAM Exclusive
        field: ONAM Inclusive
    '}
    bInclusive : BOOL := TRUE;
END_VAR
VAR_INPUT
    (* The input value for comparison *)
    {attribute 'pytmc' := 'pv: Value; io: input'}
    fValue : LREAL;
    (* Should the input value be trusted? *)
    {attribute 'pytmc' := '
        pv: Valid
        io: input
        field: ZNAM Data Invalid
        field: ONAM Data Valid
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bInputValid : BOOL := FALSE;

END_VAR
VAR_OUTPUT
    {attribute 'pytmc' := '
        pv: InRange
        io: input
        field: ZNAM Out of range
        field: ONAM In range
        field: ZSV MAJOR
        field: OSV NO_ALARM
    '}
    bInRange : BOOL;
END_VAR
IF NOT bInputValid OR fHigh < fLow THEN
    bInRange := FALSE;
ELSIF bInclusive THEN
    bInRange := (fValue >= fLow) AND (fValue <= fHigh);
ELSE
    bInRange := (fValue > fLow) AND (fValue < fHigh);
END_IF

END_FUNCTION_BLOCK

FB_ShutterSafety

FUNCTION_BLOCK FB_ShutterSafety
VAR_INPUT

    {attribute 'pytmc' := '
        pv: LSSPermission
        io: i
        field: ZNAM FALSE
        field: ONAM TRUE
    '}
    bLSSPermission : BOOL;

    {attribute 'pytmc' := '
        pv: Acknowledge
        io: io
        field: ZNAM FALSE
        field: ONAM TRUE
    '}
    bAcknowledge : BOOL;

    {attribute 'pytmc' := '
        pv: UserOpen
        io: io
        field: ZNAM Close
        field: ONAM Open
    '}
    bUserOpenRequest : BOOL;

    {attribute 'pytmc' := '
        pv: Override
        io: io
        field: ZNAM Normal mode
        field: ONAM Override mode
    '}
    bOverride : BOOL;

    nSourceIndex : UINT;
    Shutter_Enable : BOOL;

END_VAR
VAR_IN_OUT

    (* VAR_IN_OUT -> reference to fbDestinations *)
    fbDestinations : ARRAY [1..GVL_BTPS_Constants.nDestinations] OF FB_DestinationDataStore;

END_VAR
VAR_OUTPUT

    {attribute 'pytmc' := '
        pv: Safe
        io: input
        field: ZNAM Unsafe
        field: ONAM Safe
    '}
    bSafeToOpen : BOOL;

    {attribute 'pytmc' := '
        pv: Error
        io: input
        field: ZNAM No error
        field: ONAM Error
        field: ZSV NO_ALARM
        field: OSV MAJOR
    '}
    bLatchedError : BOOL;

    {attribute 'pytmc' := '
        pv: LSS:OpenRequest
        io: input
        field: ZNAM Request close
        field: ONAM Request open
    '}
    bOpenSignal : BOOL;

    {attribute 'pytmc' := '
        pv: CurrentLD
        io: input
        field: DESC Current laser destination (LD)
        field: LOW 0
        field: HIGH 15
        field: LSV MAJOR
        field: HSV MAJOR
    '}
    nCurrentLD : INT;

    (* Rising trigger indicator of a new error condition. *)
    rtError : R_TRIG;
    (* Rising trigger indicator of user requesting override. *)
    rtOverrideSet : R_TRIG;
    (* Falling trigger for a 'close shutter' request. *)
    ftCloseRequest : F_TRIG;

END_VAR
VAR

    nDestIndex : UINT;
    bBtpsPermission : BOOL;

END_VAR
bLatchedError R= bAcknowledge;
bAcknowledge := FALSE;

// Figure out the current destination below
nCurrentLD := 0;

IF nSourceIndex >= 1 AND nSourceIndex <= GVL_BTPS_Constants.nSources THEN
    bBtpsPermission := FALSE;
    FOR nDestIndex := 1 TO GVL_BTPS_Constants.nDestinations DO
        bBtpsPermission := (
            bBtpsPermission OR
            fbDestinations[nDestIndex].stSourceDataStore[nSourceIndex].bChecksOK
        );
        IF fbDestinations[nDestIndex].stSourceDataStore[nSourceIndex].bInPosition THEN
            IF nCurrentLD = 0 THEN
                (* First "valid" destination we've seen *)
                nCurrentLD := UINT_TO_INT(nDestIndex);
            ELSE
                (* Multiple "valid" destinations? This means none are valid *)
                nCurrentLD := -1;
            END_IF
        END_IF
    END_FOR
ELSE
    bBtpsPermission := FALSE;
    bLatchedError := TRUE;
END_IF

(* Latch an error if we have no permission. *)
bLatchedError S= NOT bBtpsPermission;
rtError(CLK:=bLatchedError);

(* Reset the user open request on a new error (if not in override mode) *)
bUserOpenRequest R= rtError.Q AND NOT bOverride;

(* If the user requesets a shutter close, clear override mode. *)
ftCloseRequest(CLK:=bUserOpenRequest);
bOverride R= ftCloseRequest.Q;

(* Don't open immediately when in override mode.  Make the user request it. *)
rtOverrideSet(CLK:=bOverride);
bUserOpenRequest R= rtOverrideSet.Q AND NOT bBtpsPermission;

(* LSS permission goes low -> stop override and reset user open

NOTE: We do not have these connected yet, so we can't test this.
TODO: connect bLssPermission
TODO: Double-check logic
    bOverride R= NOT bLssPermission;
    bUserOpenRequest R= NOT bLssPermission;
*)

(* Lose permission on a latched error. *)
bBtpsPermission R= bLatchedError;

(* It's safe to open IF in override mode (user says so!) or we determined it. *)
bSafeToOpen := bOverride OR bBtpsPermission;

(* Only open the shutter if requested and it's safe. *)
bOpenSignal := bSafeToOpen AND bUserOpenRequest;

END_FUNCTION_BLOCK
Related:

FB_Test_EpicsCentroidMonitor

{attribute 'call_after_init'}
FUNCTION_BLOCK FB_Test_EpicsCentroidMonitor EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
TestBasics();

END_FUNCTION_BLOCK

METHOD TestBasics
VAR_INPUT
END_VAR
VAR_INST
    fbMonitor : FB_EpicsStatsMonitor;
    nCount : INT := 0;
END_VAR
TEST('Updating_OK');

fbMonitor.nMaxDroppedFrames := 2;
fbMonitor.fMaximumFrameTime := 10.0;
fbMonitor.fMinPixelSumChange := 0.5;

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#0S, fX:=0.0, fY:=0.0, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#1S, fX:=0.1, fY:=0.1, fTotal:=1.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#2S, fX:=0.1, fY:=0.1, fTotal:=2.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

AssertTrue(fbMonitor.bIsUpdating, Message:='Centroid not reporting updating');
AssertEquals_LREAL(fbMonitor.fFrameTime, 1.5, 1E-6, Message:='Update time inaccurate');
AssertEquals_LREAL(fbMonitor.fFrameTimes[1], 1.0, 1E-6, Message:='Update time inaccurate');
AssertEquals_LREAL(fbMonitor.fFrameTimes[2], 2.0, 1E-6, Message:='Update time inaccurate');

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#2S, fX:=0.1, fY:=0.1, fTotal:=3.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

AssertEquals_LREAL(fbMonitor.fFrameTimes[1], 2.0, 1E-6, Message:='Update time inaccurate');
AssertEquals_LREAL(fbMonitor.fFrameTimes[2], 2.0, 1E-6, Message:='Update time inaccurate');

TEST_FINISHED();

(*
TEST('Updating_Not_OK');

fbMonitor.Initialize();

fbMonitor.nMaxDroppedFrames := 2;
fbMonitor.fMaximumFrameTime := 10.0;
fbMonitor.fMinPixelSumChange := 0.5;

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#0S, fX:=0.0, fY:=0.0, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#11S, fX:=0.1, fY:=0.1, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

WriteCentroidValue(fbMonitor:=fbMonitor, tLastUpdate:=T#11S, fX:=0.1, fY:=0.1, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0);
fbMonitor();

AssertFalse(fbMonitor.bIsUpdating, Message:='Centroid not updating');
AssertEquals_LREAL(fbMonitor.fFrameTime, fbMonitor.fMaximumFrameTime, 1E-6, Message:='Update time inaccurate');
AssertEquals_LREAL(fbMonitor.fFrameTimes[1], fbMonitor.fMaximumFrameTime, 1E-6, Message:='Update time inaccurate');
AssertEquals_LREAL(fbMonitor.fFrameTimes[2], fbMonitor.fMaximumFrameTime, 1E-6, Message:='Update time inaccurate');

TEST_FINISHED();

// TODO: how to make that timer trigger?
*)

TEST('Severity invalidation');

WriteCentroidValue(fbMonitor:=fbMonitor, fX:=0.0, fY:=0.0, fTotal:=0.0, nCount:=nCount, nX_Severity:=2, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0, tLastUpdate:=T#0.2S);
fbMonitor(fMinPixelSumChange:=0.0);
AssertFalse(fbMonitor.bValid, Message:='Invalid data - x severity');

WriteCentroidValue(fbMonitor:=fbMonitor, fX:=0.1, fY:=0.1, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=2, nCount_Severity:=0, nTotal_Severity:=0, tLastUpdate:=T#0.2S);
fbMonitor(fMinPixelSumChange:=0.0);
AssertFalse(fbMonitor.bValid, Message:='Invalid data - y severity');

WriteCentroidValue(fbMonitor:=fbMonitor, fX:=0.2, fY:=0.2, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=2, nTotal_Severity:=0, tLastUpdate:=T#0.2S);
fbMonitor(fMinPixelSumChange:=0.0);
AssertFalse(fbMonitor.bValid, Message:='Invalid data - count severity');

WriteCentroidValue(fbMonitor:=fbMonitor, fX:=0.2, fY:=0.2, fTotal:=0.0, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=2, tLastUpdate:=T#0.2S);
fbMonitor(fMinPixelSumChange:=0.0);
AssertFalse(fbMonitor.bValid, Message:='Invalid data - total severity');

WriteCentroidValue(fbMonitor:=fbMonitor, fX:=0.0, fY:=0.0, fTotal:=fbMonitor.fMinPixelSumChange, nCount:=nCount, nX_Severity:=0, nY_Severity:=0, nCount_Severity:=0, nTotal_Severity:=0, tLastUpdate:=T#0.1S);
fbMonitor(fMinPixelSumChange:=0.0);
AssertTrue(fbMonitor.bValid, Message:='Data valid');
AssertTrue(fbMonitor.bIsUpdating, Message:='Data valid and updating');

TEST_FINISHED();
END_METHOD

METHOD WriteCentroidValue
VAR_IN_OUT
    fbMonitor : FB_EpicsStatsMonitor;
END_VAR
VAR_INPUT
    fX : LREAL;
    fY : LREAL;
    fTotal : LREAL;
    nCount : INT;
    nX_Severity : INT := 0;
    nY_Severity : INT := 0;
    nCount_Severity : INT := 0;
    nTotal_Severity : INT := 0;
    tLastUpdate : TIME;
END_VAR
WRITE_PROTECTED_INT(ADR(fbMonitor.fbCentroidX.iPLCInternalSeverity), nX_Severity);
WRITE_PROTECTED_INT(ADR(fbMonitor.fbCentroidY.iPLCInternalSeverity), nY_Severity);
WRITE_PROTECTED_INT(ADR(fbMonitor.fbArrayCounter.iPLCInternalSeverity), nCount_Severity);
WRITE_PROTECTED_INT(ADR(fbMonitor.fbTotal.iPLCInternalSeverity), nTotal_Severity);

WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbCentroidX.fPLCInternalValue), fX);
WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbCentroidY.fPLCInternalValue), fY);
WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbTotal.fPLCInternalValue), fTotal);
WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbArrayCounter.fPLCInternalValue), INT_TO_LREAL(nCount));

WRITE_PROTECTED_TIME(ADR(fbMonitor.tLastUpdate), TIME() - tLastUpdate);
END_METHOD
Related:

FB_Test_EpicsMotorMonitor

{attribute 'call_after_init'}
FUNCTION_BLOCK FB_Test_EpicsMotorMonitor EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
TestBasics();

END_FUNCTION_BLOCK

METHOD TestBasics
VAR_INPUT
END_VAR
VAR_INST
    fbMonitor : FB_EpicsMotorMonitor;
END_VAR
TEST('Basic');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertTrue(fbMonitor.bValid, Message:='Data valid');

TEST_FINISHED();


TEST('Severities');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=0, nRBV_Severity:=2, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertFalse(fbMonitor.bValid, Message:='Invalid RBV');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=2, nMoving_Severity:=0);
fbMonitor();
AssertFalse(fbMonitor.bValid, Message:='Invalid status');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=2);
fbMonitor();
AssertFalse(fbMonitor.bValid, Message:='Invalid moving');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertTrue(fbMonitor.bValid, Message:='All valid');

TEST_FINISHED();

TEST('Moving');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0 (* DMOV *), nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertTrue(fbMonitor.bIsMoving, Message:='Moving');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=1 (* DMOV *), nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertFalse(fbMonitor.bIsMoving, Message:='Not moving');

TEST_FINISHED();

TEST('Position');

WriteData(fbMonitor:=fbMonitor, fRBV:=10.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertEquals_LREAL(Actual:=fbMonitor.fPosition, Expected:=10.0, Delta:=0.1, Message:='Position 1 OK');

WriteData(fbMonitor:=fbMonitor, fRBV:=20.0, nMoving:=0, nStatus:=0, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();
AssertEquals_LREAL(Actual:=fbMonitor.fPosition, Expected:=20.0, Delta:=0.1, Message:='Position 2 OK');

TEST_FINISHED();

TEST('MSTA set');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=16#0FFFF, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();

AssertTrue(fbMonitor.stMSTA.bPositiveDirection, Message:='bPositiveDirection True');
AssertTrue(fbMonitor.stMSTA.bDone, Message:='bDone True');
AssertTrue(fbMonitor.stMSTA.bPlusLimitSwitch, Message:='bPlusLimitSwitch True');
AssertTrue(fbMonitor.stMSTA.bHomeLimitSwitch, Message:='bHomeLimitSwitch True');
AssertTrue(fbMonitor.stMSTA.bUnused0, Message:='bUnused0 True');
AssertTrue(fbMonitor.stMSTA.bClosedLoop, Message:='bClosedLoop True');
AssertTrue(fbMonitor.stMSTA.bSlipStall, Message:='bSlipStall True');
AssertTrue(fbMonitor.stMSTA.bHome, Message:='bHome True');
AssertTrue(fbMonitor.stMSTA.bEncoderPresent, Message:='bEncoderPresent True');
AssertTrue(fbMonitor.stMSTA.bHardwareProblem, Message:='bHardwareProblem True');
AssertTrue(fbMonitor.stMSTA.bMoving, Message:='bMoving True');
AssertTrue(fbMonitor.stMSTA.bGainSupport, Message:='bGainSupport True');
AssertTrue(fbMonitor.stMSTA.bCommError, Message:='bCommError True');
AssertTrue(fbMonitor.stMSTA.bMinusLimitSwitch, Message:='bMinusLimitSwitch True');
AssertTrue(fbMonitor.stMSTA.bHomed, Message:='bHomed True');

TEST_FINISHED();


TEST('MSTA zero');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=16#0000, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();

AssertFalse(fbMonitor.stMSTA.bPositiveDirection, Message:='bPositiveDirection False');
AssertFalse(fbMonitor.stMSTA.bDone, Message:='bDone False');
AssertFalse(fbMonitor.stMSTA.bPlusLimitSwitch, Message:='bPlusLimitSwitch False');
AssertFalse(fbMonitor.stMSTA.bHomeLimitSwitch, Message:='bHomeLimitSwitch False');
AssertFalse(fbMonitor.stMSTA.bUnused0, Message:='bUnused0 False');
AssertFalse(fbMonitor.stMSTA.bClosedLoop, Message:='bClosedLoop False');
AssertFalse(fbMonitor.stMSTA.bSlipStall, Message:='bSlipStall False');
AssertFalse(fbMonitor.stMSTA.bHome, Message:='bHome False');
AssertFalse(fbMonitor.stMSTA.bEncoderPresent, Message:='bEncoderPresent False');
AssertFalse(fbMonitor.stMSTA.bHardwareProblem, Message:='bHardwareProblem False');
AssertFalse(fbMonitor.stMSTA.bMoving, Message:='bMoving False');
AssertFalse(fbMonitor.stMSTA.bGainSupport, Message:='bGainSupport False');
AssertFalse(fbMonitor.stMSTA.bCommError, Message:='bCommError False');
AssertFalse(fbMonitor.stMSTA.bMinusLimitSwitch, Message:='bMinusLimitSwitch False');
AssertFalse(fbMonitor.stMSTA.bHomed, Message:='bHomed False');

TEST_FINISHED();


TEST('MSTA something');

WriteData(fbMonitor:=fbMonitor, fRBV:=0.0, nMoving:=0, nStatus:=2#0100_0011_0000_0110, nRBV_Severity:=0, nStatus_Severity:=0, nMoving_Severity:=0);
fbMonitor();

AssertFalse(fbMonitor.stMSTA.bPositiveDirection, Message:='bPositiveDirection');
AssertTrue(fbMonitor.stMSTA.bDone, Message:='bDone');
AssertTrue(fbMonitor.stMSTA.bPlusLimitSwitch, Message:='bPlusLimitSwitch');
AssertFalse(fbMonitor.stMSTA.bHomeLimitSwitch, Message:='bHomeLimitSwitch');

AssertFalse(fbMonitor.stMSTA.bUnused0, Message:='bUnused0');
AssertFalse(fbMonitor.stMSTA.bClosedLoop, Message:='bClosedLoop');
AssertFalse(fbMonitor.stMSTA.bSlipStall, Message:='bSlipStall');
AssertFalse(fbMonitor.stMSTA.bHome, Message:='bHome');

AssertTrue(fbMonitor.stMSTA.bEncoderPresent, Message:='bEncoderPresent');
AssertTrue(fbMonitor.stMSTA.bHardwareProblem, Message:='bHardwareProblem');
AssertFalse(fbMonitor.stMSTA.bMoving, Message:='bMoving');
AssertFalse(fbMonitor.stMSTA.bGainSupport, Message:='bGainSupport');

AssertFalse(fbMonitor.stMSTA.bCommError, Message:='bCommError');
AssertFalse(fbMonitor.stMSTA.bMinusLimitSwitch, Message:='bMinusLimitSwitch');
AssertTrue(fbMonitor.stMSTA.bHomed, Message:='bHomed');

TEST_FINISHED();
END_METHOD

METHOD WriteData
VAR_IN_OUT
    fbMonitor : FB_EpicsMotorMonitor;
END_VAR
VAR_INPUT
    fRBV : LREAL;
    nMoving : INT;
    nStatus : UINT;
    nRBV_Severity : INT := 0;
    nStatus_Severity : INT := 0;
    nMoving_Severity : INT := 0;
END_VAR
WRITE_PROTECTED_INT(ADR(fbMonitor.fbRBVCheck.iPLCInternalSeverity), nRBV_Severity);
WRITE_PROTECTED_INT(ADR(fbMonitor.fbMotorStatusCheck.iPLCInternalSeverity), nStatus_Severity);
WRITE_PROTECTED_INT(ADR(fbMonitor.fbMovingCheck.iPLCInternalSeverity), nMoving_Severity);

WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbRBVCheck.fPLCInternalValue), fRBV);
WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbMotorStatusCheck.fPLCInternalValue), UINT_TO_LREAL(nStatus));
WRITE_PROTECTED_LREAL(ADR(fbMonitor.fbMovingCheck.fPLCInternalValue), INT_TO_LREAL(nMoving));
END_METHOD
Related:

FB_TestRangeComparison

{attribute 'call_after_init'}
FUNCTION_BLOCK FB_TestRangeComparison EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
    fbRange : FB_RangeComparison;
END_VAR
TEST('Inclusive valid data');

fbRange.fLow := 0.0;
fbRange.fHigh := 10.0;
fbRange.fNominal := 0.0;
fbRange.bInclusive := TRUE;
fbRange.bInputValid := TRUE;

fbRange(fValue:=0.0);
AssertTrue(fbRange.bInRange, Message:='lower bound');
fbRange(fValue:=10.0);
AssertTrue(fbRange.bInRange, Message:='upper bound');
fbRange(fValue:=5.0);
AssertTrue(fbRange.bInRange, Message:='mid range');
fbRange(fValue:=-1.0);
AssertFalse(fbRange.bInRange, Message:='below range');
fbRange(fValue:=11.0);
AssertFalse(fbRange.bInRange, Message:='above range');

TEST_FINISHED();

TEST('Exclusive valid data');

fbRange.fLow := 0.0;
fbRange.fHigh := 10.0;
fbRange.fNominal := 0.0;
fbRange.bInclusive := FALSE;
fbRange.bInputValid := TRUE;

fbRange(fValue:=0.0);
AssertFalse(fbRange.bInRange, Message:='lower bound');
fbRange(fValue:=10.0);
AssertFalse(fbRange.bInRange, Message:='upper bound');
fbRange(fValue:=5.0);
AssertTrue(fbRange.bInRange, Message:='mid range');
fbRange(fValue:=-1.0);
AssertFalse(fbRange.bInRange, Message:='below range');
fbRange(fValue:=11.0);
AssertFalse(fbRange.bInRange, Message:='above range');

TEST_FINISHED();

TEST('Invalid data');

fbRange.fLow := 0.0;
fbRange.fHigh := 10.0;
fbRange.fNominal := 0.0;
fbRange.bInclusive := TRUE;
fbRange.bInputValid := FALSE;

fbRange(fValue:=0.0);
AssertFalse(fbRange.bInRange, Message:='lower bound');
fbRange(fValue:=10.0);
AssertFalse(fbRange.bInRange, Message:='upper bound');
fbRange(fValue:=5.0);
AssertFalse(fbRange.bInRange, Message:='mid range');
fbRange(fValue:=-1.0);
AssertFalse(fbRange.bInRange, Message:='below range');
fbRange(fValue:=11.0);
AssertFalse(fbRange.bInRange, Message:='above range');

TEST_FINISHED();


TEST('Bad range');

fbRange.fLow := 0.0;
fbRange.fHigh := 0.0;
fbRange.fNominal := 0.0;
fbRange.bInclusive := TRUE;
fbRange.bInputValid := FALSE;

fbRange(fValue:=0.0);
AssertFalse(fbRange.bInRange, Message:='lower bound');
fbRange(fValue:=10.0);
AssertFalse(fbRange.bInRange, Message:='upper bound');
fbRange(fValue:=5.0);
AssertFalse(fbRange.bInRange, Message:='mid range');
fbRange(fValue:=-1.0);
AssertFalse(fbRange.bInRange, Message:='below range');
fbRange(fValue:=11.0);
AssertFalse(fbRange.bInRange, Message:='above range');

TEST_FINISHED();

END_FUNCTION_BLOCK
Related:

FB_TestShutterSafety

FUNCTION_BLOCK FB_TestShutterSafety EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
    fbShutter : FB_ShutterSafety;
    fbDestinations : ARRAY [1..GVL_BTPS_Constants.nDestinations] OF FB_DestinationDataStore;
    nDest : UINT;
    nSource : UINT;
END_VAR
TEST('Bad source index');

Reset();

fbShutter.bAcknowledge := FALSE;
fbShutter.bUserOpenRequest := FALSE;
fbShutter.bOverride := FALSE;
fbShutter.nSourceIndex := 0;

fbShutter(fbDestinations:=fbDestinations);
AssertFalse(fbShutter.bSafeToOpen, Message:='Bad source index');

TEST_FINISHED();

TEST('Safe to open');

Reset();

fbDestinations[1].stSourceDataStore[1].bChecksOK := TRUE;

fbShutter.bAcknowledge := FALSE;
fbShutter.bOverride := FALSE;

fbShutter(nSourceIndex:=1, bUserOpenRequest:=FALSE, fbDestinations:=fbDestinations);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 1');
AssertFalse(fbShutter.bOpenSignal, Message:='Safe for source 1; close');
fbShutter(nSourceIndex:=1, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 1');
AssertTrue(fbShutter.bOpenSignal, Message:='Safe for source 1; open');

fbShutter(nSourceIndex:=2, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations);
AssertFalse(fbShutter.bSafeToOpen, Message:='Not safe for source 2');
AssertFalse(fbShutter.bOpenSignal, Message:='Not safe for source 2; close');

TEST_FINISHED();



TEST('Acknowledge error');

Reset();

fbDestinations[1].stSourceDataStore[1].bChecksOK := FALSE;
fbShutter(nSourceIndex:=1, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations, bAcknowledge:=False);
AssertFalse(fbShutter.bSafeToOpen, Message:='Not safe yet');
AssertFalse(fbShutter.bOpenSignal, Message:='Not safe yet; close');
AssertFalse(fbShutter.bUserOpenRequest, Message:='Reset open request on fail');
AssertTrue(fbShutter.bLatchedError, Message:='Latched error');

fbDestinations[1].stSourceDataStore[1].bChecksOK := TRUE;
fbShutter(nSourceIndex:=1, fbDestinations:=fbDestinations, bAcknowledge:=TRUE);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 2');
AssertFalse(fbShutter.bOpenSignal, Message:='Open not yet requested');
AssertFalse(fbShutter.bLatchedError, Message:='Latched error cleared');

fbShutter(nSourceIndex:=1, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations, bAcknowledge:=FALSE);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 2');
AssertTrue(fbShutter.bOpenSignal, Message:='Safe for source 2; open');
AssertFalse(fbShutter.bLatchedError, Message:='Latched error cleared');

TEST_FINISHED();



TEST('Override error');

Reset();

fbDestinations[1].stSourceDataStore[1].bChecksOK := FALSE;
fbShutter(nSourceIndex:=1, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations, bAcknowledge:=False);
AssertFalse(fbShutter.bSafeToOpen, Message:='Not safe yet');
AssertFalse(fbShutter.bOpenSignal, Message:='Not safe yet; close');
AssertFalse(fbShutter.bUserOpenRequest, Message:='Reset open request on fail');
AssertTrue(fbShutter.bLatchedError, Message:='Latched error');

fbShutter.bOverride := TRUE;

fbShutter(nSourceIndex:=1, fbDestinations:=fbDestinations);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 2');
AssertFalse(fbShutter.bOpenSignal, Message:='Open not yet requested');
AssertTrue(fbShutter.bLatchedError, Message:='Latched error overridden not cleared');

fbShutter(nSourceIndex:=1, bUserOpenRequest:=TRUE, fbDestinations:=fbDestinations);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 2');
AssertTrue(fbShutter.bOpenSignal, Message:='Safe for source 2; open');
AssertFalse(fbShutter.bLatchedError, Message:='Latched error still there');

fbShutter(nSourceIndex:=1, bUserOpenRequest:=FALSE, fbDestinations:=fbDestinations);
AssertTrue(fbShutter.bSafeToOpen, Message:='Safe for source 2');
AssertTrue(fbShutter.bOpenSignal, Message:='Safe for source 2; open');
AssertFalse(fbShutter.bLatchedError, Message:='Latched error still there');
AssertFalse(fbShutter.bOverride, Message:='Override set after shutter closure');

TEST_FINISHED();

END_FUNCTION_BLOCK

ACTION Reset:
fbShutter.bAcknowledge := TRUE;
fbShutter.bUserOpenRequest := FALSE;
fbShutter.bOverride := TRUE;
fbShutter.nSourceIndex := 1;

fbShutter(fbDestinations:=fbDestinations);

fbShutter.bAcknowledge := FALSE;
fbShutter.bUserOpenRequest := FALSE;
fbShutter.bOverride := FALSE;
fbShutter.nSourceIndex := 1;

fbShutter(fbDestinations:=fbDestinations);

FOR nDest := 1 TO GVL_BTPS_Constants.nDestinations DO
    fbDestinations[nDest].stSourceDataStore[nSource].bChecksOK := FALSE;
END_FOR

WRITE_PROTECTED_BOOL(ADR(fbShutter.bLatchedError), FALSE);
WRITE_PROTECTED_BOOL(ADR(fbShutter.bOpenSignal), FALSE);
WRITE_PROTECTED_BOOL(ADR(fbShutter.bSafeToOpen), FALSE);
END_ACTION
Related:

MAIN

PROGRAM MAIN
VAR
    fbLogHandler : FB_LogHandler;
END_VAR
fbLogHandler();

// Run the Diagnostics PRG
DIAGNOSTICS();

// Run the Gauges PRG
PRG_GAUGES();

// Run the Valves PRG
PRG_VALVES();

// Run the Pumps PRG
PRG_PUMPS();

// Run the beam transport protection system code (BTPS)
PRG_BTPS();

// Run Evaluation of Shutter Request
PRG_ShutterRequest();

// Uncomment me to run tests
(* PRG_RunTests(); *)

END_PROGRAM
Related:

PRG_BTPS

PROGRAM PRG_BTPS
VAR

END_VAR
GVL_BTPS.fbLS1Linear(bEnable:=TRUE);
GVL_BTPS.fbLS5Linear(bEnable:=TRUE);
GVL_BTPS.fbLS8Linear(bEnable:=TRUE);
GVL_BTPS.fbLS3Linear(bEnable:=TRUE);
GVL_BTPS.fbLS4Linear(bEnable:=TRUE);
GVL_BTPS.fbLS6Linear(bEnable:=TRUE);

GVL_BTPS.fbLS1Rotary(bEnable:=TRUE);
GVL_BTPS.fbLS5Rotary(bEnable:=TRUE);
GVL_BTPS.fbLS8Rotary(bEnable:=TRUE);
GVL_BTPS.fbLS3Rotary(bEnable:=TRUE);
GVL_BTPS.fbLS4Rotary(bEnable:=TRUE);
GVL_BTPS.fbLS6Rotary(bEnable:=TRUE);

GVL_BTPS.fbLS1Goniometer(bEnable:=TRUE);
GVL_BTPS.fbLS5Goniometer(bEnable:=TRUE);
GVL_BTPS.fbLS8Goniometer(bEnable:=TRUE);
GVL_BTPS.fbLS3Goniometer(bEnable:=TRUE);
GVL_BTPS.fbLS4Goniometer(bEnable:=TRUE);
GVL_BTPS.fbLS6Goniometer(bEnable:=TRUE);

GVL_BTPS.fbLS1NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS5NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS8NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS3NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS4NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS6NearFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);

GVL_BTPS.fbLS1FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS5FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS8FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS3FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS4FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);
GVL_BTPS.fbLS6FarFieldCamStats(bEnable:=TRUE, fMaximumFrameTime:=GVL_BTPS_Retain.fMaximumFrameTime, fMinPixelSumChange:=GVL_BTPS_Retain.fMinPixelChange);


GVL_BTPS.fbDestinations[2](sName:='RIX IP3', fbDestinationValve:=GVL_BTS_VAC.fb_LD2_VGC);
GVL_BTPS.fbDestinations[4](sName:='RIX ChemRIXS', fbDestinationValve:=GVL_BTS_VAC.fb_LD4_VGC);
GVL_BTPS.fbDestinations[6](sName:='RIX qRIXS', fbDestinationValve:=GVL_BTS_VAC.fb_LD6_VGC);
GVL_BTPS.fbDestinations[8](sName:='TMO IP1', fbDestinationValve:=GVL_BTS_VAC.fb_LD8_VGC);
GVL_BTPS.fbDestinations[9](sName:='Laser Lab', fbDestinationValve:=GVL_BTS_VAC.fb_LD9_VGC);
GVL_BTPS.fbDestinations[10](sName:='TMO IP2', fbDestinationValve:=GVL_BTS_VAC.fb_LD10_VGC);
GVL_BTPS.fbDestinations[14](sName:='XPP', fbDestinationValve:=GVL_BTS_VAC.fb_LD14_VGC);

(* Shutter safety evaluation should always come at the end. *)
GVL_BTPS.fbSafetyLS1(nSourceIndex:=1, fbDestinations:=GVL_BTPS.fbDestinations);
GVL_BTPS.fbSafetyLS5(nSourceIndex:=5, fbDestinations:=GVL_BTPS.fbDestinations);
GVL_BTPS.fbSafetyLS8(nSourceIndex:=8, fbDestinations:=GVL_BTPS.fbDestinations);
GVL_BTPS.fbSafetyLS3(nSourceIndex:=3, fbDestinations:=GVL_BTPS.fbDestinations);
GVL_BTPS.fbSafetyLS4(nSourceIndex:=4, fbDestinations:=GVL_BTPS.fbDestinations);
GVL_BTPS.fbSafetyLS6(nSourceIndex:=6, fbDestinations:=GVL_BTPS.fbDestinations);

(* Based on the shutter safety result; send open or close request to shutters *)
IF NOT GVL_BTPS_Retain.bSystemOverride THEN
    GVL_Shutters.fbLS1.bOpenRequest := GVL_BTPS.fbSafetyLS1.bOpenSignal;
    GVL_Shutters.fbLS5.bOpenRequest := GVL_BTPS.fbSafetyLS5.bOpenSignal;
    GVL_Shutters.fbLS8.bOpenRequest := GVL_BTPS.fbSafetyLS8.bOpenSignal;
    GVL_Shutters.fbLS3.bOpenRequest := GVL_BTPS.fbSafetyLS3.bOpenSignal;
    GVL_Shutters.fbLS4.bOpenRequest := GVL_BTPS.fbSafetyLS4.bOpenSignal;
    GVL_Shutters.fbLS6.bOpenRequest := GVL_BTPS.fbSafetyLS6.bOpenSignal;
END_IF

(* Periodically save persistent variables with source/destination store information *)
PRG_SavePeristentVariables();

END_PROGRAM
Related:

PRG_COM

PROGRAM PRG_COM
VAR
END_VAR
//needs to run on a seperate task

(*Instanitate Serial port com function *)
//LXLHN_PTM_01
PTM_fbSerialLineControl(
    Mode:= SERIALLINEMODE_EL6_22B,
    pComIn:= ADR(PTM_stComIn),
    pComOut:=ADR(PTM_stComOut),
    SizeComIn:= SIZEOF(PTM_stComOut),
    TxBuffer:= PTM_SerialTXBuffer,
    RxBuffer:= PTM_SerialRXBuffer,
    Error=> ,
    ErrorID=> );

END_PROGRAM

PRG_GAUGES

PROGRAM PRG_GAUGES
VAR
END_VAR
(* Switchbox Gauges *)
    fb_PP_GPI_1.M_SetBits(30518);
    fb_PP_GPI_1();

    fb_PP_GPI_2.M_SetBits(30518);
    fb_PP_GPI_2();

    fb_DP_GPI.M_SetBits(30518);
    fb_DP_GPI();

    fb_PP_GCC.M_SetBits(30518);
    fb_PP_GCC(PG := GVL_BTS_VAC.fb_PP_GPI_1.PG , IG=> );

    fb_DP_GCC.M_SetBits(30518);
    fb_DP_GCC(PG := GVL_BTS_VAC.fb_PP_GPI_1.PG , IG=> );

(* Transport Tube Gauges *)
// LS1
    fb_LS1_GPI.M_SetBits(30518);
    fb_LS1_GPI();
    fb_LS1_GCC.M_SetBits(30518);
    fb_LS1_GCC(PG := GVL_BTS_VAC.fb_LS1_GPI.PG , IG=> );

// LS5
    fb_LS5_GPI.M_SetBits(30518);
    fb_LS5_GPI();
    fb_LS5_GCC.M_SetBits(30518);
    fb_LS5_GCC(PG := GVL_BTS_VAC.fb_LS5_GPI.PG , IG=> );

// LS8
    fb_LS8_GPI.M_SetBits(30518);
    fb_LS8_GPI();
    fb_LS8_GCC.M_SetBits(30518);
    fb_LS8_GCC(PG := GVL_BTS_VAC.fb_LS8_GPI.PG , IG=> );

// LD2
    fb_LD2_GPI.M_SetBits(30518);
    fb_LD2_GPI();
    fb_LD2_GCC.M_SetBits(30518);
    fb_LD2_GCC(PG := GVL_BTS_VAC.fb_LD2_GPI.PG , IG=> );

// LD4
    fb_LD4_GPI.M_SetBits(30518);
    fb_LD4_GPI();
    fb_LD4_GCC.M_SetBits(30518);
    fb_LD4_GCC(PG := GVL_BTS_VAC.fb_LD4_GPI.PG , IG=> );

// LD6
    fb_LD6_GPI.M_SetBits(30518);
    fb_LD6_GPI();
    fb_LD6_GCC(PG := GVL_BTS_VAC.fb_LD6_GPI.PG , IG=> );

// LD8
    fb_LD8_GPI.M_SetBits(30518);
    fb_LD8_GPI();
    fb_LD8_GCC.M_SetBits(30518);
    fb_LD8_GCC(PG := GVL_BTS_VAC.fb_LD8_GPI.PG , IG=> );

// LD9
    fb_LD9_GPI.M_SetBits(30518);
    fb_LD9_GPI();
    fb_LD9_GCC.M_SetBits(30518);
    fb_LD9_GCC(PG := GVL_BTS_VAC.fb_LD9_GPI.PG , IG=> );

// LD10
    fb_LD10_GPI.M_SetBits(30518);
    fb_LD10_GPI();
    fb_LD10_GCC.M_SetBits(30518);
    fb_LD10_GCC(PG := GVL_BTS_VAC.fb_LD10_GPI.PG , IG=> );

// LD14
    fb_LD14_GPI.M_SetBits(30518);
    fb_LD14_GPI();
    fb_LD14_GCC.M_SetBits(30518);
    fb_LD14_GCC(PG := GVL_BTS_VAC.fb_LD14_GPI.PG , IG=> );

//1um upgrade lines
// LS3
    fb_LS3_GPI.M_SetBits(30518);
    fb_LS3_GPI();
    fb_LS3_GCC.M_SetBits(30518);
    fb_LS3_GCC(PG := GVL_BTS_VAC.fb_LS3_GPI.PG , IG=> );
//LS4
    fb_LS4_GPI.M_SetBits(30518);
    fb_LS4_GPI();
    fb_LS4_GCC.M_SetBits(30518);
    fb_LS4_GCC(PG := GVL_BTS_VAC.fb_LS4_GPI.PG , IG=> );

//LS6
    fb_LS6_GPI.M_SetBits(30518);
    fb_LS6_GPI();
    fb_LS6_GCC(PG := GVL_BTS_VAC.fb_LS6_GPI.PG , IG=> );


(* Auto switch on cold cathode gauges if interlock permits *)

If xGCC_Enable_SW THEN
    fb_PP_GCC.M_HVE(TRUE);
    fb_DP_GCC.M_HVE(TRUE);
    fb_LS1_GCC.M_HVE(TRUE);
    fb_LS5_GCC.M_HVE(TRUE);
    fb_LS8_GCC.M_HVE(TRUE);
    fb_LD2_GCC.M_HVE(TRUE);
    fb_LD4_GCC.M_HVE(TRUE);
    fb_LD6_GCC.M_HVE(TRUE);
    fb_LD8_GCC.M_HVE(TRUE);
    fb_LD9_GCC.M_HVE(TRUE);
    fb_LD10_GCC.M_HVE(TRUE);
    fb_LD14_GCC.M_HVE(TRUE);
    fb_LS3_GCC.M_HVE(TRUE);
    fb_LS4_GCC.M_HVE(TRUE);
    fb_LS6_GCC.M_HVE(TRUE);
    xGCC_Enable_SW := False;
END_IF

END_PROGRAM
Related:

PRG_PUMPS

PROGRAM PRG_PUMPS
VAR
END_VAR
(* Switchbox pumps *)
fb_PP_PMF();

//fb_PP_PTM(i_xExtIlkOK := fb_PP_VVC.M_IsClosed());
//F_TurboExtILKLogic_2(Turbo:=fb_PP_PTM.iq_stPTM,
//BackingGauge:= , InletGauge:=, ScrollPump:=fb_PP_PMF.q_stPump));
//GVL_BTS_VAC.fb_PP_PTM(i_xExtIlkOK := F_PumpStationTurboPumpInterlock());

fb_PP_PTM(i_xExtIlkOK := F_TurboExtILKLogic(Turbo:=fb_PP_PTM.iq_stPTM, BackingGauge:=fb_PP_GPI_2.PG,
                                InletGauge:=fb_PP_GPI_1.PG, VentValve:=fb_PP_VVC.iq_stValve,
                                ScrollPump:=fb_PP_PMF.q_stPump));

fb_PP_PIP(i_stGauge := fb_DP_GCC.IG);

(* Transport Tube pumps *)
// LS1
fb_LS1_PIP(i_stGauge := fb_LS1_GCC.IG, stPump=>);

// LS5
fb_LS5_PIP(i_stGauge := fb_LS5_GCC.IG, stPump=>);

// LS8
fb_LS8_PIP(i_stGauge := fb_LS8_GCC.IG, stPump=>);

// LD2
fb_LD2_PIP(i_stGauge := fb_LD2_GCC.IG, stPump=>);

// LD4
fb_LD4_PIP(i_stGauge := fb_LD4_GCC.IG, stPump=>);

// LD6
fb_LD6_PIP(i_stGauge := fb_LD6_GCC.IG, stPump=>);

// LD8
fb_LD8_PIP(i_stGauge := fb_LD8_GCC.IG, stPump=>);

// LD9
fb_LD9_PIP(i_stGauge := fb_LD9_GCC.IG, stPump=>);

// LD10
fb_LD10_PIP(i_stGauge := fb_LD10_GCC.IG, stPump=>);

// LD14
fb_LD14_PIP(i_stGauge := fb_LD14_GCC.IG, stPump=>);


//LS3
fb_LS3_PIP(i_stGauge := fb_LS3_GCC.IG, stPump=>);
//LS4
fb_LS4_PIP(i_stGauge := fb_LS4_GCC.IG, stPump=>);
//LS6
fb_LS6_PIP(i_stGauge := fb_LS6_GCC.IG, stPump=>);

(*Serial Interface*)
(*Assign adresses to the pfeiffer controller connected to this serial terminal*)
st_Pfeiffer_CTRL_PTM[1].iSerialAddress  :=1;        //LXLHN_PTM_01
st_Pfeiffer_CTRL_PTM[1].xEnableComm         := TRUE;
(* Instanitate the Function block for serial communication with Pfeiffer*)
PTM_COM(astPfeifferControl := st_Pfeiffer_CTRL_PTM,
                astPfeifferStatus:= st_Pfeiffer_RBK_PTM,
                SerialRXBuffer:= PTM_SerialRXBuffer,
                SerialTXBuffer:= PTM_SerialTXBuffer);
(* Copy Status Pv's into the PTM structure*)
fb_PP_PTM.M_Serial_IO(st_Pfeiffer_CTRL:=st_Pfeiffer_CTRL_PTM[1],st_Pfeiffer_RBK:=st_Pfeiffer_RBK_PTM[1]);

END_PROGRAM
Related:

PRG_RunTests

PROGRAM PRG_RunTests
VAR
    fbRangeComparison : FB_TestRangeComparison;
    fbShutterSafety : FB_TestShutterSafety;
    fbCentroidTest : FB_Test_EpicsCentroidMonitor;
    fbMotorTest : FB_Test_EpicsMotorMonitor;

END_VAR
TcUnit.RUN();

END_PROGRAM
Related:

PRG_SavePeristentVariables

PROGRAM PRG_SavePeristentVariables
VAR
    fbWritePersistentData: FB_WritePersistentData;
    tonStart : TON;
    tonInterval : TON;
END_VAR
tonInterval(
    IN:=TRUE,
    PT:=T#1ms (* Write 1ms after startup of the PLC *)

);

tonStart(
    IN:=tonStart.Q AND NOT fbWritePersistentData.BUSY,
    PT:=T#1m (* Write every minute *)
);

fbWritePersistentData(
    NETID:='', (* This PLC *)
    PORT:=851, (* Application port! *)
    TMOUT:=T#10s,  (* Maximum time before considering this a failure *)
    START:=tonStart.Q OR tonInterval.Q (* Start with tonStart goes high or periodically *)
);

END_PROGRAM

PRG_ShutterRequest

PROGRAM PRG_ShutterRequest
VAR
END_VAR
/////Shutter Request Program evaluates the LSS Permission state from the DI and acts on the Open Request state
//// 1um Lines
//LS3
IF (NOT GVL_Shutters.fbLS3.bLssStatus) THEN
    GVL_Shutters.fbLS3.bOpenRequest := FALSE;
END_IF

//LS6
IF (NOT GVL_Shutters.fbLS6.bLssStatus) THEN
    GVL_Shutters.fbLS6.bOpenRequest := FALSE;
END_IF

/////800 nm
//LS1
IF (NOT GVL_Shutters.fbLS1.bLssStatus) THEN
    GVL_Shutters.fbLS1.bOpenRequest := FALSE;
END_IF
//LS4
IF (NOT GVL_Shutters.fbLS4.bLssStatus) THEN
    GVL_Shutters.fbLS4.bOpenRequest := FALSE;
END_IF
//LS5
IF (NOT GVL_Shutters.fbLS5.bLssStatus) THEN
    GVL_Shutters.fbLS5.bOpenRequest := FALSE;
END_IF
//LS8
IF (NOT GVL_Shutters.fbLS8.bLssStatus) THEN
    GVL_Shutters.fbLS8.bOpenRequest := FALSE;
END_IF

END_PROGRAM
Related:

PRG_VALVES

PROGRAM PRG_VALVES
VAR
    Valve_opn_ok : BOOL;
END_VAR
(* Switchbox valves *)
fb_PP_VGC(
    i_stUSG := fb_PP_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

fb_PP_VVC(
    i_xExtILK_OK := fb_PP_VGC.M_IsClosed(),
    i_xOverrideMode := xSystemOverrideMode_BTS
);

GVL_BTS_VAC.fb_PP_VRC_1(
    i_xExtILK_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS
);

GVL_BTS_VAC.fb_PP_VRC_2(
    i_xExtILK_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS
);

fb_VP_VVC_1(
    i_xExtILK_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS
);

fb_VP_VVC_2(
    i_xExtILK_OK := TRUE
);

// Vent manifold valve - interlock logic with slow or fast vent
IF (fb_PP_PMF.q_stPump.eState <> E_PumpState.pumpSTOPPED)  THEN  // Condition to evaluate if Roughing Pump is ON
    fb_VP_VVC_4.i_xExtILK_OK := FALSE;
    fb_VP_VVC_2.i_xExtILK_OK := FALSE;
    fb_VP_VVC_1.i_xExtILK_OK := FALSE;
ELSE
    fb_VP_VVC_4.i_xExtILK_OK := TRUE;
    fb_VP_VVC_2.i_xExtILK_OK := TRUE;
    fb_VP_VVC_1.i_xExtILK_OK := TRUE;
        IF (fb_VP_VVC_2.iq_stValve.q_xOPN_DO OR fb_VP_VVC_1.iq_stValve.q_xOPN_DO) THEN
            fb_VP_VVC_4.M_Set_OPN_SW(TRUE);
        ELSE
            fb_VP_VVC_4.M_Set_OPN_SW(FALSE);
        END_IF
END_IF

fb_VP_VVC_4(
    i_xOverrideMode := xSystemOverrideMode_BTS
);

// Turbo Isolation valve - Interlocked with turbo pump operation
fb_PP_VRC_4(
    i_xExtILK_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS
);

(* Transport Tube vavles *)
// LS1
fb_LS1_VGC(
    i_stUSG := fb_LS1_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter,
);

// LS5
fb_LS5_VGC(
    i_stUSG := fb_LS5_GPI.PG,
    i_stDSG := fb_DP_GPI.PG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LS8
fb_LS8_VGC(
    i_stUSG := fb_LS8_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD2
fb_LD2_VGC(
    i_stUSG := fb_LD2_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD4
fb_LD4_VGC(
    i_stUSG := fb_LD4_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD6
fb_LD6_VGC(
    i_stUSG := fb_LD6_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD8
fb_LD8_VGC(
    i_stUSG := fb_LD8_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD9
fb_LD9_VGC(
    i_stUSG := fb_LD9_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD10
fb_LD10_VGC(
    i_stUSG := fb_LD10_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LD14
fb_LD14_VGC(
    i_stUSG := fb_LD14_GCC.IG,
    i_stDSG := fb_DP_GCC.IG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);


//// LS3
fb_LS3_VGC(
    i_stUSG := fb_LS3_GPI.PG,
    i_stDSG := fb_DP_GPI.PG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LS4
fb_LS4_VGC(
    i_stUSG := fb_LS4_GPI.PG,
    i_stDSG := fb_DP_GPI.PG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

// LS6
fb_LS6_VGC(
    i_stUSG := fb_LS6_GPI.PG,
    i_stDSG := fb_DP_GPI.PG,
    i_xDis_DPIlk := FALSE,
    i_xEPS_OK := TRUE,
    i_xPMPS_OK := TRUE,
    i_xExt_OK := TRUE,
    i_xOverrideMode := xSystemOverrideMode_BTS,
    io_fbFFHWO := g_FFOut,
    fbArbiter := g_fbArbiter
);

END_PROGRAM
Related: