DUTs ---- E_PressureState ^^^^^^^^^^^^^^^ :: TYPE E_PressureState : ( // Invalid states PressInvalid,// //gc_GaugeValidState - 1, GaugeDisconnected, //gc_GaugeValidState -2, OoR, //gc_GaugeValidState -6, // Ion gauges Off, //gc_GaugeValidState - 3, Starting, //gc_GaugeValidState - 4 //Valid States (Positive) Valid, // gc_GaugeValidState set in "Global Variable Folder: Constants" ValidHi, //gc_GaugeValidState + 1, ValidLo //gc_GaugeValidState + 2, ) INT; END_TYPE E_PumpState ^^^^^^^^^^^ :: TYPE E_PumpState : ( pumpSTOPPED := 0, pumpSTARTING := 1, pumpRUNNING := 2, pumpFAULT := 3, pumpSTOPPING :=4 ); END_TYPE E_ValvePositionState ^^^^^^^^^^^^^^^^^^^^ :: TYPE E_ValvePositionState : ( OPEN := 0, CLOSED := 1, MOVING := 2, INVALID :=3, OPEN_F :=4 ); END_TYPE E_VCN ^^^^^ :: TYPE E_VCN : ( CloseValve := 0, OpenValve := 1, PressureControl := 2, ManualControl := 3 ); END_TYPE E_VGC ^^^^^ :: TYPE E_VGC : ( Vented, AtVacuum, ERR_DiffPress, ERR_LostVac, ERR_ExtFault, At_Vac, Triggered, Vac_Fault, Cls_Timeout, Opn_Timeout ); END_TYPE Gauge_Type ^^^^^^^^^^ :: TYPE Gauge_Type: ( PG722B := 0, //Baraton Gauge IG903 := 1 , //Cold Cathode PG907 := 2 , //Pirani Gauge IG909:= 3 , //Hot Cathode PG925 := 4 ); //MicroPirani Gauge (* This enumeration should match up with the EDM MUX button on the vacuum gauge expert controls screen *) END_TYPE ST_AgilentPTM ^^^^^^^^^^^^^ :: TYPE ST_AgilentPTM EXTENDS ST_PTM : STRUCT (* Inputs *) (* 24V in Ramp state; 0V for others *) {attribute 'pytmc' := ' pv: START; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xSTART : BOOL; (* setpoint related to frequency *) {attribute 'pytmc' := ' pv: R1Status; field: ZNAM FALSE; field: ONAM AT SPEED; io: i; '} i_xR1 : BOOL; (* setpoint related to Power *) {attribute 'pytmc' := ' pv: R2Status; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xR2 : BOOL; (* low speed mode *) {attribute 'pytmc' := ' pv: LSPD; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xLSpd : BOOL; (* Fault input*) //i_xFault : BOOL; Inherit ST_PTM.xFault (* controls *) (* Start/Stop - q_RunDo ; Inherit ST_PTM. *) (* Low speed control *) {attribute 'pytmc' := ' pv: LSPD_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xLSpd : BOOL; (*Soft start *) {attribute 'pytmc' := ' pv: XSS_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xSS : BOOL; (*pump Lock*) {attribute 'pytmc' := ' pv: FaultLock; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} xPumpFaultLock : BOOL := FALSE; // i_xPumpLockReset: BOOL := FALSE; Inherit ST_PTM.xReset // Error messages {attribute 'pytmc' := ' pv: ErrorMessage; io: i; '} sError : STRING; {attribute 'pytmc' := ' pv: Power_MON; io: i; field: PREC 2 field: EGU "W"; '} i_rPowerMon : REAL; END_STRUCT END_TYPE ST_EbaraDryPump ^^^^^^^^^^^^^^^ :: TYPE ST_EbaraDryPump EXTENDS ST_RoughPump : STRUCT (* Extension of the rough pump archetype for ebara Applicable to: EV-S20 EV-S50 EV-S100 EV-S200 *) //Controls {attribute 'pytmc' := ' pv: MPStart; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xMPStart : BOOL; //Main Pump start {attribute 'pytmc' := ' pv: BPStart; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xBPStart : BOOL; // Booster Pump start (this can be started by the pump automatically) xBPIlk : BOOL; //Booster pump interlock rBPIlkSP : REAL := 30; //Booster pump pressure setpoint tonBP : TON := (PT:=T#5S); //Timer for pressure and valve stability //Readbacks {attribute 'pytmc' := ' pv: MPStatus; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xMPStatus : BOOL; //MP status {attribute 'pytmc' := ' pv: BPStatus; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xBPStatus : BOOL; //BP status {attribute 'pytmc' := ' pv: WARN_DI; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xWarning : BOOL; //Warning status {attribute 'pytmc' := ' pv: ALARM; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xAlarm : BOOL; //Alarm, maps to error {attribute 'pytmc' := ' pv: REMOTE; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xRemote : BOOL; //Remote control status END_STRUCT END_TYPE ST_EbaraEVA ^^^^^^^^^^^ :: TYPE ST_EbaraEVA EXTENDS ST_RoughPump: STRUCT (* Output *) //q_xRunDO : BOOL; in Rough PUMP (* Input *) {attribute 'pytmc' := ' pv: REMOTE; field: ZNAM FALSE; field: ONAM TRUE; io: io; '} q_xRemote : BOOL; {attribute 'pytmc' := ' pv: ALARM_OK; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xAlarmOK : BOOL; {attribute 'pytmc' := ' pv: RUN_DI; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xIsRun : BOOL; END_STRUCT END_TYPE ST_EbaraPTM ^^^^^^^^^^^ :: TYPE ST_EbaraPTM EXTENDS ST_PTM : STRUCT (* Extension of the PTM archetype for Ebara turbo controllers Applicable to: ETC series *) //Controls {attribute 'pytmc' := ' pv: START; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xStart : BOOL; //Outputs - FB decides if these are actually set {attribute 'pytmc' := ' pv: STOP; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xStop : BOOL; {attribute 'pytmc' := ' pv: RESET; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xReset : BOOL; // xReset is momentary reset switch - resets protection functions {attribute 'pytmc' := ' pv: PROT; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xProtection : BOOL; //NC. If opened, pump will decelerate/not start {attribute 'pytmc' := ' pv: SETSPEED field: ZNAM FALSE; field: ONAM TRUE; io: io '} iq_xSpeedSet : BOOL; //Request to set speed {attribute 'pytmc' := ' pv: SPEED_REQ io: io field: EGU "Hz" '} q_iSpeedSet : DINT; //Requested speed (min. 100Hz) {attribute 'pytmc' := ' pv: OVRD_ON field: ZNAM OFF; field: ONAM ON; io: io '} i_xOverride : BOOL; //Override mode - ignores interlocks //Readbacks - also has Accel/At speed/Decel/Fault/Cur Speed (min 100RPM) {attribute 'pytmc' := ' pv: ROTATE_STATUS field: ZNAM FALSE; field: ONAM ROTATING; io: i '} i_xRotate : BOOL; //If open, pump is rotating {attribute 'pytmc' := ' pv: DECEL field: ZNAM FALSE; field: ONAM DECELERATING; io: i '} i_xDecel : BOOL; //If closed, brake engaged (pump decelerating) {attribute 'pytmc' := ' pv: FAULT_OK field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xNCFault : BOOL; //Normally closed fault wiring. If false, there is a fault, or cable is unplugged. END_STRUCT END_TYPE ST_KashiyamaDryPump ^^^^^^^^^^^^^^^^^^^ :: TYPE ST_KashiyamaDryPump EXTENDS ST_RoughPump: STRUCT (* Output *) //q_xRunDO : BOOL; in Rough PUMP {attribute 'pytmc' := ' pv: RESET_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i '} q_xResetDo : BOOL; {attribute 'pytmc' := ' pv: LSPD_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i '} q_xLspdDo : BOOL; (* Input *) {attribute 'pytmc' := ' pv: REMOTE; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xLocal : BOOL; {attribute 'pytmc' := ' pv: ALARM_OK; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xAlarmOK : BOOL; {attribute 'pytmc' := ' pv: WARN_OK; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xWarningOK : BOOL; {attribute 'pytmc' := ' pv: RUN_DI; field: ZNAM FALSE; field: ONAM TRUE; io: i '} i_xIsRun : BOOL; (* Logic status *) xLspd_IlkENA: BOOL; (* EPICS *) //xRunSW : BOOL; in Rough PUMP {attribute 'pytmc' := ' pv: RESET_SW; field: ZNAM FALSE; field: ONAM TRUE; io: io '} xResetSW : BOOL; //For resetting faults {attribute 'pytmc' := ' pv: LSPD_SW; field: ZNAM FALSE; field: ONAM TRUE; io: io '} xLspdSW : BOOL; LowSP : REAL := 0.02; //Torr HighSP : REAL; //Torr RdyTmr : REAL; END_STRUCT END_TYPE ST_LeyboldPTM ^^^^^^^^^^^^^ :: TYPE ST_LeyboldPTM EXTENDS ST_PTM : STRUCT (* Extension of the PTM archetype for Oerlikon turbo controllers Applicable to: Mag Drive Digital Mag Drive S More? *) //Readbacks {attribute 'pytmc' := ' pv: DECEL; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xDecel : BOOL; {attribute 'pytmc' := ' pv: TEMP; field: EGU "C"; io: i; '} i_diPumpTemp: DINT; {attribute 'pytmc' := ' pv: PWR; io: i; '} i_diPwr : DINT; i_diElecTemp: DINT; {attribute 'pytmc' := ' pv: OVR_TEMP; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xTempFault: BOOL; i_xNCError : BOOL; //Using normally closed wiring //Controls {attribute 'pytmc' := ' pv: REMOTE; field: ZNAM FALSE; field: ONAM TRUE; io: io; '} q_xRemote : BOOL; (* Remote control enabled *) END_STRUCT END_TYPE ST_PfeifferPTM ^^^^^^^^^^^^^^ :: TYPE ST_PfeifferPTM EXTENDS ST_PTM : STRUCT (* Extension of the PTM archetype for Pfeiffer turbos Applicable to: HiPace series w/ onboard controllers More? *) {attribute 'pytmc' := ' pv: RESET; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} q_xReset : BOOL; //Readbacks {attribute 'pytmc' := ' pv: DrivePower_RBV; io: i; '} i_diPwr : DINT; {attribute 'pytmc' := ' pv: TempElec_RBV; io: i; '} i_diElecTemp : DINT; {attribute 'pytmc' := ' pv: TempPump_RBV; io: i; '} i_diBtmTemp: DINT; {attribute 'pytmc' := ' pv: TempBearing_RBV; io: i; '} i_diBrngTemp: DINT; {attribute 'pytmc' := ' pv: TempMotor_RBV; io: i; '} i_diMtrTemp: DINT; {attribute 'pytmc' := ' pv: ErrorCode_RBV; io: i; '} i_iErrorCode : INT; //might change these to enumeration someday {attribute 'pytmc' := ' pv: WarningCode_RBV; io: i; '} i_iWarningCode : INT; {attribute 'pytmc' := ' pv: TempFault_RBV; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xTempFault : BOOL; {attribute 'pytmc' := ' pv: Power_RBV; io: i; '} i_uiPowerPctRbk : UINT; //Controls q_uiPowerPct : UINT := 100; //Should normally be 100 END_STRUCT END_TYPE ST_PIP ^^^^^^ :: TYPE ST_PIP : STRUCT (* Read back *) // i_xHVisON : BOOL; (* Interlock *) {attribute 'pytmc' := ' pv: ILK_OK; field: ZNAM NOT OK ; field: ONAM OK ; io: i; '} xILKOk : BOOL; {attribute 'pytmc' := ' pv: ERROR; field: ZNAM FALSE ; field: ONAM TRUE ; io: i; '} xError : BOOL; {attribute 'pytmc' := ' pv: AT_VAC_SP; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rHVEna_SP : REAL := 1.0E-4; {attribute 'pytmc' := ' pv: ILK_DEVICE; io: i; '} sIlkDeviceName: STRING; //Required for other devices using this gauge as interlock sPath: STRING; (* EPICS Controls *) {attribute 'pytmc' := ' pv: HV_SW; io: io; field: ZNAM OFF; field: ONAM ON; '} xHVEna_SW : BOOL; {attribute 'pytmc' := ' pv: Auto_On; field: ZNAM FALSE; field: ONAM TRUE; io:i; '} xAutoOn : BOOL; {attribute 'pytmc' := ' pv: OVRD_ON ; field: ZNAM Override OFF ; field: ONAM Override ON; io: io; '} xOverrideMode : BOOL; (* Shows the override status of this valve *) {attribute 'pytmc' := ' pv: FORCE_START; io: io; field: ZNAM FALSE; field: ONAM FORCE START; '} pv_xOvrdStart : BOOL; {attribute 'pytmc' := ' pv: SP_HYS; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} /// Protection setpoint hysteresis rHYS_PR: REAL := 0.001; {attribute 'pytmc' := ' pv: AI_Offset; io: io; '} iOffset: INT:=13; {attribute 'pytmc' := ' pv: Inverted; field: ZNAM NORMAL; field: ONAM INVERTED; io: io; '} bOutputInverted : BOOL; (* IO Controls *) {attribute 'pytmc' := ' pv: HV_DO; field: ZNAM OFF; field: ONAM ON; io: i; '} q_xHVEna_DO : BOOL; // Enable High Voltage when True // 'TcLinkTo' (EL1124) ^Input {attribute 'pytmc' := ' pv: PRESS; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" io: i; '} rPRESS: REAL; // {attribute 'pytmc' := ' pv: PRESS_AI; io: i; '} i_iPRESS: REAL; // {attribute 'pytmc' := ' pv: HV_DI; field: ZNAM FALSE; field: ONAM TRUE; io:i; '} i_xHV_DI : BOOL; // NO contact // 'TcLinkTo' (EL1004) ^Input {attribute 'pytmc' := ' pv: STATE; field: ZRST STOPPED; field: ONST STARTING; field: TWST RUNNING; field: THST FAULT; field: FRST STOPPING; io: i; '} eState : E_PumpState; {attribute 'pytmc' := ' pv: LOGGER; io: io; field: ZNAM OFF ; field: ONAM ON ; '} xLog : BOOL:=TRUE; END_STRUCT END_TYPE ST_PTM ^^^^^^ :: TYPE ST_PTM : STRUCT (* General PTM Structure Each PTM might have a serial control structure, but all will have a general supervisory control structure NOTE: This is an archetype, use an extension of this structure for a specific pump (or make one). *) (* Controls *) {attribute 'pytmc' := ' pv: RUN_SW; field: ZNAM STOP; field: ONAM RUN; io: io; '} xRunSW : BOOL; {attribute 'pytmc' := ' pv: RST_SW; field: ZNAM FALSE; field: ONAM TRUE; io: io; '} xResetSW : BOOL; //For resetting faults {attribute 'pytmc' := ' pv: RUN_DO; io: i; '} q_RunDO : BOOL; (* Readbacks *) {attribute 'pytmc' := ' pv: ILK_OK; field: ZNAM ILK ACTIVE; field: ONAM ILK OK; io: i; '} xExtRunOk : BOOL; (* also a control *) {attribute 'pytmc' := ' pv: ACCEL; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xAccel : BOOL; {attribute 'pytmc' := ' pv: AT_SPD; field: ZNAM FALSE; field: ONAM AT SPEED; io: i; '} i_xAtSpd : BOOL; {attribute 'pytmc' := ' pv: SPEED; io: i; field: EGU "Hz" '} i_diCurSpd : DINT; xCommTimeout : BOOL := TRUE; (* Initialized true since we haven't talked to pump yet *) (* Alarm Outputs *) {attribute 'pytmc' := ' pv: FAULT; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xFault : BOOL; (*FAULT*) {attribute 'pytmc' := ' pv: WARN; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xWarn : BOOL; (* warning *) {attribute 'pytmc' := ' pv: ALARM; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} i_xALARM : BOOL; (* ALARM *) (* IWS instructions *) xActive : BOOL; // this bit is set to add the pump to the system iPumpGrp : INT; // all members of a pump group start at the same time (* Operational Setpoints *) rForelineSP : REAL := 0.5; {attribute 'pytmc' := ' pv: BP_SP; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rBackingPressureSP : REAL := 0.01; {attribute 'pytmc' := ' pv: IP_SP; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rInletPressureSP : REAL := 0.02; //20mTorr (*State*) {attribute 'pytmc' := ' pv: STATE; field: ZRST STOPPED; field: ONST STARTING; field: TWST RUNNING; field: THST FAULT; field: FRST STOPPING; io: i; '} eState : E_PumpState; {attribute 'pytmc' := ' pv: CURR_MON; io: i; field: PREC 2 field: EGU "A"; '} i_rCurrentMon : REAL; {attribute 'pytmc' := ' pv: TEMP_MON; io: i; field: PREC 2; field: EGU "C"; '} i_rTempMon : REAL; {attribute 'pytmc' := ' pv: LOGGER; io: i; field: ZNAM OFF ; field: ONAM ON ; '} xLog : BOOL:=TRUE; END_STRUCT END_TYPE ST_RoughPump ^^^^^^^^^^^^ :: TYPE ST_RoughPump : STRUCT (* Generic roughing pump controls NOTE: This is an archetype, use an extension of this structure for a specific pump (or make one). *) //Controls {attribute 'pytmc' := ' pv: RUN_SW; field: ZNAM Stop; field: ONAM Start; io: io; '} pv_xRunSW : BOOL; //epics/ software control switch {attribute 'pytmc' := ' pv: RUN_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i '} q_xRunDo : BOOL; //Status and Readback {attribute 'pytmc' := ' pv: ILK_OK; field: ZNAM NOT OK ; field: ONAM OK ; io: i '} xIlkOK : BOOL; //Interlock bit, true means OK to run xExtIlk : BOOL; //External interlock, this is where the logic goes {attribute 'pytmc' := ' pv: AT_SPD; field: ZNAM FALSE; field: ONAM TRUE; io: i '} xAtSpd : BOOL; //Pump at speed setpoint {attribute 'pytmc' := ' pv: WARN; field: ZNAM FALSE; field: ONAM TRUE; io: i '} xWrn : BOOL; //Pump warning iWrn : BOOL; //Warning state/code {attribute 'pytmc' := ' pv: ERROR; field: ZNAM FALSE; field: ONAM TRUE; io: i '} xErr : BOOL; //Error summary iErr : BOOL; //Error state/code (*State*) {attribute 'pytmc' := ' pv: STATE; field: ZRST STOPPED; field: ONST STARTING; field: TWST RUNNING; field: THST FAULT; field: FRST STOPPING; io: i; '} eState : E_PumpState; END_STRUCT END_TYPE ST_ValveBase ^^^^^^^^^^^^ :: TYPE ST_ValveBase : STRUCT (* EPICS Controls *) {attribute 'pytmc' := ' pv: OPN_SW; field: ZNAM CLOSE; field: ONAM OPEN; io: io ; '} pv_xOPN_SW : BOOL; {attribute 'pytmc' := ' pv: ALM_RST; io: io; '} pv_xAlmRst : BOOL; {attribute 'pytmc' := ' pv: FORCE_OPN; io: io; field: ZNAM FALSE; field: ONAM FORCE OPEN; '} pv_xOvrdOpn : BOOL; {attribute 'pytmc' := ' pv: OVRD_ON ; field: ZNAM Override OFF ; field: ONAM Override ON; io: io; '} xOverrideMode : BOOL; (* Shows the override status of this valve *) (* I/Os*) (* Readbacks *) {attribute 'pytmc' := ' pv: OPN_DI; io: i; field: ZNAM FALSE; field: ONAM OPEN; '} i_xOpnLS : BOOL; {attribute 'pytmc' := ' pv: CLS_DI; io: i; field: ZNAM FALSE; field: ONAM CLOSE; '} i_xClsLS : BOOL; (* Controls *) {attribute 'pytmc' := ' pv: OPN_DO; io: i; field: ZNAM FALSE; field: ONAM TRUE; '} q_xOPN_DO : BOOL; (* Interlocks *) {attribute 'pytmc' := ' pv: EXT_ILK_OK ; field: ZNAM NOT OK ; field: ONAM OK ; io: i ; '} xEXT_OK : BOOL := FALSE; (* External interlock for custom interlocking in addition to regular DP ilk, this must be set true, or the interlock condition before calling the FB_VGC *) {attribute 'pytmc' := ' pv: OPN_OK; field: ZNAM OPN ILK NOT OK ; field: ONAM OPN ILK OK ; io: i; '} xOPN_OK : BOOL; (* Final SUM of DP_OK and EXT_OK, needed because it allows the DP ilk to be switched off, see FB_VGC.Dis_DPIlk *) (* States *) {attribute 'pytmc' := ' pv: POS_STATE; type: mbbi ; field: ZRST OPEN ; field: ONST CLOSED ; field: TWST MOVING ; field: THST INVALID ; field: FRST OPEN_F ; io: i; '} eState : E_ValvePositionState := INVALID; {attribute 'pytmc' := ' pv: STATE; field: ZRST Vented; field: ONST At Vacuum; field: TWST Differential Pressure; field: THST Lost Vacuum; field: FRST Ext Fault; field: FVST AT Vacuum; field: SXST Triggered; field: SVST Vacuum Fault; field: EIST Close Timeout; field: NIST Open Timeout; io: i; '} eVGC_State : E_VGC; (* Error *) {attribute 'pytmc' := ' pv: ERROR; field: ZNAM NO ERROR ; field: ONAM ERROR PRESENT ; io: o; '} bErrorPresent : BOOL; iErrorCode: INT; {attribute 'pytmc' := ' pv: ErrMsg; io: o; '} sErrorMessage: STRING; {attribute 'pytmc' := ' pv: LOGGER; io: io; field: ZNAM OFF ; field: ONAM ON ; '} xLog : BOOL:=TRUE; END_STRUCT END_TYPE ST_VCC_NO ^^^^^^^^^ :: TYPE ST_VCC_NO: STRUCT (* A normally open valve needs permission to close! *) (* Inputs *) {attribute 'pytmc' := ' pv: CLS_SW ; field: ONAM CLOSE; field: ZNAM OPEN; io: io ; '} pv_xCLS_SW : BOOL; {attribute 'pytmc' := ' pv: FORCE_CLS; field: ZNAM FALSE; field: ONAM FORCE CLOSE; io: io; '} pv_xOvrdCls : BOOL; {attribute 'pytmc' := ' pv: CLS_OK; field: ZNAM FALSE; field: ONAM CLS_OK; io: i; '} xCLS_OK : BOOL; {attribute 'pytmc' := ' pv: OVRD_ON; field: ZNAM Override OFF ; field: ONAM Override ON; io: io '} xOverrideMode : BOOL; (* Shows the override status of this valve *) (* Outputs *) {attribute 'pytmc' := ' pv: CLS_DO; field: ZNAM FALSE; field: ONAM TRUE; io: i; '} xCLS_DO : BOOL; END_STRUCT END_TYPE ST_VCN ^^^^^^ :: TYPE ST_VCN : STRUCT (* VCN - Valve Controlled Needle Used for Pfeiffer EVR 116 needle valves *) {attribute 'pytmc' := ' pv: OPN_SW; field: ZNAM CLOSE; field: ONAM OPEN; io: io ; '} xOPN_SW : BOOL; {attribute 'pytmc' := ' pv: POS_RDBK; io: i; '} // Inputs i_iPosition : REAL; //Position readback (if it exists) // Outputs {attribute 'pytmc' := ' pv: POS_AO_R; io: i ; '} q_iRawPosition : INT; //Position control //For the EVR 116 this is 0-10V analog 0.4 closed to 9.1 Closed //Softvariables {attribute 'pytmc' := ' pv: ILK_OK; field: ZNAM NOT OK ; field: ONAM OK ; io: i; '} xIlkOK : BOOL := FALSE; // Interlock Bit rUpperLimit : REAL:=100; // Percentage9.1; //Upper limit on valve open {attribute 'pytmc' := ' pv: POS_REQ; io: io; '} rReqPosition : REAL; //Requested position (0.0-100.0%) {attribute 'pytmc' := ' pv: STATE ; field: ZRST Close ; field: ONST Open; field: TWST PressureControl ; field: THST ManualControl ; io: io '} eValveControl : E_VCN := CloseValve; // Valve control state ftIlk : F_TRIG; END_STRUCT END_TYPE ST_VFS ^^^^^^ :: TYPE ST_VFS EXTENDS ST_ValveBase: STRUCT (* Interlock *) {attribute 'pytmc' := ' pv: TRIG; field: ZNAM TRIG_OFF; field: ONAM TRIG_ON; io: i ; '} i_xTrigger : BOOL; (* Commands *) {attribute 'pytmc' := ' pv: CLS_SW; field: ZNAM FALSE; field: ONAM CLOSE; io: io ; '} xCLS_SW AT%Q*:BOOL; (*external open signal e.g epics*) (* Alarm Outputs *) {attribute 'pytmc' := ' pv: ERR_Ext; field: ZNAM NO ERROR ; field: ONAM External error present ; io: i; '} xERR_ExtFault : BOOL; {attribute 'pytmc' := ' pv: VAC_FAULT_OK; field: ZNAM FAULT ; field: ONAM FAULT OK; io: i ; '} i_xVAC_FAULT_OK : BOOL; (*Valve Vacuum OK, is set to False when there is Vacuum Fault*) (*ILK Devices*) {attribute 'pytmc' := ' pv: GFS; io: i; '} sGFS: String; END_STRUCT END_TYPE ST_VG ^^^^^ :: TYPE ST_VG : STRUCT /// A general gauge structure is used to make the rest of the interlocking simpler. There are some parameters for cold cathodes that are not used by pirani. ///These features aren't disabled, they just aren't used, think child/parent classes. /// {attribute 'pytmc' := ' pv: PRESS; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" io: i; '} rPRESS: REAL; //This is the human-readable pressure {attribute 'pytmc' := ' pv: AT_VAC; io: i; field: ZNAM FALSE; field: ONAM TRUE; '} xAT_VAC: BOOL; {attribute 'pytmc' := ' pv: PRESS_OK; field: ZNAM OFF; field: ONAM ON; io: i; '} xPRESS_OK: BOOL; {attribute 'pytmc' := ' pv: STATE; field: ZRST PressInvalid; field: ONST GaugeDisconnected; field: TWST OoR; field: THST Off; field: FRST Starting; field: FVST Valid; field: SXST ValidHi; field: SVST ValidLo; io: i; '} eState : E_PressureState; (* EPICS Controls *) {attribute 'pytmc' := ' pv: HV_SW; io: io; field: ZNAM OFF; field: ONAM ON; '} xHV_SW: BOOL; // High Voltage Switch from epics /// Controls and I/Os {attribute 'pytmc' := ' pv: PRESS_AI; io: i; '} i_iPRESS_R :INT; // input Pressure // Link to analog Input {attribute 'pytmc' := ' pv: HV_ON; io: i; '} i_xHV_ON : BOOL; // True when High Voltage is on {attribute 'pytmc' := ' pv: DISC_ACTIVE; field: ZNAM NO DISC; field: ONAM DISC ACTIVE; io: i; '} i_xDisc_Active : BOOL;// Discharge Current Active {attribute 'pytmc' := ' pv: HV_DIS_DO; io: i; field: ZNAM FALSE ; field: ONAM TRUE ; '} q_xHV_DIS : BOOL; // Enable High Voltage when True // wHV_RO: WORD; /// Bakeout bit xBAKEOUT: BOOL; //enum for gauge type - will replace iType eTYPE: Gauge_Type := PG907; //Gauge type, deprecated (default to pirani) iTYPE: INT := 2; // Index location of the associated Pirani Gauge wPG: WORD; xTurnOnTime: BOOL;/// Turn on Timers for cold cathode warmup iVacSp : INT;/// At vacuum setpoint for all gauges {attribute 'pytmc' := ' pv: VAC_SP; io:io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rVAC_SP: REAL := 0.001; /// At vacuum setpoint for all gauges /// Protection setpoint for ion gauges at which the gauge turns off, not used for pirani {attribute 'pytmc' := ' pv: PRO_SP; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rPRO_SP: REAL := 0.001; {attribute 'pytmc' := ' pv: SP_HYS; io: io; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} /// Protection setpoint hysteresis rHYS_PR: REAL := 0.001; {attribute 'pytmc' := ' pv: ILK_OK; field: ZNAM NOT OK ; field: ONAM OK ; io: i; '} xILKOk : BOOL; (* also a control *) {attribute 'pytmc' := ' pv: LOGGER; io: io; field: ZNAM OFF ; field: ONAM ON ; '} xLog : BOOL:=TRUE; //Required for other devices using this gauge as interlock sPath: STRING; /// Full scale pressure in Torr for baratron pressure conversion rFULL_SCALE: REAL := 1000; END_STRUCT END_TYPE ST_VGC ^^^^^^ :: TYPE ST_VGC EXTENDS ST_ValveBase: STRUCT (* Interlocks *) {attribute 'pytmc' := ' pv: DP_OK; field: ZNAM DP NOT OK ; field: ONAM DP OK ; io: i; '} xDP_OK : BOOL; (* Managed by the VGC function (FB_VGC) *)// Indicates the valve can be opened because the differential pressure is low enough {attribute 'pytmc' := ' pv: AT_VAC_SP; io: o; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rAT_VAC_SP : REAL := 0.000001; // Interlock setpoint for gauges on both sides of valve rAT_VAC_SP_LAST : REAL :=0.000001 ; // Interlock setpoint for gauges on both sides of valve {attribute 'pytmc' := ' pv: AT_VAC_HYS; io: o; field: HOPR 1000 field: LOPR 0 field: PREC 2 field: EGU "TORR" autosave_pass1: VAL DESC '} rAT_VAC_HYS : REAL :=0.000001; // Hysteresis of the vacuum sp {attribute 'pytmc' := ' pv: HYST_PERC ; io: o; autosave_pass1: VAL DESC '} rHYST_PERC : REAL := 0.80; // Hysteresis percentage {attribute 'pytmc' := ' pv: AT_VAC ; field: ZNAM NOT AT VAC ; field: ONAM AT VAC ; io: i; '} xAT_VAC : BOOL; //At vacuum setpoint (* Alarm Outputs *) {attribute 'pytmc' := ' pv: ERR_DifPres; field: ZNAM NO ERROR ; field: ONAM Diffrential error present ; io: i; '} xERR_DifPres : BOOL; {attribute 'pytmc' := ' pv: ERR_SP; field: ZNAM NO ERROR ; field: ONAM Setpoint error present ; io: i; '} xERR_SP : BOOL; {attribute 'pytmc' := ' pv: ERR_Ext; field: ZNAM NO ERROR ; field: ONAM External error present ; io: i; '} xERR_ExtFault : BOOL; xAlmSum : BOOL; (*ILK Devices*) {attribute 'pytmc' := ' pv: ILK_DEVICE_US; io: i; '} sIlkUSDeviceName: STRING; {attribute 'pytmc' := ' pv: ILK_DEVICE_DS; io: i; '} sIlkDSDeviceName: STRING; END_STRUCT END_TYPE ST_VRC ^^^^^^ :: TYPE ST_VRC EXTENDS ST_ValveBase: STRUCT (* Readbacks *) //In case VRC is normally open {attribute 'pytmc' := ' pv: CLS_OK; io: i; '} xCLS_OK : BOOL := TRUE; END_STRUCT END_TYPE ST_VVC ^^^^^^ :: TYPE ST_VVC : STRUCT (* Inputs *) {attribute 'pytmc' := ' pv: OPN_SW; field: ZNAM CLOSE; field: ONAM OPEN; io: io; '} pv_xOPN_SW : BOOL; {attribute 'pytmc' := ' pv:FORCE_OPN; field: ZNAM FALSE; field: ONAM OPEN; io: io; '} xOvrdOpn : BOOL; {attribute 'pytmc' := ' pv: OVRD_ON; field: ZNAM Override ON ; field: ONAM Override OFF; io: io; '} xOverrideMode : BOOL; (* Shows the override status of this valve *) {attribute 'pytmc' := ' pv: OPN_OK; field: ZNAM OPN ILK NOT OK ; field: ONAM OPN ILK OK ; io: i; '} xOPN_OK : BOOL; (* Outputs *) {attribute 'pytmc' := ' pv: OPN_DO; field: ZNAM CLOSE; field: ONAM OPEN; io: i; '} q_xOPN_DO : BOOL; END_STRUCT END_TYPE GVLs ---- Constants ^^^^^^^^^ :: VAR_GLOBAL CONSTANT gc_iSizeOfGGOArray : INT := 50; gc_GaugeValidState : INT := 4; END_VAR Global_Variables ^^^^^^^^^^^^^^^^ :: VAR_GLOBAL g_iSizeOfGGOArray : INT := 50; g_stSystem : ST_System(*System_Struct*):= ( xFirstScan := TRUE ); g_DummyVG : ST_VG; fbGetCurTaskIdx : GETCURTASKINDEX; END_VAR Global_Version ^^^^^^^^^^^^^^ :: {attribute 'TcGenerated'} // This function has been automatically generated from the project information. VAR_GLOBAL CONSTANT {attribute 'const_non_replaced'} {attribute 'linkalways'} stLibVersion_LCLS_Vacuum : ST_LibVersion := (iMajor := 1, iMinor := 3, iBuild := 1, iRevision := 0, sVersion := '1.3.1'); END_VAR POUs ---- F_RoughPumpInlet_ILK ^^^^^^^^^^^^^^^^^^^^ :: (* Determine whether a pump attached to the inlet of a roughing pump is safe to open. Return true, signaling it is safe to open when the roughing pump is confirmed to be working. *) FUNCTION F_RoughPumpInlet_ILK : BOOL VAR_INPUT ScrollPump : ST_RoughPump; END_VAR VAR END_VAR F_RoughPumpInlet_ILK := (ScrollPump.eState = pumpRUNNING); (* Confirm that the roughing pump is running before opening the valve *) END_FUNCTION F_TURBO_VRC_ILK ^^^^^^^^^^^^^^^ :: (* Close the vic if the roughing pump pirani loses its at-vac setpoint while the turbo is pumping *) FUNCTION F_TURBO_VRC_ILK : BOOL VAR_INPUT i_xHiVac : BOOL; i_xRunPTM : BOOL; i_xFLVac : BOOL; END_VAR VAR rHysterisis : REAL := 0; xInterlock: BOOL; rTolerance: REAL := 20; END_VAR (* Close the vic if the roughing pump pirani loses its at-vac setpoint while the turbo is pumping *) F_TURBO_VRC_ILK := (NOT i_xHiVac AND NOT i_xRunPTM AND NOT i_xFLVac) OR (NOT i_xRunPTM AND i_xFLVac) OR (i_xRunPTM AND i_xFLVac); END_FUNCTION F_TurboExtILK_NO ^^^^^^^^^^^^^^^^ :: (* This function Block interfaces with a NO turbo valve*) (* Mainly used in the FEE HXR GATT*) FUNCTION F_TurboExtILK_NO : BOOL VAR_INPUT Turbo : ST_PTM; BackingGauge: ST_VG; InletGauge : ST_VG; VentValve : ST_VCC_NO; //NO type turbo valve ScrollPump : ST_RoughPump; END_VAR VAR END_VAR END_FUNCTION F_TurboExtILK_NO_1 ^^^^^^^^^^^^^^^^^^ :: (* This function Block interfaces with a NO turbo valve*) (* Mainly used in the FEE HXR GATT*) FUNCTION F_TurboExtILK_NO_1 : BOOL VAR_INPUT Turbo : ST_PTM; BackingGauge: ST_VG; InletGauge : ST_VG; VentValve : ST_VCC_NO; //NO type turbo valve ScrollPump : ST_RoughPump; END_VAR END_FUNCTION F_TurboExtILKLogic ^^^^^^^^^^^^^^^^^^ :: (* This function evaluates the Turbo ILK logic*) (* The Turbo shall not Run if the vent valve is open*) (* The function also switches off the Turbo if the Inlet pressure or the backing Pressure*) (* are higher than the configured set point*) FUNCTION F_TurboExtILKLogic : BOOL VAR_INPUT Turbo : ST_PTM; BackingGauge: ST_VG; InletGauge : ST_VG; VentValve : ST_VVC; ScrollPump : ST_RoughPump; END_VAR VAR END_VAR END_FUNCTION F_TurboExtILKLogic_2 ^^^^^^^^^^^^^^^^^^^^ :: (* This function evaluates the Turbo ILK logic*) (* The function switches off the Turbo if the Inlet pressure or the backing Pressure*) (* are higher than the configured set point*) FUNCTION F_TurboExtILKLogic_2 : BOOL VAR_INPUT Turbo : ST_PTM; BackingGauge: ST_VG; InletGauge : ST_VG; ScrollPump : ST_RoughPump; END_VAR VAR END_VAR END_FUNCTION F_TurboGateValve_ILK ^^^^^^^^^^^^^^^^^^^^ :: (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) FUNCTION F_TurboGateValve_ILK : BOOL (* function return TRUE when ILK is OK*) VAR_INPUT i_Turbo : ST_PTM; // Turbo Pump i_stISG : ST_VG; //Gauge measuring inlet Pressure e.g Pirani END_VAR VAR END_VAR (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) F_TurboGateValve_ILK := (NOT (i_Turbo.eState = pumpSTOPPED) AND NOT (i_Turbo.eState = pumpSTOPPING) AND NOT (i_Turbo.eState = pumpFAULT)) AND (i_stISG.xPRESS_OK AND i_stISG.rPRESS < i_Turbo.rInletPressureSP); END_FUNCTION F_TurboGateValve_ILK_1 ^^^^^^^^^^^^^^^^^^^^^^ :: (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) FUNCTION F_TurboGateValve_ILK_1 : BOOL (* function return TRUE when ILK is OK*) VAR_INPUT i_Turbo : ST_PTM; // Turbo Pump i_stISG : ST_VG; //Gauge measuring inlet Pressure e.g Pirani END_VAR VAR END_VAR (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) F_TurboGateValve_ILK_1 := (NOT (i_Turbo.eState = pumpSTOPPED) AND NOT (i_Turbo.eState = pumpFAULT)) AND (i_stISG.xPRESS_OK AND i_stISG.rPRESS < i_Turbo.rInletPressureSP); END_FUNCTION F_TurboGateValve_Protection_ILK ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) (* And Protects the Turbo pump from backing pressure above the SP*) (* And Protects the Turbo pump in the case of backing pump not running*) FUNCTION F_TurboGateValve_Protection_ILK : BOOL (* function return TRUE when ILK is OK*) VAR_INPUT i_Turbo : ST_PTM; // Turbo Pump i_stISG : ST_VG; //Gauge measuring inlet Pressure e.g Pirani i_stBSG : ST_VG; //Gauge measuring backing Pressure e.g Pirani ScrollPump : ST_RoughPump; // Roughing pump END_VAR VAR END_VAR (* This Function Block evaluates the ILK condition of the Turbo Gate valve *) (* The logic Protects the Turbo pump from inlet pressure above the SP*) F_TurboGateValve_Protection_ILK := (NOT (i_Turbo.eState = pumpSTOPPED) AND NOT (i_Turbo.eState = pumpSTOPPING) AND NOT (i_Turbo.eState = pumpFAULT)) AND (i_stISG.xPRESS_OK AND i_stISG.rPRESS < i_Turbo.rInletPressureSP) AND (i_stBSG.xPRESS_OK AND i_stBSG.rPRESS < i_Turbo.rBackingPressureSP) AND (ScrollPump.eState = pumpRUNNING); END_FUNCTION F_VALVE_REL_ILK ^^^^^^^^^^^^^^^ :: (* OK, this is going to be a general-use function that will return whether the ratio of the pressures given is less than x orders of magnitude. True iff 10^(-x) < a/b < 10^x. Simple, right? Just make sure it gets a positive order of magnitude and the pressures aren't 0. ~Scott *) {attribute 'no_check'} FUNCTION F_VALVE_REL_ILK : BOOL VAR_INPUT i_stUSG : ST_VG; // Upstream Gague i_stDSG : ST_VG; // Downstream Gague //Orders of magnitude allowed between up/downstream pressures for valve to open. Must be real positive integer. i_iOrdersMagnitude: INT; END_VAR VAR rPressRatio : REAL; rMaxRatioAllowed : LREAL := EXPT(10,i_iOrdersMagnitude); rMinRatioAllowed : LREAL := EXPT(10,(-i_iOrdersMagnitude)); END_VAR (* S. Stubbs 11/2015 *) (* OK, this is going to be a general-use function that will return whether the ratio of the pressures given is less than x orders of magnitude. True iff 10^(-x) < a/b < 10^x. Simple, right? Just make sure it gets a positive order of magnitude and the pressures aren't 0. ~Scott *) (* Find ratio *) IF i_stDSG.rPRESS <> 0 THEN rPressRatio := i_stUSG.rPRESS / i_stDSG.rPRESS; F_VALVE_REL_ILK := rPressRatio < rMaxRatioAllowed AND rMinRatioAllowed < rPressRatio; ELSE F_VALVE_REL_ILK := FALSE; END_IF END_FUNCTION F_VRC_DIODE_ILK ^^^^^^^^^^^^^^^ :: (* The principle is simple, a VRC should never open when B side pressure is greater than the A side pressure. *) FUNCTION F_VRC_DIODE_ILK : BOOL VAR_INPUT i_stPGA : ST_VG; // A side pressure gauge i_stPGB : ST_VG; // B side pressure gauge END_VAR VAR rHysterisis : REAL := 0; xInterlock: BOOL; rTolerance: REAL := 20; END_VAR (* The principle is simple, a VRC should never open when B side pressure is greater than the A side pressure. *) rTolerance := 20; F_VRC_DIODE_ILK := ((i_stPGA.rPRESS + rTolerance >= i_stPGB.rPRESS + rHysterisis) OR i_stPGA.xBAKEOUT) AND i_stPGA.xPRESS_OK AND i_stPGB.xPRESS_OK; (* OK it gets a bit more complicated with hysterisis, Now, if the F_VIC_DIODE_ILK is FALSE then some hysterisis is added to the B side. But if it's OK then we just stick with making sure that the A side is greater than the B side *) (* IF NOT F_VIC_DIODE_ILK THEN Hysterisis := 40; ELSE Hysterisis := 0; END_IF *) END_FUNCTION F_VRC_DIODE_ILK_OK ^^^^^^^^^^^^^^^^^^ :: (*The function returns TRUE i.e. ILK_OK when pressure on A side is greater than or equal to the pressure on B side*) FUNCTION F_VRC_DIODE_ILK_OK : BOOL VAR_INPUT i_stPGA : ST_VG; // A side pressure gauge i_stPGB : ST_VG; // B side pressure gauge END_VAR VAR rHysterisis : REAL := 0; xInterlock: BOOL; rTolerance: REAL := 0; END_VAR (* The principle is simple, a VRC should never open when B side pressure is greater than the A side pressure. *) F_VRC_DIODE_ILK_OK := (i_stPGA.rPRESS + rTolerance >= i_stPGB.rPRESS + rHysterisis) AND i_stPGA.xPRESS_OK AND i_stPGB.xPRESS_OK; END_FUNCTION FB_972 ^^^^^^ :: {attribute 'no_check'} FUNCTION_BLOCK FB_972 EXTENDS FB_GaugeBase VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR_IN_OUT END_VAR VAR rV : REAL; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* Standard MKS 9XX series conversion *) (* works for 972 and 925 *) // no div by zero If (iTermBits=0) THEN iTermBits := 32767;END_IF rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; IF rV > 1 THEN PG.rPRESS := LREAL_TO_REAL(EXPT(10, 2*rV-11)); PG.xPRESS_OK := TRUE; PG.eState := Valid; //Press OK ELSE PG.xPRESS_OK := FALSE; PG.eState := OoR; END_IF (* Soft IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_9XX ^^^^^^ :: (* Standard MKS 9XX series conversion *) (* works for 925 *) FUNCTION_BLOCK FB_9XX EXTENDS FB_GaugeBase VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR_IN_OUT END_VAR VAR rV : REAL; fbGaugeState : FB_PressureState; rMaxPressure : REAL := 760; //Torr rMinPressure : REAL := 1E-5; //Torr (*Default set point 50 mT*) rVAC_SP: REAL := 5E-2; iTermBits: UINT := 30518 ; // The terminal's maximum value in bits default el3174 as per vacuum architecture (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* Standard MKS 9XX series conversion *) (* works for 972 and 925 *) //Default setpoint 50 mT IF PG.rVAC_SP = 0 THEN PG.rVAC_SP := rVAC_SP; END_IF // check no div by zero If (iTermBits=0) THEN iTermBits := 30518;END_IF rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; IF rV >= 0.99 THEN IF rV >= 0.99 AND rV <= 1 THEN PG.rPRESS := 1E-5; ELSE PG.rPRESS := LREAL_TO_REAL(EXPT(10, rV-6)); END_IF PG.xPRESS_OK := TRUE; PG.eState := Valid; ELSE PG.xPRESS_OK := FALSE; IF (rV = 0) THEN PG.eState := GaugeDisconnected; ELSE PG.eState := PressInvalid; END_IF END_IF (* Setpoint evaluation *) PG.xAT_VAC := PG.xPRESS_OK AND (PG.rPRESS < PG.rVAC_SP); (*Logger*) ACT_Logger(); (*Soft IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION ACTION ACT_Logger: //STATE Logger IF ePrevState <> PG.eState THEN CASE PG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := PG.eState; END_IF END_ACTION FB_ADS ^^^^^^ :: FUNCTION_BLOCK FB_ADS VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.FIELDBUS); tErrorPresent : R_TRIG; END_VAR END_FUNCTION_BLOCK FB_ADS_WATCHDOG ^^^^^^^^^^^^^^^ :: (*This function block is to be used whenever deivce data going to be read over ADS*) (*The remote plc has to instantiate this function block in order to write a watchdog variable*) (*that the ADS read function expects to keep checking*) FUNCTION_BLOCK FB_ADS_WATCHDOG VAR_INPUT sNetId : String; //NetID of the Destination PLC controller nPort : uint; // port number sVarName : string;// the variable name of the watchdog on the remote plc. END_VAR VAR_OUTPUT bError:BOOL; END_VAR VAR fb_WriteWatchdog:FB_WriteWatchdog; ftReset_Watchdog: F_TRIG; xFirstPass: BOOL:= true; END_VAR ftReset_Watchdog(CLK:= fb_WriteWatchdog.bBusy OR xFirstPass); xFirstPass := false; (*calling ADS Watchdog function*) fb_WriteWatchdog( bEnable:= TRUE , sNetId:= sNetId, nPort:= nPort, nIdxGrp:= , nIdxOffs:= , sVarName:= sVarName, tWatchdogTime:= t#30ms, bSendNow:= ftReset_Watchdog.Q , bBusy=> , nLastCnt=> , bError=> bError, nErrorId=> ); (*Error*) END_FUNCTION_BLOCK FB_CMR362 ^^^^^^^^^ :: (* For Pfeiffer CMR 362 *) FUNCTION_BLOCK FB_CMR362 VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} PG : ST_VG; END_VAR VAR MinPressure: REAL := 7.5E-3; //Torr FullScale : REAL := 75.0 ; //Torr V : REAL; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* For Pfeiffer CMR 362 *) (*Soft IO Mapping*) ACT_IO(); (* Real-value calculation *) If (iTermBits=0) THEN iTermBits := 32767;END_IF V := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; IF V < 0.4 THEN PG.eState := GaugeDisconnected; //Most likely not connected ELSIF V >= 0.4 AND V <= 1.0 THEN PG.rPRESS := MinPressure; PG.eState := ValidLo; ELSIF V > 1 AND V <= 9.8 THEN PG.rPRESS := LREAL_TO_REAL((V-1)*0.125*FullScale); PG.eState := Valid; ELSE PG.eState := OoR; //Larger voltage, probably out of range? END_IF (* Pressure OK check *) PG.xPRESS_OK := (PG.rPRESS >= MinPressure); (*Soft IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_EbaraDryPump ^^^^^^^^^^^^^^^ :: (*This function block does basic controls for the Ebara EV-S Dry pump. Starts booster pump when appropriate. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_EbaraDryPump EXTENDS FB_Pump VAR_INPUT i_stBPGauge : ST_VG; //booster pump interlock gauge i_xVlvOpn : BOOL; //valve(s) to main system load are open i_xExtIlkOK : BOOL; END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} stPump : ST_EbaraDryPump; //Ebara dry pump structure END_VAR VAR_IN_OUT END_VAR VAR (*IO*) q_xMPStart AT%Q*: BOOL; //Main Pump start q_xBPStart AT%Q*: BOOL; // Booster Pump start (this can be started by the pump automatically) //Readbacks i_xMPStatus AT%I*: BOOL; //MP status i_xBPStatus AT%I*: BOOL; //BP status i_xWarning AT%I*: BOOL; //Warning status i_xAlarmOK AT%I*: BOOL; //Alarm, maps to error i_xRemote AT%I*: BOOL; //Remote control status END_VAR (* Ebara Dry Pump Control Routine A. Wallace 2016-4-29 Applicable to: EV-S20 EV-S50 EV-S100 EV-S200 Does basic controls for the pump. Starts booster pump when appropriate. Turns off pump in the event of errors/ warnings. Provides interlocking interface. Main Pump Operational Note: Ebara recommends that the pump be exposed to the full system load after the main pump is running. Booster Pump Operational Note: The booster pump can either be started automatically by the pump itself (probably based on a timer?) or it can be started by the PLC. The pressure setpoint is by default 30T. It may be wise to make sure the pump was exposed to the full system load before starting the booster. *) //Mapping ACT_IO(); stPump.xExtIlk := NOT i_xExtIlkOK; //Interlock and error/warning stPump.xIlkOK := NOT stPump.xExtIlk AND NOT (stpump.xErr);// OR stpump.xWrn); //Start/ Stop the pump IF stpump.xIlkOK THEN stpump.q_xMPStart := stpump.pv_xRunSW; ELSE stpump.q_xMPStart := stpump.pv_xRunSW := FALSE; END_IF //Booster pump stpump.xBPIlk := i_xVlvOpn AND stpump.i_xMPStatus AND (i_stBPGauge.rPRESS < stpump.rBPIlkSP) AND i_stbpgauge.xPRESS_OK; stpump.tonBP(IN:=stpump.xBPIlk); stpump.q_xBPStart := stpump.tonBP.Q; (*State evaluation*) IF stPump.i_xAlarm THEN stpump.eState := pumpFAULT; ELSIF NOT stpump.q_xMPStart AND stPump.i_xAlarm THEN stpump.eState := pumpSTOPPED; ELSIF stpump.q_xMPStart AND NOT stPump.i_xMPStatus THEN stpump.eState := pumpSTARTING; ELSIF stPump.i_xMPStatus OR stPump.i_xBPStatus THEN stpump.eState := pumpRUNNING; ELSIF NOT stPump.i_xMPStatus AND NOT stPump.i_xBPStatus THEN stpump.eState := pumpSTOPPED; ELSE stpump.eState := pumpFAULT; END_IF //Mapping ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*Outputs*) q_xMPStart := stPump.q_xMPStart; q_xBPStart := stPump.q_xBPStart; stPump.q_xRunDo := stPump.q_xMPStart; (*Inputs*) stPump.i_xMPStatus:= i_xMPStatus; stPump.i_xBPStatus:= i_xBPStatus; stPump.i_xWarning:= NOT i_xWarning; // These are normally closed inputs stPump.xWrn := NOT i_xWarning; stPump.i_xAlarm:= NOT(i_xAlarmOK); StPump.xErr := NOT(i_xAlarmOK); stPump.i_xRemote:= i_xRemote; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> stpump.eState THEN CASE stpump.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := stpump.eState; END_IF // Log Action tAction(CLK:= stPump.q_xRunDo); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= NOT stPump.i_xAlarm); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_EbaraEVA ^^^^^^^^^^^ :: (*This function block does basic controls for the Ebara EV-A pump. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_EbaraEVA EXTENDS FB_Pump VAR_INPUT i_xExtIlkOK : BOOL; END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} stPump : ST_EbaraEVA; //Ebara dry pump structure END_VAR VAR // For logging (* fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_PumpState; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, PUMP_RUN, etc.) tFault : F_TRIG;*) (*IO*) q_xRunDo AT%Q* : BOOL; // Output signal q_xRemote At%Q* :BOOL;// Remote/Local Control q_xResetAlarm At%Q* :BOOL;// Remote/Local Control i_xAlarmOK AT%I* :BOOL; // Normally closed Alarm bit i_xIsRun AT%I*:BOOL; // Input status END_VAR (*MG 2019*) stPump.xIlkOK := i_xExtIlkOK AND stPump.i_xAlarmOK ; // Enable the remote signal when the interlock evaluation is ok //stPump.q_xRemote := stPump.xIlkOK; IF stPump.xIlkOK THEN stPump.q_xRunDO := stPump.pv_xRunSW; ELSE stPump.PV_xRunSW := FALSE; stPump.q_xRunDO := FALSE; END_IF (*State evaluation*) IF NOT(stPump.i_xAlarmOK ) THEN ePrevState := stpump.eState; stpump.eState := pumpFAULT; ELSIF NOT stpump.q_xRunDo AND stPump.i_xAlarmOK THEN stpump.eState := pumpSTOPPED; ELSIF NOT stpump.q_xRunDo AND NOT stPump.i_xIsRun THEN stpump.eState := pumpSTARTING; ELSIF stPump.i_xIsRun THEN stpump.eState := pumpRUNNING; ELSE stpump.eState := pumpFAULT; END_IF //Soft IO Mapping ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: stPump.i_xAlarmOK:= i_xAlarmOK; stPump.xErr:= NOT(i_xAlarmOK); stPump.i_xIsRun := i_xIsRun; (*outputs*) q_xRunDo:=stPump.q_xRunDo; q_xRemote:= stPump.q_xRemote; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> stpump.eState THEN CASE stpump.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := stpump.eState; END_IF // Log Action tAction(CLK:= stPump.q_xRunDo); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= stPump.i_xAlarmOK); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_Gauge_Interface ^^^^^^^^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) FUNCTION_BLOCK FB_Gauge_Interface VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} VG : ST_VG; END_VAR VAR i_rPRESS AT%I*: REAL; i_xAT_VAC AT%I*: BOOL; i_xPRESS_OK AT%I*: BOOL; END_VAR (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) VG.rPRESS := i_rPRESS; VG.xAT_VAC := i_xAT_VAC; VG.xPRESS_OK := i_xPRESS_OK; END_FUNCTION_BLOCK FB_GaugeBase ^^^^^^^^^^^^ :: {attribute 'reflection'} FUNCTION_BLOCK FB_GaugeBase VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR //Logging fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_PressureState; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, etc.) tOverrideActivated : R_TRIG; {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; END_VAR END_FUNCTION_BLOCK FB_GaugeModbus ^^^^^^^^^^^^^^ :: FUNCTION_BLOCK FB_GaugeModbus EXTENDS FB_GaugeBase VAR_INPUT IP: STRING; //IP of Legacy PLC nMBAddr :WORD; //Start address of the Register Memory iBitOffset:INT :=0; // must be smaller than 32 tTimeout :TIME:= T#1s; END_VAR VAR_OUTPUT VG: ST_VG; xNoPLCResponse: BOOL; END_VAR VAR fbPLCInputCoilsRx : FB_MBReadInputs;// (Modbus function 2) i_PLC_nBits : ARRAY [1..4] OF BYTE; ftReset : F_TRIG; tonRetry : TON; ErrCount:INT :=0; iIndex :INT; iBit:INT; END_VAR VAR CONSTANT nZero : INT := 0; nOne : INT := 1; nTwo : INT := 2; nThree : INT := 3; nFour : INT := 4; nFive : INT := 5; nSix : INT := 6; nSeven : INT := 7; END_VAR A_ReadMB(); END_FUNCTION_BLOCK ACTION A_ReadMB: (*Credit to Alex Wallace*) (* Modbus Info for Koyo Modbus Addresses for Koyo DL05/06/240/250/260/430/440/450 PLCs PLC Memory Type | Modbus start address Decimal (octal) | Function codes Inputs (X) 2048 (04000) 2 Special Relays (SP) 3072 (06000) 2 Outputs (Y) 2048 (04000) 1, 5, 15 Control Relays (C) 3072 (06000) 1, 5, 15 Timer Contacts (T) 6144 (014000) 1, 5, 15 Counter Contacts (CT) 6400 (014400) 1, 5, 15 Stage Status Bits (S) 6144 (012000) 1, 5, 15 *) // Retry after some time tonRetry.IN := NOT fbPLCInputCoilsRx.bBusy; tonRetry.PT := T#10S; tonRetry(); ftReset(CLK:=fbPLCInputCoilsRx.bBusy); fbPLCInputCoilsRx.bExecute := ftReset.Q OR tonRetry.Q; fbPLCInputCoilsRx(sIPAddr:=IP, nTCPPort:=502, nQuantity:=32, nMBAddr:=nMBAddr, cbLength:=SIZEOF(i_PLC_nBits), pDestAddr:=ADR(i_PLC_nBits), tTimeout:=tTimeout); //if there's a modbus error, set all incoming bits to zero IF fbPLCInputCoilsRx.bError THEN i_PLC_nBits[1].0 := 0; i_PLC_nBits[1].1 := 0; i_PLC_nBits[1].2 := 0; i_PLC_nBits[1].3 := 0; i_PLC_nBits[1].4 := 0; i_PLC_nBits[1].5 := 0; i_PLC_nBits[1].6 := 0; i_PLC_nBits[1].7 := 0; i_PLC_nBits[2].0 := 0; i_PLC_nBits[2].1 := 0; i_PLC_nBits[2].2 := 0; i_PLC_nBits[2].3 := 0; i_PLC_nBits[2].4 := 0; i_PLC_nBits[2].5 := 0; i_PLC_nBits[2].6 := 0; i_PLC_nBits[2].7 := 0; VG.xPRESS_OK := FALSE; xNoPLCResponse:= TRUE; ELSIF NOT ftReset.Q AND fbPLCInputCoilsRx.cbRead > 0 THEN xNoPLCResponse:= FALSE; VG.rPRESS := 1E-8; iIndex:= REAL_TO_INT(iBitOffset/8); iBit:= (24 MOD 10); VG.xPRESS_OK := M_GetBit(iIndex,iBit); fbPLCInputCoilsRx.bExecute :=False; ELSIF (*ftReset.Q*) NOT (fbPLCInputCoilsRx.bExecute) AND fbPLCInputCoilsRx.cbRead = 0 THEN xNoPLCResponse:= TRUE; END_IF // Log Action tAction(CLK:= fbPLCInputCoilsRx.bError or xNoPLCResponse); IF tAction.Q THEN ErrCount:= ErrCount+1; fbLogger(sMsg:='Modbus read error', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_GCC_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_GCC_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR PG : ST_VG; fb_MKS422: FB_MKS422; //fb_MKS500: FB_MKS500; i_iPRESS_R AT %Q* :INT; cycle:INT :=0; END_VAR M_INIT(); M_Interlock(); M_CheckRange(); //M_SelfProtection(); cycle:=cycle+1; END_FUNCTION_BLOCK FB_GCM ^^^^^^ :: (* For Baratron Pressure Gauge *) (*The full-scale pressure times the ratio of the meas. voltage to full-scale voltage (10V). The minimum pressure is always going to be 5e-4 * F.S.V.*) FUNCTION_BLOCK FB_GCM VAR_IN_OUT END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} PG : ST_VG; END_VAR VAR_INPUT i_rFULL_SCALE : REAL; END_VAR VAR rMinPressure: REAL; rHYST_PERC : REAL := 0.80; // Hysteresis percentage rV :REAL; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* Baratron Pressure Calc A. Wallace 2016-6-16 The full-scale pressure times the ratio of the meas. voltage to full-scale voltage (10V). The minimum pressure is always going to be 5e-4 * F.S.V.*) PG.rFULL_SCALE := i_rFULL_SCALE; rMinPressure := PG.rFULL_SCALE * 5E-4; If (iTermBits=0) THEN iTermBits := 32767;END_IF rV :=MAX((10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits),0); PG.rPRESS := (PG.rFULL_SCALE/10)*rV; IF PG.rPRESS > (rMinPressure)THEN PG.xPRESS_OK := TRUE; ELSE PG.xPRESS_OK := FALSE; END_IF (*IO soft linking*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_GPI_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_GPI_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR fb_MKS275: FB_MKS275; fb_MKS317: FB_MKS317; i_iPRESS_R AT %Q* :INT; cycle:INT :=0; END_VAR M_INIT(); M_CheckRange(); cycle:=cycle+1; END_FUNCTION_BLOCK FB_ITR90 ^^^^^^^^ :: (* This function block implements the Oerlikon Leybold IONVAC Hot Cathode ITR 90*) FUNCTION_BLOCK FB_ITR90 VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR rV : REAL; // input voltage rC: REAL := -0.125; // a constant in the manual (pressure unit dependent) rK: REAL := 1.0;// Calibration factor C (gas type dependent) iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* This function block implements the Oerlikon Leybold IONVAC Hot Cathode ITR 90*) (* Real-value calculation *) rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; PG.rPRESS := (LREAL_TO_REAL(EXPT(10,(rV-7.75)/0.75+rC))) * rK; (* Pressure gauge State checks *) //IF (rV <=9.7 ) AND (rV>=1) THEN //PG.eState := Valid; // normal IF rV >= 0.5 AND rV <= 4.5 THEN PG.eState := ValidLo; //LO ELSIF rV > 4.5 AND rV<= 9.9 THEN PG.eState := ValidHi; //HIGH ELSIF rV < 0.5 THEN PG.eState := GaugeDisconnected; //not on ELSE PG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc END_IF (* Pressure gauge OK checks *) PG.xPRESS_OK := (rV <=9.9 ) AND (rV>=0.5); (* Setpoint evaluation *) PG.xAT_VAC := PG.xPRESS_OK AND PG.rPRESS < PG.rVAC_SP; (*Soft IO Linking*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_Kashiyama_VRC ^^^^^^^^^^^^^^^^ :: (* Main Kashiyama gate valve permit to be open when: 1. pump is NORMAL operating status, and 2. chamber pressure is below HighSP or pump is at LowSpeed state. it will close if chamber pressure is below than lowSP for 180s. *) FUNCTION_BLOCK FB_Kashiyama_VRC VAR_IN_OUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv:'} q_stValve : ST_VRC; END_VAR VAR_INPUT Pump : ST_KashiyamaDryPump;//Kashyiama pump i_xExtILK_OK : BOOL; //Connect to Interlock logic condition(e.g F_TURBO_VRC_ILK Function), otherwise, Set to True if the valve is not interlocked Gauge : ST_VG;// Pressure Gauge i_xOverrideMode : BOOL; END_VAR VAR At_HiVac : BOOL; tmrTP : TP; tmrPT: TIME := T#180S; v1: BOOL; tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; END_VAR (* Main Kashiyama gate valve permit to be open when: 1. pump is NORMAL operating status, and 2. chamber pressure is below HighSP or pump is at LowSpeed state. it will close if chamber pressure is below than lowSP for 60s. *) (* For main Kayshiyama pump's gate valve: Pump LowSP is MU100 base pressure (20 mTorr) Pump HighSP is 600 Torr; Second Kayshiyama pump allow Gas Attenuator operating at higher pressure, it's gate valve is to be open when Gas Attenuator chamber pressure is above lowSP. *) ///Check valve postion IF NOT q_stValve.i_xClsLS AND q_stValve.i_xOpnLS THEN q_stValve.eState:=OPEN; ELSIF q_stValve.i_xClsLS AND NOT q_stValve.i_xOpnLS THEN q_stValve.eState:=CLOSED; ELSIF NOT q_stValve.i_xClsLS AND NOT q_stValve.i_xOpnLS THEN q_stValve.eState:=MOVING; ELSE q_stValve.eState:=INVALID; END_IF //valve open interlock q_stValve.xOPN_OK := Pump.i_xIsRun AND Pump.RdyTmr <= 0.0 AND (Gauge.rPRESS < Pump.HighSP OR Pump.q_xLspdDo) AND i_xExtILK_OK; (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=q_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q THEN q_stValve.pv_xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=q_stValve.pv_xOvrdOpn, PT:=T#10S); (* Here's where the valve closes *) IF NOT q_stValve.xOPN_OK AND NOT tonOvrd.Q THEN q_stValve.pv_xOPN_SW := FALSE; END_IF At_HiVac := Gauge.xPRESS_OK AND Gauge.rPRESS < Pump.LowSP; tmrTP (IN := At_HiVac AND q_stValve.q_xOPN_DO , PT := tmrPT); IF At_HiVac AND q_stValve.q_xOPN_DO AND NOT tmrTP.Q THEN q_stValve.pv_xOPN_SW := FALSE ; END_IF (* Here's where the valve opens *) q_stValve.q_xOPN_DO := (q_stValve.pv_xOPN_SW AND q_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); (*Soft IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) q_stValve.i_xOpnLS := i_xOpnLS; q_stValve.i_xClsLS:= i_xClsLS; (*outputs*) q_xOPN_DO:= q_stValve.q_xOPN_DO; END_ACTION FB_KashiyamaPump ^^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Kashiyama pump. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_KashiyamaPump VAR_IN_OUT END_VAR VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} q_stPump : ST_KashiyamaDryPump; END_VAR VAR TONtmr : TON; resetTmr : TON; (* Output *) q_xRunDO AT%Q*: BOOL; q_xResetDo AT%Q*: BOOL; q_xLspdDo AT%Q*: BOOL; (* Input *) i_xLocal AT%I*: BOOL; i_xAlarm AT%I*: BOOL; i_xWarning AT%I*: BOOL; i_xIsRun AT%I*: BOOL; END_VAR //Kashiyama Dry pump outputs at high level indicate no warning, no alarm and remotely q_stPump.xIlkOK := q_stPump.i_xLocal AND q_stPump.i_xWarningOK AND q_stPump.i_xAlarmOK; IF q_stPump.xIlkOK THEN q_stPump.q_xRunDO := q_stPump.pv_xRunSW; ELSE q_stPump.PV_xRunSW := FALSE; q_stPump.q_xRunDO := FALSE; END_IF q_stPump.xLspd_IlkENA := q_stPump.q_xRunDO; IF q_stPump.xLspd_IlkENA THEN q_stPump.q_xLspdDo := q_stPump.xLspdSW; ELSE q_stPump.xLspdSW := FALSE; q_stPump.q_xLspdDo := FALSE; END_IF //resetTmr (In := iq_stPump.xResetSW, PT := T#2S); //IF resetTmr.Q THEN // iq_stPump.xResetSW := FALSE; //END_IF q_stPump.q_xResetDo := NOT q_stPump.i_xIsRun AND q_stPump.xResetSW; //countdown Timer TONtmr (IN := q_stPump.i_xIsRun, PT := T#20S); q_stPump.RdyTmr := TO_REAL(TONtmr.PT) - TO_REAL(TONtmr.ET); (*State evaluation*) IF NOT q_stPump.i_xAlarmOK THEN q_stPump.eState := pumpFAULT; ELSIF NOT q_stPump.q_xRunDo AND q_stPump.i_xAlarmOK THEN q_stPump.eState := pumpSTOPPED; ELSIF NOT q_stPump.q_xRunDo AND NOT q_stPump.i_xIsRun THEN q_stPump.eState := pumpSTARTING; ELSIF q_stPump.i_xIsRun THEN q_stPump.eState := pumpRUNNING; ELSE q_stPump.eState := pumpFAULT; END_IF //Mapping ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (* Output *) q_xRunDO := q_stPump.q_xRunDO; q_xResetDo := q_stPump.q_xResetDo;; q_xLspdDo := q_stPump.q_xLspdDo;; (* Input *) q_stPump.i_xLocal := i_xLocal; q_stPump.i_xAlarmOK := i_xAlarm; q_stPump.i_xWarningOK := i_xWarning; q_stPump.i_xIsRun := i_xIsRun; END_ACTION FB_MKS248 ^^^^^^^^^ :: (* MKS248 valve using MKS1249 Drive Module *) FUNCTION_BLOCK FB_MKS248 VAR_INPUT i_xExtIlkOK : BOOL; //External Interlock, SET to TRUE if not used i_rReqPos : REAL; //Requested position END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} iq_stVFN : ST_VCN; //Needle valve structure END_VAR VAR CONSTANT rOpenVoltage : REAL := 9.8; rCloseVoltage : REAL := 0; END_VAR VAR // Requested voltage rReqVoltage: REAL := 0; (*IO*) q_iRawPosition AT%Q* :INT; END_VAR (* MKS248 valve using MKS1249 Drive Module *) // Interlocking iq_stVFN.xIlkOK := i_xExtIlkOK; (*Checking which Control mode is selected*) IF iq_stVFN.xIlkOK THEN IF iq_stVFN.eValveControl = OpenValve THEN iq_stVFN.rReqPosition := iq_stVFN.rUpperLimit;(*Percentage*)// iq_stVCN.rUpperLimit; ELSIF iq_stVFN.eValveControl = CloseValve THEN iq_stVFN.rReqPosition := 0; (*Percentage*) ELSIF iq_stVFN.eValveControl = ManualControl THEN iq_stVFN.rReqPosition := LIMIT(0, iq_stVFN.rReqPosition, iq_stVFN.rUpperLimit); ELSIF iq_stVFN.eValveControl = PressureControl THEN iq_stVFN.rReqPosition := LIMIT(0, i_rReqPos, iq_stVFN.rUpperLimit); END_IF ELSE iq_stVFN.rReqPosition := 0; iq_stVFN.eValveControl := CloseValve; END_IF // Requested Voltage calculation rReqVoltage := iq_stVFN.rReqPosition * (rOpenVoltage-rCloseVoltage)/100 + rCloseVoltage; rReqVoltage := LIMIT(rCloseVoltage, rReqVoltage, rOpenVoltage); //The requested voltage should remain within this range //Raw position calc iq_stVFN.q_iRawPosition := REAL_TO_INT( 32767/10 * rReqVoltage); (*IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*outputs*) q_iRawPosition := iq_stVFN.q_iRawPosition; END_ACTION FB_MKS275 ^^^^^^^^^ :: (* This function block is used to provide protection and automatic turn on of ion gauges, it also manages the turn on of the AT_VAC boolean, and checks to make sure the pressure is good *) (* For MKS 275 mini-convectron *) {attribute 'no_check'} FUNCTION_BLOCK FB_MKS275 EXTENDS FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR V : REAL; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits Vlowest: REAL := 10; (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR VAR CONSTANT MinPressure: REAL := 1E-4; rDeadband : REAL :=0.05; rValidLoBoundary : REAL := 0.375; // 0.375V as per manual page 27 rValidHiBoundary : REAL := 5.659;// 5.534; // manual page 27 rDisconnectedBoundary : REAL := 0.3; END_VAR (* This function block is used to provide protection and automatic turn on of ion gauges, it also manages the turn on of the AT_VAC boolean, and checks to make sure the pressure is good *) (* For MKS 275 mini-convectron *) //Default setpoint 50 mT IF PG.rVAC_SP = 0 THEN PG.rVAC_SP := 5E-2; END_IF (* Real-value calculation *) If (iTermBits=0) THEN iTermBits := 32767;END_IF V := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; Vlowest := MIN(V, Vlowest); IF V > rDisconnectedBoundary AND V < rValidLoBoundary THEN PG.rPRESS := MinPressure; PG.eState := ValidLo; ELSIF V >= rValidLoBoundary AND V < rValidLoBoundary +rDeadband THEN PG.rPRESS := MAX( MinPressure, LREAL_TO_REAL( -0.02585 + 0.03767*V + 0.04563*EXPT(V,2)+ 0.1151*EXPT(V,3) - 0.04158*EXPT(V,4)+ 0.008737*EXPT(V,5)) ); PG.eState := ValidLo; ELSIF V >= rValidLoBoundary AND V < 2.842 THEN PG.rPRESS := MAX( MinPressure, LREAL_TO_REAL( -0.02585 + 0.03767*V + 0.04563*EXPT(V,2)+ 0.1151*EXPT(V,3) - 0.04158*EXPT(V,4)+ 0.008737*EXPT(V,5)) ); PG.eState := Valid; ELSIF V >= 2.842 AND V < 4.945 THEN PG.rPRESS := LREAL_TO_REAL((0.1031-0.02322*V+0.07229*EXPT(V,2))/(1-0.3986*V+0.07438*EXPT(V,2)-0.006866*EXPT(V,3))); PG.eState := Valid; ELSIF V >= 4.945 AND V < rValidHiBoundary THEN PG.rPRESS := LREAL_TO_REAL((100.624-20.5623*V)/(1-0.37679*V+0.0348656*EXPT(V,2))); PG.eState := ValidHi; ELSIF V <= rDisconnectedBoundary THEN PG.eState := GaugeDisconnected; //Most likely not connected PG.rPRESS := -1; ELSE PG.eState := OoR; //Larger voltage, probably out of range? PG.rPRESS := -1; END_IF (* Protection Functions *) (* If the PG pressure is greater than the IG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the IG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) (* Pressure OK check *) PG.xPRESS_OK := (PG.rPRESS >= MinPressure); (* Setpoint evaluation *) PG.xAT_VAC := PG.xPRESS_OK AND (PG.rPRESS<=PG.rVAC_SP); (*Logger*) ACT_Logger(); (*Soft IO Mapping *) IO(); END_FUNCTION_BLOCK ACTION ACT_Logger: //STATE Logger if (PG.xLog) THEN IF ePrevState <> PG.eState THEN CASE PG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := PG.eState; END_IF END_IF END_ACTION ACTION IO: PG.i_iPRESS_R :=i_iPRESS_R; PG.sPath := sPath; END_ACTION FB_MKS317 ^^^^^^^^^ :: (* This function is for the Pirani MKS 317 connected to a 937A/B *) (* This function block is used to provide protection and automatic turn on of ion gauges, it also manages the turn on of the AT_VAC boolean, and checks to make sure the pressure is good *) FUNCTION_BLOCK FB_MKS317 Extends FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT b937A :BOOL := FALSE; // True if this gauge is connected to MKS937A controller, False if connected to MKS937B controller END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR rV : REAL; (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR VAR CONSTANT rMinPressure: REAL := 1E-4; rDefaultVAC_SP: REAL :=5E-2; // Default 50 mT rDisconnectedBoundary : REAL := 0.10; rValidLoBoundary : REAL := 0.22; rValidBoundaryMin : REAL := 0.6; rValidHiBoundary : REAL := 9.7; rValidHiBoundaryMax : REAL := 9.9; rNoSensorBoundary : REAL := 10; END_VAR (* 937B Logarithmic Output Conversion *) (* 5-20-2016, Alex Wallace and Scott Stubbs *) (* 09-28-2018, Margaret Ghaly *) (* This function will read the pressure from any gauge on a 937B. *) rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/32767; if (b937A) THEN PG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV)/0.6)-12)); //manual page 61 MKS937A ELSE PG.rPRESS := LREAL_TO_REAL(EXPT(10,(rV-7.2)/0.6)); //manual page 73 MKS937B END_IF //Default setpoint IF PG.rVAC_SP = 0 THEN PG.rVAC_SP := rDefaultVAC_SP; END_IF IF (rV <=rValidHiBoundary ) AND (rV>=rValidBoundaryMin) THEN PG.eState := Valid; // normal ELSIF rV >= rDisconnectedBoundary AND rV <= rValidLoBoundary THEN PG.eState := ValidLo; //LO PG.rPRESS := rMinPressure; ELSIF rV > rValidHiBoundary AND rV<= rValidHiBoundaryMax THEN PG.eState := ValidHi; //HIGH ELSIF rV < rDisconnectedBoundary THEN PG.eState := GaugeDisconnected; //not on PG.rPRESS := -1; ELSE PG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc PG.rPRESS := -1; END_IF (* Pressure OK check *) (* Pressure gauge OK checks *) PG.xPRESS_OK := (rV <=rValidHiBoundary ) AND (rV>=rDisconnectedBoundary); //PG.xPRESS_OK := (PG.rPRESS >= rMinPressure); (* Setpoint evaluation *) PG.xAT_VAC := (*(PG.eState =Valid)*) PG.xPRESS_OK AND PG.rPRESS <= PG.rVAC_SP; (*Logger*) ACT_Logger(); (*soft io*) IO(); END_FUNCTION_BLOCK ACTION ACT_Logger: //STATE Logger IF (PG.xLog) THEN IF ePrevState <> PG.eState THEN CASE PG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := PG.eState; END_IF END_IF END_ACTION ACTION IO: PG.i_iPRESS_R :=i_iPRESS_R; PG.sPath := sPath; END_ACTION FB_MKS317A ^^^^^^^^^^ :: (*Deprecated*) (* This function is for the Pirani MKS 317 connected to a 937A*) (* This function block is used to provide protection and automatic turn on of ion gauges, it also manages the turn on of the AT_VAC boolean, and checks to make sure the pressure is good *) FUNCTION_BLOCK FB_MKS317A Extends FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR rV : REAL; rMinPressure: REAL := 1E-3; rDefaultVAC_SP: REAL :=5E-2; // Default 50 mT rValidLoBoundary : REAL := 0.2;//Manual page 61 rValidHiBoundary : REAL := 9.7; //manual oage 61 rDisconnectedBoundary : REAL := 0.17; rNoSensorBoundary : REAL := 10; (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* 937B Logarithmic Output Conversion *) (* 5-20-2016, Alex Wallace and Scott Stubbs *) (* 09-28-2018, Margaret Ghaly *) (* This function will read the pressure from any gauge on a 937A. *) rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/32767; PG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV)/0.6)-12)); //manual page 61 937A //Default setpoint IF PG.rVAC_SP = 0 THEN PG.rVAC_SP := rDefaultVAC_SP; END_IF IF (rV <=9.7 ) AND (rV>=0.6) THEN PG.eState := Valid; // normal ELSIF rV >= 0.18 AND rV <= 0.22 THEN PG.eState := ValidLo; //LO PG.rPRESS := rMinPressure; ELSIF rV > 9.7 AND rV<= 9.9 THEN PG.eState := ValidHi; //HIGH ELSIF rV < 0.18 THEN PG.eState := GaugeDisconnected; //not on PG.rPRESS := -1; ELSE PG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc PG.rPRESS := -1; END_IF (* Pressure OK check *) (* Pressure gauge OK checks *) PG.xPRESS_OK := (rV <=rValidHiBoundary ) AND (rV>=rDisconnectedBoundary); //PG.xPRESS_OK := (PG.rPRESS >= rMinPressure); (* Setpoint evaluation *) PG.xAT_VAC := (*(PG.eState =Valid)*) PG.xPRESS_OK AND PG.rPRESS <= PG.rVAC_SP; (*Logger*) ACT_Logger(); (*soft io*) IO(); END_FUNCTION_BLOCK ACTION ACT_Logger: IF (PG.xLog) THEN //STATE Logger IF ePrevState <> PG.eState THEN CASE PG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := PG.eState; END_IF END_IF END_ACTION ACTION IO: PG.i_iPRESS_R :=i_iPRESS_R; PG.sPath := sPath; END_ACTION FB_MKS392 ^^^^^^^^^ :: (* This function block implements the MKS392 gauge*) FUNCTION_BLOCK FB_MKS392 VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} PG : ST_VG; END_VAR VAR rV : REAL; // input voltage iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* Real-value calculation *) rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; PG.rPRESS := (LREAL_TO_REAL(EXPT(10,(2*rV)-11))); (* Pressure gauge State checks *) IF rV >= 0.5 AND rV <= 3.5 THEN PG.eState := ValidLo; //LO ELSIF rV > 3.5 AND rV<= 6.5 THEN PG.eState := ValidHi; //HIGH ELSIF rV < 0.5 THEN PG.eState := GaugeDisconnected; //not on ELSE PG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc END_IF (* Pressure gauge OK checks *) PG.xPRESS_OK := (rV <=6.5 ) AND (rV>=0.5); (* Setpoint evaluation *) PG.xAT_VAC := PG.xPRESS_OK AND PG.rPRESS < PG.rVAC_SP; (*Soft IO Linking*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: PG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_MKS422 ^^^^^^^^^ :: (* This function is for the Cold Cathode MKS 422 connected to a 937A/B *) (*This function provides ILK and Set Point Protection for the Cold Cathode*) FUNCTION_BLOCK FB_MKS422 EXTENDS FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT PG : ST_VG; // Pirani Gauge Structure used to Interlock the Cold Cathode b937A :BOOL:=FALSE; // True if this gauge is connected to MKS937A controller, False if connected to MKS937B controller END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} IG : ST_VG; // The Cold Cathode Data Structure END_VAR VAR rV : REAL; timer:TON; (*IOs to be linked*) /// Controls and I/Os i_iPRESS_R AT %I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL := True; // Disable Gauge High Voltage when True // 'TcLinkTo' (EL2794) ^Output END_VAR VAR CONSTANT MinPressure: REAL := 1E-10; cDefaultPressure : REAL := 0; END_VAR (* 937B Logarithmic Output Conversion *) (* 5-20-2016, Alex Wallace and Scott Stubbs *) (* 09-28-2018, Margaret Ghaly *) (* This function will read the pressure from any gauge on a 937B. *) (*Soft IO Linking*) IO(); (*Pressure readbacks*) rV := 10*INT_TO_REAL(IG.i_iPRESS_R)/32767; if (b937A) THEN IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV)/0.6)-12)); //manual page 61 MKS937A ELSE IG.rPRESS := LREAL_TO_REAL(EXPT(10,(rV-7.2)/0.6)); //manual page 73 MKS937B END_IF (* Pressure gauge State checks *) IF (rV <=9.6 ) AND (rV>=0.6) THEN IG.eState := Valid; // normal ELSIF rV >= 0.18 AND rV <= 0.22 THEN IG.eState := ValidLo; //LO ELSIF rV > 9.6 AND rV<= 9.9 THEN IG.eState := ValidHi; //HIGH ELSIF rV < 0.18 THEN // IG.eState := GaugeDisconnected; //not on IG.rPRESS :=cDefaultPressure; ELSE IG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc IG.rPRESS :=cDefaultPressure; END_IF (* Ion Gauge Protection Functions *) (* If the PG pressure is greater than the VG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the VG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) IF (PG.rPRESS <= IG.rPRO_SP) THEN IG.q_xHV_DIS := NOT IG.xHV_SW; IG.xILKOk := TRUE; ELSIF NOT IG.q_xHV_DIS AND timer.Q THEN IF IG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) OR PG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) THEN IG.q_xHV_DIS := TRUE; IG.xHV_SW := FALSE; END_IF ELSE IG.q_xHV_DIS := TRUE; IG.xHV_SW := FALSE; IG.xILKOk := FALSE; END_IF (* Pressure gauge OK checks *) IG.xPRESS_OK := (rV <=9.6 ) AND (rV>=0.6); (* Setpoint evaluation *) IG.xAT_VAC := IG.xPRESS_OK AND IG.rPRESS < IG.rVAC_SP; (*Logger*) ACT_Logger(); (*Soft IO Linking*) // check ethercat Diagnostics IO(); timer(IN:= NOT IG.q_xHV_DIS, PT:= T#2s); END_FUNCTION_BLOCK ACTION ACT_Logger: // ILK logger IF (IG.xLog) THEN IF NOT IG.xILKOk AND NOT IG.q_xHV_DIS THEN fbLogger(sMsg:='Lost external interlock while gauge was on.', eSevr:=TcEventSeverity.Critical); END_IF // Log Action tAction(CLK:= IG.xHV_SW); IF tAction.Q THEN fbLogger(sMsg:='Ion gauge commanded to switch on', eSevr:=TcEventSeverity.Info); END_IF //STATE Logger IF ePrevState <> IG.eState THEN CASE IG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := IG.eState; END_IF END_IF END_ACTION ACTION IO: (*soft link inputs*) IG.i_iPRESS_R:= i_iPRESS_R; (*soft link outputs*) q_xHV_DIS := IG.q_xHV_DIS; IG.sPath := sPath; END_ACTION FB_MKS500 ^^^^^^^^^ :: (* This function is for the Cold Cathode MKS 500. If connected to Beckhoff EP boxes. Set the EP bit to TRUE, this is necessary for the MKS500-to-EP box interface beacuse the EP boxes do not natively support the 5v IO signals on the MKS500 gauge.*) (*This function provides ILK and Set Point Protection for the Cold Cathode*) (* 500 Logarithmic Output Conversion, factory default configuration *) FUNCTION_BLOCK FB_MKS500 EXTENDS FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT PG : ST_VG; bEP : BOOL :=FALSE; // Set to True if This Gauge is connected to EP BOX and not EL Terminals END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} IG : ST_VG; END_VAR VAR rV : REAL; GaugeTurnOnTmr : TON; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits (*IOs to be linked*) /// Controls and I/Os i_iPRESS_R AT %I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL; // Enable High Voltage when True // 'TcLinkTo' (EP2624) ^Output // only for EL and ES terminal i_xHV_ON AT %I* : BOOL; // True when High Voltage is on // 'TcLinkTo' (EL1124) ^Input i_xDisc_Active AT %I* : BOOL;// Discharge Current Active // 'TcLinkTo' (EL1124) ^Input END_VAR VAR CONSTANT // Ranges has to match the configurator software pBase : REAL :=1.0E-10;//default curve base pressure is 1E-10. Confusing since can't actually read that low using analog out. vBase : REAL := 1; vDisconnected : REAL:= 0.5; vSlope : REAL := 1.0; vGaugeOff: REAL := 9.8; vNoDischarge: REAL := 9.3; MinPressure: REAL := 1E-10; cDefaultPressure : REAL := 0; rDeadband : REAL :=0.3; END_VAR (* 500 Logarithmic Output Conversion, factory default configuration *) If (iTermBits=0) THEN iTermBits := 32767;END_IF rV := 10*INT_TO_REAL(IG.i_iPRESS_R)/iTermBits; (* Set Guage State based on the Analog voltage*) IF rV < vDisconnected THEN IG.eState := GaugeDisconnected; IG.rPRESS := cDefaultPressure; ELSIF rV >= vDisconnected AND rV < (vNoDischarge -rDeadband) THEN IG.eState := Valid; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF rV >= (vNoDischarge -rDeadband) AND rV <(vGaugeOff -rDeadband) THEN IG.eState := Starting; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF rV >= (vGaugeOff -rDeadband) THEN IG.eState := Off; //IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); IG.rPRESS := cDefaultPressure; ELSE IG.eState := OoR; //IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); IG.rPRESS := cDefaultPressure; END_IF (* Set Guage State based on the inputs HV ON and Discharge current*) If (NOT bEP) THEN IF IG.i_xHV_ON AND NOT IG.i_xDisc_Active THEN IG.eState := Starting; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF IG.i_xHV_ON AND IG.i_xDisc_Active THEN IG.eState := Valid; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF NOT IG.i_xHV_ON AND NOT IG.i_xDisc_Active THEN IG.eState := Off; IG.rPRESS := cDefaultPressure; END_IF END_IF (* Ion Gauge Protection Functions *) (* If the PG pressure is greater than the VG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the VG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) IF PG.rPRESS <= IG.rPRO_SP AND PG.xPRESS_OK THEN IG.q_xHV_DIS := IG.xHV_SW; IG.xILKOk := TRUE; ELSIF IG.q_xHV_DIS THEN IF IG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) OR PG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) THEN IG.q_xHV_DIS := FALSE; IG.xHV_SW := FALSE; END_IF ELSE IG.q_xHV_DIS := FALSE; IG.xHV_SW := FALSE; IG.xILKOk := FALSE; END_IF (* Pressure gauge OK checks *) GaugeTurnOnTmr(IN:=IG.q_xHV_DIS, PT:=T#10S, Q=>IG.xTurnOnTime); //Backwards compatibility IG.xPRESS_OK := (IG.eState = Valid) OR IG.xBAKEOUT; (* Setpoint evaluation *) IG.xAT_VAC := (IG.xPRESS_OK AND (IG.rPRESS < IG.rVAC_SP)) OR IG.xBAKEOUT AND (IG.eState = Valid) ; ACT_Logger(); (*Soft IO Linking*) // check ethercat Diagnostics IO(); END_FUNCTION_BLOCK ACTION ACT_Logger: // ILK logger IF (IG.xLog) THEN IF NOT IG.xILKOk AND IG.q_xHV_DIS THEN fbLogger(sMsg:='Lost external interlock while gauge was on.', eSevr:=TcEventSeverity.Critical); END_IF // Log Action tAction(CLK:= IG.xHV_SW); IF tAction.Q THEN fbLogger(sMsg:='Ion gauge commanded to switch on', eSevr:=TcEventSeverity.Info); END_IF //STATE Logger IF ePrevState <> IG.eState THEN CASE IG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := IG.eState; END_IF END_IF END_ACTION ACTION IO: (*soft link inputs*) IG.i_iPRESS_R:= i_iPRESS_R; IG.i_xHV_ON:= i_xHV_ON; IG.i_xDisc_Active:=i_xDisc_Active; (*soft link outputs*) q_xHV_DIS := IG.q_xHV_DIS; IG.sPath := sPath; END_ACTION FB_MKS500_EP ^^^^^^^^^^^^ :: (*Deprecated*) (* This function is for the Cold Cathode MKS 500 connected to Beckhoff EP boxes. A separate function block is necessary for the MKS500-to-EP box interface beacuse the EP boxes do not natively support the 5v IO signals on the MKS500 gauge.*) (*This function provides ILK and Set Point Protection for the Cold Cathode*) (* 500 Logarithmic Output Conversion, factory default configuration *) FUNCTION_BLOCK FB_MKS500_EP EXTENDS FB_GaugeBase VAR_IN_OUT END_VAR VAR_INPUT PG : ST_VG; END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} IG : ST_VG; END_VAR VAR rV : REAL; GaugeTurnOnTmr : TON; (*IOs to be linked*) /// Controls and I/Os i_iPRESS_R AT %I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL; // Enable High Voltage when True // 'TcLinkTo' (EP2624) ^Output // END_VAR VAR CONSTANT // Ranges has to match the configurator software pBase : REAL :=1.0E-10;//default curve base pressure is 1E-10. Confusing since can't actually read that low using analog out. vBase : REAL := 1; vDisconnected : REAL:= 0.5; vSlope : REAL := 1.0; vGaugeOff: REAL := 9.8; vNoDischarge: REAL := 9.3; MinPressure: REAL := 1E-10; cDefaultPressure : REAL := 0; END_VAR (* 500 Logarithmic Output Conversion, factory default configuration *) rV := 10*INT_TO_REAL(IG.i_iPRESS_R)/32767; (* Set Guage State based on the Analog voltage*) IF rV < vDisconnected THEN IG.eState := GaugeDisconnected; IG.rPRESS := cDefaultPressure; ELSIF rV >= vDisconnected AND rV < (vNoDischarge -0.3) THEN IG.eState := Valid; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF rV >= (vNoDischarge -0.3) AND rV <(vGaugeOff -0.3) THEN IG.eState := Starting; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSIF rV >= (vGaugeOff -0.3) THEN IG.eState := Off; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); ELSE IG.eState := OoR; IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase)))); END_IF (* Ion Gauge Protection Functions *) (* If the PG pressure is greater than the VG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the VG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) IF PG.rPRESS <= IG.rPRO_SP AND PG.xPRESS_OK THEN IG.q_xHV_DIS := IG.xHV_SW; IG.xILKOk := TRUE; ELSIF IG.q_xHV_DIS THEN IF IG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) OR PG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) THEN IG.q_xHV_DIS := FALSE; IG.xHV_SW := FALSE; END_IF ELSE IG.q_xHV_DIS := FALSE; IG.xHV_SW := FALSE; IG.xILKOk := FALSE; END_IF (* Pressure gauge OK checks *) GaugeTurnOnTmr(IN:=IG.q_xHV_DIS, PT:=T#10S, Q=>IG.xTurnOnTime); //Backwards compatibility IG.xPRESS_OK := (IG.eState = Valid) OR IG.xBAKEOUT; (* Setpoint evaluation *) IG.xAT_VAC := (IG.xPRESS_OK AND (IG.rPRESS < IG.rVAC_SP)) OR IG.xBAKEOUT AND (IG.eState = Valid) ; ACT_Logger(); (*Soft IO Linking*) // check ethercat Diagnostics IO(); END_FUNCTION_BLOCK ACTION ACT_Logger: IF (IG.xLog) THEN // ILK logger IF NOT IG.xILKOk AND NOT IG.q_xHV_DIS THEN fbLogger(sMsg:='Lost external interlock while gauge was on.', eSevr:=TcEventSeverity.Critical); END_IF // Log Action tAction(CLK:= IG.q_xHV_DIS); IF tAction.Q THEN fbLogger(sMsg:='Ion gauge commanded to switch on', eSevr:=TcEventSeverity.Info); END_IF //STATE Logger IF ePrevState <> IG.eState THEN CASE IG.eState OF ValidHi: fbLogger(sMsg:='Gauge pressure valid high.', eSevr:=TcEventSeverity.Info); ValidLo: fbLogger(sMsg:='Gauge pressure valid low.', eSevr:=TcEventSeverity.Info); Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := IG.eState; END_IF END_IF END_ACTION ACTION IO: (*soft link inputs*) IG.i_iPRESS_R:= i_iPRESS_R; (*soft link outputs*) q_xHV_DIS := IG.q_xHV_DIS; IG.sPath := sPath; END_ACTION FB_MKS722 ^^^^^^^^^ :: (* This function is for the MKS 722*) FUNCTION_BLOCK FB_MKS722 VAR_IN_OUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} VG : ST_VG; END_VAR VAR_INPUT i_rMinPressure : REAL; i_rMaxPressure : REAL; END_VAR VAR iTermBits: UINT := 32767 ; // The terminal's maximum value in bits rMinPressure: REAL; rVMin : REAL := 0.01; // Anything less than this voltage is considered disconnected rVMax : REAL := 10.5; // Anything more than this is considered invalid rV :REAL; (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input END_VAR (* Real-value calculation *) If (iTermBits=0) THEN iTermBits := 32767;END_IF rV := 10*INT_TO_REAL(VG.i_iPRESS_R)/iTermBits; // Is the gauge plugged in, and within a reasonable range? IF rV < rVMin THEN VG.eState := GaugeDisconnected; ELSIF rV > rVMax THEN VG.eState := PressInvalid; ELSIF VG.rPRESS >= rMinPressure AND VG.rPRESS <= i_rMaxPressure THEN VG.eState := Valid; ELSIF VG.rPRESS < i_rMinPressure THEN VG.eState := ValidLo; ELSE VG.eState := ValidHi; END_IF // Legacy Press OK boolean eval VG.xPRESS_OK := VG.eState >= gc_GaugeValidState; (*IO Soft Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: VG.i_iPRESS_R :=i_iPRESS_R; END_ACTION FB_MKS909 ^^^^^^^^^ :: (* Standard MKS 909 hot cathode *) (* works for 972 and 925 *) FUNCTION_BLOCK FB_MKS909 EXTENDS FB_GaugeBase VAR_INPUT PG : ST_VG; END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} IG : ST_VG; END_VAR VAR_IN_OUT END_VAR VAR rV : REAL; rMaxPressure : REAL := 5E-2; //Torr rMinPressure : REAL := 1E-10; //Torr timer:TON; (*Default set point 50 mT*) rVAC_SP: REAL := 9E-3; // as per manual iTermBits: UINT := 30518 ; // The terminal's maximum value in bits default el3174 as per vacuum architecture (*IO*) i_iPRESS_R AT%I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL := True; // Active Low END_VAR (* Standard MKS 9XX series conversion *) (* works for 972 and 925 *) //Default setpoint 50 mT IF PG.rVAC_SP = 0 THEN PG.rVAC_SP := rVAC_SP; END_IF // check no div by zero If (iTermBits=0) THEN iTermBits := 30518;END_IF rV := 10*INT_TO_REAL(PG.i_iPRESS_R)/iTermBits; (* Pressure OK check *) // Gauge state // Is the gauge plugged in, and within a reasonable range? IF (rV >= 0.1 and rV <=8.7) THEN // as per manual IG.rPRESS := LREAL_TO_REAL(EXPT(10, rV-10)); IG.eState := Valid; IG.xPRESS_OK := TRUE; //Backwards compatibility ELSIF rV > 8.7 AND rV < 10 THEN IG.rPRESS :=rMaxPressure; IG.xPRESS_OK := FALSE; IG.eState := Off; ELSE IG.xPRESS_OK := FALSE; IG.eState := GaugeDisconnected; END_IF IF (PG.rPRESS <= IG.rPRO_SP) AND (PG.xPRESS_OK) THEN PG.q_xHV_DIS := NOT IG.xHV_SW; IG.xILKOk := TRUE; ELSIF NOT IG.q_xHV_DIS AND timer.Q THEN IF IG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) OR PG.rPRESS > (IG.rPRO_SP + IG.rHYS_PR) THEN IG.q_xHV_DIS := TRUE; IG.xHV_SW := FALSE; END_IF ELSE IG.q_xHV_DIS := TRUE; IG.xHV_SW := FALSE; IG.xILKOk := FALSE; END_IF timer(IN:= NOT IG.q_xHV_DIS, PT:= T#2s); (* Setpoint evaluation *) IG.xAT_VAC := IG.xPRESS_OK AND (IG.rPRESS < IG.rVAC_SP); (*Soft IO Mapping*) ACT_IO(); (*Logger*) THIS^.ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*soft link inputs*) IG.i_iPRESS_R:= i_iPRESS_R; (*soft link outputs*) q_xHV_DIS := IG.q_xHV_DIS; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT IG.xILKOk AND NOT IG.q_xHV_DIS THEN fbLogger(sMsg:='Lost external interlock while gauge was on.', eSevr:=TcEventSeverity.Critical); END_IF // Log Action tAction(CLK:= IG.q_xHV_DIS); IF tAction.Q THEN fbLogger(sMsg:='Ion gauge commanded to switch on', eSevr:=TcEventSeverity.Info); END_IF //STATE Logger IF ePrevState <> IG.eState THEN CASE IG.eState OF ValidHi: ValidLo: Valid: fbLogger(sMsg:='Gauge pressure valid.', eSevr:=TcEventSeverity.Info); GaugeDisconnected: fbLogger(sMsg:='Gauge Disconnected.', eSevr:=TcEventSeverity.Critical); PressInvalid: fbLogger(sMsg:='Gauge pressure invalid.', eSevr:=TcEventSeverity.Warning); OoR: fbLogger(sMsg:='Gauge pressure out of range.', eSevr:=TcEventSeverity.Warning); Starting: fbLogger(sMsg:='Gauge starting.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := IG.eState; END_IF END_ACTION FB_MKS_937A ^^^^^^^^^^^ :: (* 937A Logarithmic Output Conversion *) (* 7-15-2015 *) (* This function will read the pressure from any gauge on a 937A. *) (* To be used when the specific device function block is not available *) FUNCTION_BLOCK FB_MKS_937A VAR_IN_OUT END_VAR VAR_INPUT PG : ST_VG := g_DummyVG; // Gauge Structure used to Interlock the ion gauges END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv: '} VG : ST_VG; // Vacuum Gauge Data Structure END_VAR VAR rV : REAL; (*IOs to be linked*) /// Controls and I/Os i_iPRESS_R AT %I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL; // Disable Gauge High Voltage when True // 'TcLinkTo' (EL2794) ^Output END_VAR (* 937A Logarithmic Output Conversion *) (* 7-15-2015 *) (* This function will read the pressure from any gauge on a 937A. *) rV := 10*INT_TO_REAL(VG.i_iPRESS_R)/32767; VG.rPRESS := LREAL_TO_REAL(EXPT(10,rV/0.6-12)); (* Ion Gauge Protection Functions *) (* If the PG pressure is greater than the VG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the VG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) IF PG.rPRESS < VG.rPRO_SP THEN VG.q_xHV_DIS := NOT VG.xHV_SW; ELSIF NOT VG.q_xHV_DIS THEN IF VG.rPRESS > VG.rPRO_SP + VG.rHYS_PR OR PG.rPRESS > VG.rPRO_SP + VG.rHYS_PR THEN VG.q_xHV_DIS := TRUE; END_IF ELSE VG.q_xHV_DIS := TRUE; END_IF (* Pressure gauge OK checks *) VG.xPRESS_OK := (rV <=9.6 ) AND (rV>=0.6); //legacy IF (rV <=9.6 ) AND (rV>=0.6) THEN VG.eState := Valid; // normal ELSIF rV >= 0.18 AND rV <= 0.22 THEN VG.eState := ValidLo; //LO ELSIF rV >= 9.7 AND rV<= 9.9 THEN VG.eState := ValidHi; //HIGH ELSIF rV < 0.18 THEN VG.eState := GaugeDisconnected; //not on ELSE VG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc END_IF (* Setpoint evaluation *) VG.xAT_VAC := (VG.eState =Valid) AND VG.rPRESS < VG.rVAC_SP; (*Soft IO Linking*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*soft link inputs*) VG.i_iPRESS_R:= i_iPRESS_R; (*soft link outputs*) q_xHV_DIS := VG.q_xHV_DIS; END_ACTION FB_MKS_937B ^^^^^^^^^^^ :: (* 937B Logarithmic Output Conversion *) (* 5-20-2016, Alex Wallace and Scott Stubbs *) (* This function will read the pressure from any gauge on a 937B. *) (* To be used when the specific device function block is not available *) FUNCTION_BLOCK FB_MKS_937B VAR_IN_OUT END_VAR VAR_INPUT PG : ST_VG := g_DummyVG; // Gauge Structure used to Interlock the ion gauges END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv: '} VG : ST_VG; // Vacuum Gauge Data Structure END_VAR VAR rV : REAL; (*IOs to be linked*) /// Controls and I/Os i_iPRESS_R AT %I* :INT; // input Pressure // Link to analog Input q_xHV_DIS AT %Q* : BOOL; // Disable Gauge High Voltage when True // 'TcLinkTo' (EL2794) ^Output END_VAR (* 937B Logarithmic Output Conversion *) (* 5-20-2016, Alex Wallace and Scott Stubbs *) (* This function will read the pressure from any gauge on a 937B. *) rV := 10*INT_TO_REAL(VG.i_iPRESS_R)/32767; VG.rPRESS := LREAL_TO_REAL(EXPT(10,(rV-7.2)/0.6)); (* Gauge Protection Functions *) (* If the PG pressure is greater than the VG.PRO_SP then the gauge is disabled *) (* If the PG pressure is less than the VG.PRO_SP then the gauge is enabled *) (* This FB also implements some hysteresis so the gauge doesn't have rapid power cycling while near the turn on boundary *) IF PG.rPRESS < VG.rPRO_SP THEN // might enable the gauge automatically, do we want that? VG.q_xHV_DIS := NOT VG.xHV_SW; ELSIF NOT VG.q_xHV_DIS THEN IF VG.rPRESS > VG.rPRO_SP + VG.rHYS_PR OR PG.rPRESS > VG.rPRO_SP + VG.rHYS_PR THEN VG.q_xHV_DIS := TRUE; END_IF ELSE VG.q_xHV_DIS := TRUE; END_IF (* Pressure gauge OK checks *) VG.xPRESS_OK := (rV <=9.6 ) AND (rV>=0.6); (* Pressure gauge State checks *) IF (rV <=9.7 ) AND (rV>=0.6) THEN VG.eState := Valid; // normal ELSIF rV >= 0.18 AND rV <= 0.22 THEN VG.eState := ValidLo; //LO ELSIF rV > 9.7 AND rV<= 9.9 THEN VG.eState := ValidHi; //HIGH ELSIF rV < 0.18 THEN VG.eState := GaugeDisconnected; //not on ELSE VG.eState := PressInvalid; //other fault - could be no gauge, controller powering up etc END_IF (* Setpoint evaluation *) VG.xAT_VAC := (VG.eState =Valid) AND VG.rPRESS < VG.rVAC_SP; (*Soft IO Linking*) // check ethercat Diagnostics ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*soft link inputs*) VG.i_iPRESS_R:= i_iPRESS_R; (*soft link outputs*) q_xHV_DIS := VG.q_xHV_DIS; END_ACTION FB_PIP_Gamma ^^^^^^^^^^^^ :: (* This function block does basic controls FOR the ION pump connected to a Gamma QPCe controller. Provides interlocking interface. Enable HV only when interlock gauge press is less than 1.0E-4 Torr *) {attribute 'reflection'} FUNCTION_BLOCK FB_PIP_Gamma Extends FB_Pump VAR_INPUT i_stGauge : ST_VG; //Ion or Pirani gauge for pump interlock i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum interlock logic*) END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} stPump : ST_PIP; //Gamma Ion pump structure q_IG : ST_VG; //When ion pump is used as a measuring device for interlocking gate valves END_VAR VAR_IN_OUT END_VAR VAR rPRESS : REAL; rV : REAL; timer:TON; rHVEna_SP : REAL :=1E-4; // Default protection setpoint as per the gamma QPCe manual (* IO Controls *) q_xHVEna_DO AT %Q*: BOOL; // Enable High Voltage when TRUE i_iPRESS AT %I*: INT; // i_xSP_DI AT %I* : BOOL; // NO contact //function of relay set on the QPC to HV output state // For logging tTimeOutAction : F_TRIG; tOverrideActivated : R_TRIG; tPumpStartTimeout : TON := (PT:=T#10s); // Timeout pump start if pressure < 1E-11 for more than 10s. MinPressure : REAL:= 1E-11; // Minimum readback pressure, pump must register pressure above this to be considered running //Overrides tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; tOvrd : TIME := T#10s; {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; END_VAR (* Does Gamma Ion pump HV interlock. Enable HV only when interlock gauge press is less than 1.0E-4 Torr *) (*Ensures the set point is not higher than 1.0E-4*) IF (stPump.rHVEna_SP > rHVEna_SP) THEN stPump.rHVEna_SP := rHVEna_SP; END_IF (* Interlock *) //stPump.xHV_ExtIlk := i_stGauge.xPRESS_OK AND (i_stGauge.rPRESS <= stPump.rHVEna_SP);//AND NOT tPumpStartTimeout.Q); //ADP (* Enable HV *) IF i_stGauge.rPRESS <= stPump.rHVEna_SP AND i_stGauge.xPRESS_OK THEN IF (stPump.xAutoOn) AND NOT (stPump.q_xHVEna_DO) THEN stPump.q_xHVEna_DO := TRUE; ELSE stPump.q_xHVEna_DO := stPump.xHVEna_SW OR ((tonOvrd.Q AND i_xOverrideMode)); END_IF stPump.xILKOk:= TRUE; ELSIF stPump.q_xHVEna_DO (*AND timer.Q*) THEN IF q_IG.rPRESS > (stPump.rHVEna_SP + stPump.rHYS_PR) THEN // Ion pumps when running switches off based on their own pressure readings stPump.q_xHVEna_DO := FALSE; stPump.xHVEna_SW := FALSE; stPump.xILKOk := FALSE; ELSE stPump.q_xHVEna_DO := stPump.xHVEna_SW OR ((tonOvrd.Q AND i_xOverrideMode)); END_IF ELSIF (tonOvrd.Q AND i_xOverrideMode) THEN stPump.q_xHVEna_DO := TRUE; ELSE stPump.q_xHVEna_DO := FALSE; stPump.xHVEna_SW := FALSE; stPump.xILKOk := FALSE; END_IF //stPump.q_xHVEna_DO := stPump.xHVEna_SW AND stPump.xHV_ExtIlk; //stPump.xHVEna_SW := stPump.q_xHVEna_DO; timer(IN:= NOT stPump.q_xHVEna_DO, PT:= T#1s); // TAW Experimental ion pump timeout tPumpStartTimeout(IN:=(NOT stPump.i_xHV_DI AND stPump.q_xHVEna_DO)); tTimeOutAction(CLK:=tPumpStartTimeout.Q); IF tTimeOutAction.Q THEN fbLogger(sMsg:='Pump time out.', eSevr:=TcEventSeverity.Critical); END_IF (*Pump STATE*) IF tPumpStartTimeout.Q THEN //Pump timeout stPump.eState := pumpFAULT; ELSIF stPump.q_xHVEna_DO AND NOT stPump.i_xHV_DI THEN stPump.eState := pumpSTARTING; ELSIF NOT stPump.q_xHVEna_DO AND NOT stPump.i_xHV_DI THEN stPump.eState := pumpSTOPPED; ELSIF stPump.q_xHVEna_DO AND stPump.i_xHV_DI THEN stPump.eState := pumpRUNNING; ELSIF NOT stPump.q_xHVEna_DO AND stPump.i_xHV_DI THEN stPump.eState := pumpSTOPPING; ELSE stPump.eState := pumpFAULT; END_IF (*Update output gauge values*) ACT_SetGauge(); (*Soft IO Mapping*) IO(); ACT_IlkOverride(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IlkOverride: (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the Pump permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=stPump.xILKOk, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND stPump.pv_xOvrdStart THEN stPump.pv_xOvrdStart :=FALSE; if (stPump.eState = pumpRUNNING) AND (i_xOverrideMode) THEN stPump.xHVEna_SW:= TRUE; END_IF //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF // Release the Force DO bit when the system override is false IF NOT(i_xOverrideMode) THEN stPump.pv_xOvrdStart :=FALSE; END_IF //Override timer tonOvrd(IN:= stPump.pv_xOvrdStart, PT:=tOvrd); END_ACTION ACTION ACT_Logger: // ILK logger IF NOT (stPump.xILKOk) AND ((ePrevState = pumpRUNNING) OR (ePrevState = pumpSTARTING)) AND NOT tonOvrd.Q THEN fbLogger(sMsg:='Pump turned off due to loss of interlock.', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> stPump.eState THEN CASE stPump.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); pumpSTOPPING: fbLogger(sMsg:='Pump stopping.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := stPump.eState; END_IF // Log Action tAction(CLK:= stPump.q_xHVEna_DO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Pump override mode activated', eSevr:=TcEventSeverity.Warning); END_IF // Log FAULT tFault(CLK:= NOT tPumpStartTimeout.Q); IF tFault.Q THEN fbLogger(sMsg:='Pump Fault', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION ACTION ACT_SetGauge: (*MG*) (* convert the analog input into readable pressure*) (* use logarithmic Pressure calculation, Output= Normal and Offset = 0*) rV := 10*INT_TO_REAL(i_iPress)/32767; //Manual Page 9 if (stPump.bOutputInverted) THEN rPRESS := LREAL_TO_REAL(EXPT(10,(rV-stPump.iOffset)*-1)); ELSE rPRESS := LREAL_TO_REAL(EXPT(10,(rV-stPump.iOffset))); END_IF IF (stPump.q_xHVEna_DO) THEN q_IG.rPRESS := rPRESS; stPump.rPRESS := rPRESS; END_IF IF (stPump.i_xHV_DI) AND (q_IG.rPRESS < q_IG.rVAC_SP) THEN //ADP// AND (q_IG.rPRESS<>0) AND stPump.q_xHVEna_DO) THEN q_IG.xPRESS_OK := TRUE; ELSE q_IG.xPRESS_OK := FALSE; END_IF q_IG.sPath := This^.stPump.sPath; END_ACTION ACTION IO: stPump.sIlkDeviceName := This^.i_stGauge.sPath; stPump.xError := (stPump.eState = pumpFAULT); stPump.xOverrideMode := i_xOverrideMode; (*inputs*) stPump.i_iPRESS:= i_iPRESS; stPump.i_xHV_DI:= i_xSP_DI; (*output*) q_xHVEna_DO:= stPump.q_xHVEna_DO; //According to Manual pin should be grounded to activate function This^.stPump.sPath:= sPath; END_ACTION FB_PIP_Interface_In ^^^^^^^^^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) FUNCTION_BLOCK FB_PIP_Interface_In VAR_INPUT END_VAR VAR_OUTPUT VG : ST_VG; END_VAR VAR i_rPRESS AT%I*: REAL; //This is the human-readable pressure i_xAT_VAC AT%I*: BOOL; i_xPRESS_OK AT%I*: BOOL; i_xIs_Connected AT%I*: BOOL; END_VAR (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) VG.rPRESS := i_rPRESS; VG.xAT_VAC := i_xAT_VAC; VG.xPRESS_OK := i_xPRESS_OK; IF NOT(i_xIs_Connected) THEN i_xPRESS_OK := FALSE; END_IF END_FUNCTION_BLOCK FB_PIP_Interface_Out ^^^^^^^^^^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) FUNCTION_BLOCK FB_PIP_Interface_Out VAR_INPUT VG : ST_VG; END_VAR VAR_OUTPUT END_VAR VAR i_rPRESS AT%Q*: REAL; i_xAT_VAC AT%Q*: BOOL; i_xPRESS_OK AT%Q*: BOOL; END_VAR (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) i_rPRESS:=VG.rPRESS; i_xAT_VAC:= VG.xAT_VAC; i_xPRESS_OK:=VG.xPRESS_OK; END_FUNCTION_BLOCK FB_PIP_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_PIP_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR PG : ST_VG; fb_PIP_GAMMA: FB_PIP_GAMMA; END_VAR M_INIT(); M_Interlock(); M_AutoStart(); END_FUNCTION_BLOCK FB_PressureState ^^^^^^^^^^^^^^^^ :: (*Deprecated*) FUNCTION_BLOCK FB_PressureState VAR_INPUT i_rV : REAL; i_rVMin : REAL := 0.01; // Anything less than this voltage is considered disconnected i_rVMax : REAL := 10.5; // Anything more than this is considered invalid i_IGTurnOnTime : TIME := T#10S; i_MinPressure : REAL; i_MaxPressure : REAL; END_VAR VAR_OUTPUT q_eState : E_PressureState; END_VAR VAR_IN_OUT iq_stVG : ST_VG; END_VAR VAR tonGaugeTurnOnTmr : TON; END_VAR (* Pressure State Evaluation A. Wallace, 2016-8-31 This FB serves to evaluate the state of the pressure based on the raw voltage, pressure, and gauge state. *) // Is the gauge plugged in, and within a reasonable range? IF i_rV < i_rVMin THEN q_eState := GaugeDisconnected; ELSIF i_rV > i_rVMax THEN q_eState := PressInvalid; ELSE // Is the gauge reading back a pressure that makes sense? CASE iq_stVG.eTYPE OF IG903: tonGaugeTurnOnTmr(IN:=iq_stVG.q_xHV_DIS, PT:=i_IGTurnOnTime, Q=>iq_stVG.xTurnOnTime); IF iq_stVG.q_xHV_DIS THEN IF tonGaugeTurnOnTmr.Q THEN IF iq_stVG.rPRESS >= i_MinPressure AND iq_stVG.rPRESS <= i_MaxPressure THEN q_eState := Valid; ELSE q_eState := OoR; END_IF ELSE q_eState := Starting; END_IF ELSE q_eState := Off; END_IF IG909: IF iq_stVG.q_xHV_DIS THEN IF iq_stVG.rPRESS >= i_MinPressure AND iq_stVG.rPRESS <= i_MaxPressure THEN q_eState := Valid; ELSE q_eState := OoR; END_IF ELSE q_eState := Off; END_IF PG907, PG925, PG722B: IF iq_stVG.rPRESS >= i_MinPressure AND iq_stVG.rPRESS <= i_MaxPressure THEN q_eState := Valid; ELSIF iq_stVG.rPRESS < i_MinPressure THEN q_eState := ValidLo; ELSE q_eState := ValidHi; END_IF ELSE q_eState := PressInvalid; END_CASE END_IF iq_stVG.eState := q_eState; // Legacy Press OK boolean eval iq_stVG.xPRESS_OK := q_eState >= gc_GaugeValidState; END_FUNCTION_BLOCK FB_PTM_Agilent ^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Agilent TV Turbo pump. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_PTM_Agilent EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_xExtILKOk : BOOL; // Connect to external interlock logic(e.g TURBO_ILK Function), TRUE if not used. i_stGauge : ST_VG; //Pirani backing gauge for pump interlock i_rMaxBackingPressure : REAL; END_VAR VAR_OUTPUT {attribute 'pytmc' :=' pv:'} iq_stPtm : ST_AgilentPTM; END_VAR VAR {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; //xRunOk : BOOL; nMaxR1Fault: INT :=5; nR1Fault : INT; tFaultWindowDuration : TIME := T#300S; tR1FaultDuration : TIME := T#120S; tTimeOutDuration: TIME:= T#10M; tFaultWindowElapsed: TON; tR1Fault : TON; tStartTimeOut : TON; tR1TimeOut: TON; step : INT :=0; nErrorCode: INT :=0; xBackingPressureOK: BOOL; iMaxSpeed : INT:=963; rPowerScale: REAL:=10;//W rCurrentScale: REAL:=1;//A (*I/Os*) i_xSTART AT%I*: BOOL; i_xR1 AT%I* : BOOL; i_xR2 AT%I* : BOOL; i_xLSpd AT%I*: BOOL; i_iTempMon AT%I*: INT; i_iPowerMon AT%I*: INT; i_iCurrentMon AT%I*: INT; i_iRawSpeed AT %I* : INT; i_xFault AT%I* : BOOL; q_RunDO AT%Q* : BOOL; q_xLSpd AT%Q* : BOOL; // For logging // Msg : FB_FormatString; // fbJson : FB_JsonSaxWriter; tOverrideActivated : R_TRIG; iTermBits: UINT := 32767 ; // The terminal's maximum value in bits END_VAR (* Basic Agilent Turbo Controls *) (* M. Ghaly, 2018-9-26 *) (*connect External interlock to structure variable*) //iq_stPtm.xExtRunOk := i_xExtILKOk; (* state machine *) CASE (step) OF (*Pump Stopped State*) 0: // Pump Stopped State iq_stPtm.xRunSW:=FALSE; //Reset Output signals; iq_stPtm.q_RunDO:=FALSE; iq_stPtm.q_xSS := FALSE; iq_stPtm.q_xLSpd:= FALSE; //Reset timer tFaultWindowElapsed.IN := FALSE; nR1Fault := 0; iq_stPtm.eState := pumpSTOPPED; step :=10; (*Idle State waiting to start*) 10: // Idle State, waiting for the xRunSwitch IF iq_stPtm.xExtRunOk AND iq_stPTM.xRunSW THEN iq_stPtm.q_RunDO := iq_stPTM.xRunSW; // step:=20; END_IF (*Pump Starting State*) 20: // Pump is starting nErrorCode:=0; iq_stPtm.q_RunDO := iq_stPTM.xRunSW; // done in previous state IF (*(NOT iq_stPtm.i_xR1) AND*)iq_stPtm.i_xSTART THEN iq_stPtm.eState := pumpSTARTING; step:= 40; ELSIF tStartTimeOut.Q THEN step:=90; // or fault state nErrorCode:=950; END_IF // should check R1 ?? check Fault condition? 40: // Pump is starting iq_stPtm.eState := pumpSTARTING; IF NOT iq_stPtm.i_xR1 AND NOT iq_stPtm.i_xSTART THEN step := 0 ; ELSIF NOT iq_stPtm.i_xR1 AND iq_stPtm.i_xSTART THEN step :=40; ELSIF iq_stPtm.i_xR1 AND NOT iq_stPtm.i_xSTART THEN step:= 50; END_IF IF tR1TimeOut.Q THEN step:= 90; iq_stPtm.xPumpFaultLock := TRUE; nErrorCode:= 930; // R1 Signal at Start time out. // fbLogger(sMsg:='Timeout: Pump could not achieve running speed.', eSevr:=TcEventSeverity.Warning); END_IF 50: // Pump is Running (Normal Operation) ( R1 is True and STARTing is FALSE) iq_stPtm.eState := pumpRUNNING; IF NOT iq_stPtm.i_xR1 THEN nR1Fault:= nR1Fault+1 ; tFaultWindowElapsed.IN := TRUE; /// //tR1Fault.IN := TRUE; step :=60; END_IF //Log new state IF ePrevState <> iq_stPtm.eState THEN //fbLogger(sMsg:='Pump at speed.', eSevr:=TcEventSeverity.Verbose); ePrevState := iq_stPtm.eState; END_IF 60: // Pump is Running (Not Nomral Operation) (R1 is False, pump not @ speed ) IF ((nR1Fault = nMaxR1Fault) AND NOT tFaultWindowElapsed.Q) OR (tR1Fault.Q) THEN // number of fault occured during the same time window step:= 90; tR1Fault.IN := FALSE; tFaultWindowElapsed.IN := FALSE; iq_stPtm.xPumpFaultLock := TRUE; nErrorCode:= 920; // R1 Signal Lost multiple times. //fbLogger(sMsg:='Running signal Lost multiple times.', eSevr:=TcEventSeverity.Warning); ELSIF tFaultWindowElapsed.Q THEN tFaultWindowElapsed.IN := FALSE; nR1Fault := 0; ELSIF iq_stPtm.i_xR1 THEN tR1Fault.IN := FALSE; step:=50; END_IF 90: // Pump Fault STATE iq_stPtm.xRunSW:=FALSE; iq_stPtm.q_RunDO:=FALSE; iq_stPtm.q_xSS := FALSE; iq_stPtm.q_xLSpd:= FALSE; iq_stPtm.eState := pumpFAULT; step := 95; 95: IF (NOT iq_stPtm.xPumpFaultLock) OR iq_stPtm.xRunSW THEN step:=0; END_IF END_CASE (* Interrupts *) (* FAULT signal*) IF (iq_stPtm.i_xFault) AND (step < 90 ) THEN iq_stPtm.eState := pumpFAULT; // no need taken care of in the state iq_stPtm.xPumpFaultLock := TRUE; nErrorCode := 900; // Pump Controller FAULT step:=90; END_IF (*Pump Lock Reset bit*) IF (iq_stPtm.xResetSW ) THEN iq_stPtm.xResetSW := FALSE; iq_stPtm.xPumpFaultLock := FALSE; END_IF (*Check backing gauge pressure*) //xBackingPressureOK := i_stGauge.xPRESS_OK AND ( i_stGauge.rPRESS < iq_stPtm.rBackingPressureSP); (* Interlock Sum *) iq_stPtm.xExtRunOk := i_xExtILKOk AND NOT iq_stPtm.i_xFault AND NOT iq_stPtm.xPumpFaultLock AND NOT(tILK.Q); IF NOT iq_stPtm.xExtRunOk AND (NOT (step >= 90)) THEN step:=0; END_IF (*Stop By the Operator and not due to fault *) IF (NOT iq_stPtm.xRunSW) AND (NOT (step >= 90)) AND (NOT (step = 10)) THEN step := 0; END_IF // Log faults Moved to ACT_Logger (*tErrorPresent(CLK:=iq_stPtm.xPumpFaultLock); IF tErrorPresent.Q THEN fbJson.StartObject(); fbJson.AddKey('vacuum_typecode'); fbJson.AddUdint(nErrorCode); fbJson.EndObject(); fbLogger.sJson := fbJson.GetDocument(); fbLogger(sMsg:='Turbo control fault', eSevr:=TcEventSeverity.Warning); fbJson.ResetDocument(); END_IF *) (* Timers *) tFaultWindowElapsed(PT:=tFaultWindowDuration); tR1Fault(IN:= (step = 60), PT:=tR1FaultDuration); tStartTimeOut(IN := (step=20), PT:=tTimeOutDuration); tR1TimeOut(IN := (step=40), PT:=tTimeOutDuration); (* IO Mapping*) IO(); (*Assign Error Message*) iq_stPtm.sError := ErrorMessage(nErrorCode, iq_stPtm.eState); // Log States and triggers ACT_Logger(); (*Validate Backing Pressure set point doesn't exceed the Maximum backingPressure*) iq_stPtm.rBackingPressureSP := BackingPressureSetPoint(iq_stPtm.rBackingPressureSP,i_rMaxBackingPressure); ACT_Interlock(); END_FUNCTION_BLOCK ACTION ACT_Interlock: This^.tILK(IN:= (i_stGauge.rPRESS>=iq_stPtm.rBackingPressureSP), PT:=T#5S, Q=> ); END_ACTION ACTION ACT_Logger: //STATE Logger // ILK logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= iq_stPTM.i_xFault OR iq_stPtm.xPumpFaultLock); IF tFault.Q THEN fbLogger(sMsg:=iq_stPtm.sError, eSevr:=TcEventSeverity.Critical); END_IF END_ACTION ACTION IO: (*inputs*) iq_stPtm.i_xSTART:= i_xSTART; iq_stPtm.i_xR1:= i_xR1; iq_stPtm.i_xR2:= i_xR2; iq_stPtm.i_xLSpd:= i_xLSpd; iq_stPtm.i_xFault:= i_xFault; (* Real-value calculation *) If (iTermBits=0) THEN iTermBits := 32767;END_IF iq_stPtm.i_rCurrentMon := (10*INT_TO_REAL(i_iCurrentMon)/iTermBits)*rCurrentScale; iq_stPtm.i_rTempMon := 10*INT_TO_REAL(i_iTempMon)/iTermBits; iq_stPtm.i_rPowerMon := (10*INT_TO_REAL(i_iPowerMon)/iTermBits)*rPowerScale; iq_stPtm.i_diCurSpd := 10*LREAL_TO_DINT(INT_TO_REAL(i_iRawSpeed)/iTermBits); (*outputs*) q_RunDO := iq_stPtm.q_RunDO; q_xLSpd := iq_stPtm.q_xLSpd; END_ACTION FB_PTM_Ebara_010M ^^^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Ebara Turbo pump connected to the ETC010M Controller. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) {attribute 'no_check'} FUNCTION_BLOCK FB_PTM_Ebara_010M EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_xExtILKOk : BOOL; // Turbo ILK bit , set to True if not used //i_iRawSpeed: INT; //Should come from PLC Soft IO. i_rMaxBackingPressure : REAL := 3; i_iMinSpeed : DINT := 100; //Hz check pump manual for setting P43 i_iMaxSpeed : DINT := 560; //Hz check pump manual for setting P43 END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPTM : ST_EbaraPTM; END_VAR VAR i_iADCBits : UINT := 15; RTRIG_INLK: R_TRIG; TOF_RESET: TON :=(PT:=T#1S); TOF_SetSpeed: TON :=(PT:=T#1S); (*IO*) (*inputs*) i_xDecel AT %I* : BOOL; //Link to brake input i_xAccel AT %I* : BOOL; i_xRotate AT %I* : BOOL; i_xNCFault AT %I* : BOOL;// i_xAtSpd AT %I* : BOOL; i_iRawSpeed AT %I* : INT; // Link to Analog input i_iTempMon AT %I* : INT; // Link to Analog input -- input Voltage according to the pump temprature 0->5V 0->100C i_iCurrentMon AT %I* : INT; //Link to Analog -- input Voltage to the output current of motor 0->5V 0->10A (*output*) q_xStart AT %Q* : BOOL; // link to output q_xStop AT %Q* : BOOL; //Link to output q_xReset AT %Q* : BOOL; q_xProtection AT %Q* : BOOL; q_xSetSpeed AT %Q* : BOOL; q_iSpeedSet AT %Q* :INT; //Link to analog Output END_VAR (* Ebara ETC control FB Scott Stubbs & Alex W. Modified Nov 2018 by M. Ghaly *) (* The pump runs when the RunDO is set to true. The RunDO sets the outputs Start and Stop to true when it is true, and false otherwise. Per the manual, when START bit is closed the pump will accelerate, and when the STOP bit is Opened, the pump will decelerate with the brake and stop *) (* Simple protection reset *) iq_stPtm.xExtRunOk:= i_xExtILKOk AND NOT iq_stPTM.i_xFault; iq_stPTM.q_xProtection:= TRUE;//iq_stPTM.xExtRunOk; (*soft IO mapping*) ACT_IO(); (* Basic pump supervisory section *) (* If override mode, ignore everything else *) IF iq_stPTM.i_xOverride THEN IF iq_stPTM.xRunSW THEN iq_stPTM.q_RunDO:=TRUE; ELSE iq_stPTM.q_RunDO:=FALSE; END_IF //Handle faults ELSIF iq_stPTM.i_xFault OR ( NOT iq_stPTM.xExtRunOk) THEN iq_stPTM.q_RunDO:=FALSE; iq_stPTM.xRunSW:=FALSE; //And one for when we need to start the pump normally. Only allows pump to be started if ILK ok. ELSIF iq_stPTM.xRunSW AND iq_stPTM.xExtRunOk THEN iq_stPTM.q_RunDO:=TRUE; END_IF //One section for when pump is told to stop IF NOT iq_stPTM.xRunSW THEN iq_stPTM.q_RunDO:=FALSE; //IF (NOT iq_stPTM.i_xFault)(* AND (NOT iq_stPTM.i_xALARM) *)THEN iq_stPTM.eState := pumpSTOPPED; END_IF; END_IF //Set the Start and Stop outputs to Run do . check manual alternate START/STOP iq_stPTM.q_xStart := iq_stPTM.q_RunDO; iq_stPTM.q_xStop := iq_stPTM.q_RunDO; (* to reset the Input speed OK bit *) TOF_SetSpeed (IN:= iq_stPTM.iq_xSpeedSet, Q => ); IF (TOF_SetSpeed.Q) THEN iq_stPTM.iq_xSpeedSet:= FALSE; END_IF (* to reset the Reset Bit *) IF (iq_stPTM.xResetSW ) THEN iq_stPTM.q_xReset := TRUE; END_IF TOF_RESET (IN:= iq_stPTM.q_xReset, Q => ); IF TOF_RESET.Q THEN iq_stPTM.q_xReset:= FALSE; iq_stPTM.xResetSW := FALSE; END_IF (*When the Protection signal is lost, Error bit doesn't show, but the controller still needs to be reset*) RTRIG_INLK(CLK:= iq_stPTM.q_xProtection ); IF (RTRIG_INLK.Q) AND (iq_stPTM.i_xNCFault) THEN iq_stPTM.q_xReset := TRUE; END_IF (*Validate Backing Pressure set point doesn't exceed the Maximum backingPressure*) iq_stPtm.rBackingPressureSP := BackingPressureSetPoint(iq_stPtm.rBackingPressureSP,i_rMaxBackingPressure); (*Pump STATE*) IF iq_stPtm.i_xFault THEN iq_stPtm.eState := pumpFAULT; ELSIF iq_stPTM.i_xDecel AND NOT iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTOPPING; ELSIF NOT iq_stPtm.q_RunDO AND NOT iq_stPTM.i_xFault THEN iq_stPtm.eState := pumpSTOPPED; ELSIF NOT iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO AND iq_stPTM.i_xAccel THEN iq_stPtm.eState := pumpSTARTING; ELSIF iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpRUNNING; ELSE iq_stPtm.eState := pumpFAULT; END_IF (*soft IO mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*soft io mapping*) (*inputs*) iq_stPTM.i_xDecel := i_xDecel ; iq_stPTM.i_xAccel := i_xAccel ; iq_stPTM.i_xRotate := i_xRotate; iq_stPTM.i_xFault := NOT iq_stPTM.i_xNCFault; //Normally closed fault handling iq_stPTM.i_xNCFault := i_xNCFault ; iq_stPTM.i_xALARM:= NOT i_xNCFault; iq_stPTM.i_xAtSpd := i_xAtSpd; iq_stPTM.i_rTempMon := (100*(INT_TO_REAL(i_iTempMon)/16383));// Manual page 28(DC 0 to 5V -> 0 to 100 DegC) iq_stPTM.i_rCurrentMon := (10*(INT_TO_REAL(i_iCurrentMon)/16383));// Manual page 28 (DC 0 to 5V -> 0 to 10 A) iq_stPTM.i_diCurSpd := LREAL_TO_DINT(560*LREAL_TO_DINT(i_iRawSpeed)/16383);//(560*(INT_TO_REAL(i_iRawSpeed)/16383));// Manual page 28 (DC 0 to 5V -> 100 to 560Hz Rated Rotational Speed ) (*outputs*) q_xStart:= iq_stPTM.q_xStart; q_xStop := iq_stPTM.q_xStop; q_xReset := iq_stPTM.q_xReset; q_xProtection := iq_stPTM.q_xProtection; q_xSetSpeed := iq_stPTM.iq_xSpeedSet; (*Validate Set Speed within range *) IF iq_stPTM.q_iSpeedSet >= i_iMinSpeed AND iq_stPTM.q_iSpeedSet <=i_iMaxSpeed THEN q_iSpeedSet := LIMIT(0, DINT_TO_INT((iq_stPTM.q_iSpeedSet - i_iMinSpeed) / i_iMaxSpeed *16383/5),16383); //Max 5V END_IF END_ACTION ACTION ACT_Logger: IF NOT i_xExtIlkOK AND (ePrevState = pumpRUNNING OR ePrevState = pumpSTARTING) THEN fbLogger(sMsg:='Pump turned off due to loss of interlock.', eSevr:=TcEventSeverity.Critical); END_IF // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF //STATE Logger IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); pumpSTOPPING: fbLogger(sMsg:='Pump decelerating.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // Log FAULT tFault(CLK:= iq_stPTM.i_xNCFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_PTM_Ebara_011M ^^^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Ebara Turbo pump connected to the ETC011M Controller. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) {attribute 'no_check'} FUNCTION_BLOCK FB_PTM_Ebara_011M EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_rMaxBackingPressure : REAL := 3; i_iMinSpeed : DINT := 100; //check pump manual for setting i_iMaxSpeed : DINT := 560; // check pump manual for setting i_xExtILKOk : BOOL; // Connect to external interlock logic, TRUE if not used. END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPTM : ST_EbaraPTM; END_VAR VAR TOF_SetSpeed: TON := (PT:=T#1S); TOF_RESET: TON :=(PT:=T#1S); i_iADCBits : UINT := 12; (*IO*) (*inputs*) i_xDecel AT %I* : BOOL; //Link to brake input i_xAccel AT %I* : BOOL; i_xRotate AT %I* : BOOL; i_xFaultNC AT %I* : BOOL;// remove? i_xAtSpd AT %I* : BOOL; i_iRawSpeed AT %I* : INT; // Link to Analog input (*output*) q_xStart AT %Q* : BOOL; // link to output q_xStop AT %Q* : BOOL; //Link to output q_xReset AT %Q* : BOOL; q_xProtection AT %Q* : BOOL; q_xSetSpeed AT %Q* : BOOL; q_iSpeedSet AT %Q* :INT; //Link to analog Output END_VAR (* Ebara ETC control FB Scott Stubbs & Alex W. Modified Nov 2018 by M. Ghaly *) (* Switch processing *) //TOF_START.IN := iq_stPTM.i_xStart; //TOF_START(); //TOF_STOP.IN := iq_stPTM.i_xStop; //TOF_STOP(); //TOF_RESET.IN := iq_stPTM.i_xReset; //TOF_RESET(); (* Simple protection reset *) iq_stPtm.xExtRunOk:= i_xExtILKOk; iq_stPTM.q_xProtection := TRUE;//iq_stPTM.xExtRunOk; (*soft IO mapping*) ACT_IO(); (* Basic pump supervisory section *) (* If override mode, ignore everything else *) IF iq_stPTM.i_xOverride THEN IF iq_stPTM.xRunSW THEN iq_stPTM.q_RunDO:=TRUE; ELSE iq_stPTM.q_RunDO:=FALSE; END_IF //Handle faults - the way the Ebara cable is done, xFault is normally closed ELSIF iq_stPTM.i_xFault OR (iq_stPTM.q_RunDO AND NOT iq_stPTM.xExtRunOk) THEN iq_stPTM.q_RunDO:=FALSE; //iq_stPTM.q_xProtection:=FALSE; iq_stPTM.xRunSW:=FALSE; //And one for when we need to start the pump normally. Only allows pump to be started if ILK ok. ELSIF iq_stPTM.xRunSW AND iq_stPTM.xExtRunOk THEN iq_stPTM.q_RunDO:=TRUE; END_IF //One section for when pump is told to stop IF NOT iq_stPTM.xRunSW THEN iq_stPTM.q_RunDO:=FALSE; IF (NOT iq_stPTM.i_xFault) AND (NOT iq_stPTM.i_xALARM) THEN iq_stPTM.eState := pumpSTOPPED; END_IF; END_IF //Set the Start and Stop outputs to Run do . check manual. iq_stPTM.q_xStart := iq_stPTM.q_xStop := iq_stPTM.q_RunDO; (* to reset the Input speed OK bit *) TOF_SetSpeed (IN:= iq_stPTM.iq_xSpeedSet, Q => ); IF TOF_SetSpeed.Q THEN iq_stPTM.iq_xSpeedSet:= FALSE; END_IF (* to reset the Reset Bit *) IF (iq_stPTM.xResetSW ) THEN iq_stPTM.q_xReset := TRUE; END_IF TOF_RESET (IN:= iq_stPTM.q_xReset, Q => ); IF TOF_RESET.Q THEN iq_stPTM.q_xReset:= FALSE; iq_stPTM.xResetSW := FALSE; END_IF (*Pump STATE*) IF iq_stPtm.i_xFault THEN iq_stPtm.eState := pumpFAULT; ELSIF iq_stPTM.i_xDecel AND NOT iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTOPPING; ELSIF NOT iq_stPtm.q_RunDO AND NOT iq_stPTM.i_xFault THEN iq_stPtm.eState := pumpSTOPPED; ELSIF NOT iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTARTING; ELSIF iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpRUNNING; ELSE iq_stPtm.eState := pumpFAULT; END_IF (*Validate Backing Pressure set point doesn't exceed the Maximum backingPressure*) iq_stPtm.rBackingPressureSP := BackingPressureSetPoint(iq_stPtm.rBackingPressureSP,i_rMaxBackingPressure); (*soft IO mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*soft io mapping*) (*inputs*) iq_stPTM.i_xDecel := i_xDecel ; iq_stPTM.i_xAccel := i_xAccel ; iq_stPTM.i_xRotate := i_xRotate; iq_stPTM.i_xNCFault := i_xFaultNC ; iq_stPTM.i_xAtSpd := i_xAtSpd; //iq_stPTM.i_iRawSpeed := i_iRawSpeed; //V (AI/32767*10) * (33600 {Max RPM} - 100) / 5 (*outputs*) q_xStart:= iq_stPTM.q_xStart; q_xStop := iq_stPTM.q_xStop; q_xReset := iq_stPTM.q_xReset; q_xProtection := iq_stPTM.q_xProtection; q_xSetSpeed := iq_stPTM.iq_xSpeedSet; (*Validate Set Speed within range *) IF iq_stPTM.q_iSpeedSet >= i_iMinSpeed AND iq_stPTM.q_iSpeedSet <=i_iMaxSpeed THEN q_iSpeedSet := LIMIT(0, DINT_TO_INT((iq_stPTM.q_iSpeedSet - i_iMinSpeed) / i_iMaxSpeed *16383/5),16383); //Max output 5V //:= DINT_TO_INT((iq_stPTM.q_iSpeedSet-100) / 33600 *32767/2); //Max 5V END_IF (* Pump speed calculation *) IF i_iRawSpeed / (EXPT(2,i_iADCBits)-1) * 10 >= 0.2 THEN //Speed reading appears zero at ~0.16V iq_stPTM.i_diCurSpd := LREAL_TO_DINT(560*LREAL_TO_DINT(i_iRawSpeed)/16383); //iq_stPTM.i_diCurSpd := LREAL_TO_DINT(i_iRawSpeed/(EXPT(2,i_iADCBits)-1)*67000); //V (AI/(2^{# ADC bits} -1 ) * 10[ADC 10V scaling]) * (33600 [Max RPM] - 100) / 5 ELSE iq_stPTM.i_diCurSpd := 0; END_IF //Normally closed fault handling iq_stPTM.i_xFault := NOT iq_stPTM.i_xNCFault; //?? END_ACTION ACTION ACT_Logger: //STATE Logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); pumpSTOPPING: fbLogger(sMsg:='Pump decelerating.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // ILK logger // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= iq_stPTM.i_xNCFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_PTM_MagDriveDigital ^^^^^^^^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Leybold connected to the MagDriveDigital Controller. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_PTM_MagDriveDigital EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_xExtILKOk : BOOL; // Connect to external interlock logic, TRUE if not used. END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPtm : ST_LeyboldPTM; END_VAR VAR xRunOk : BOOL; tofRemoteDelay : TOF; (*IO*) i_xAtSpd AT%I*: BOOL; // Normaml operation when true i_xFault AT%I*: BOOL; // Error NC i_xAccel AT%I*: BOOL; i_xDecel AT%I*: BOOL; i_xWarn AT%I*: BOOL; q_RunDO AT%Q*: BOOL; // Start/Stop q_xRemote AT%Q*: BOOL; END_VAR (* Basic MagDrive Digital Turbo Controls *) (* A. Wallace, 2015-7-15 *) tofRemoteDelay(IN:=iq_stPtm.q_RunDO, PT:=T#5S); iq_stPtm.q_xRemote := tofRemoteDelay.Q; (* Interlock Sum *) iq_stPtm.xExtRunOk:= i_xExtILKOk; xRunOk := iq_stPtm.xExtRunOk AND NOT iq_stPtm.i_xFault; (* Basic pump supervisory section *) IF xRunOk THEN iq_stPtm.q_RunDO := iq_stPTM.xRunSW; ELSE iq_stPtm.xRunSW:=FALSE; iq_stPtm.q_RunDO:=FALSE; END_IF (*Pump STATE*) IF iq_stPtm.i_xFault THEN iq_stPtm.eState := pumpFAULT; ELSIF iq_stPTM.i_xDecel AND NOT iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTOPPING; ELSIF NOT iq_stPtm.q_RunDO AND NOT iq_stPTM.i_xFault THEN iq_stPtm.eState := pumpSTOPPED; ELSIF NOT iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO AND iq_stPTM.i_xAccel THEN iq_stPtm.eState := pumpSTARTING; ELSIF iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpRUNNING; ELSE iq_stPtm.eState := pumpFAULT; END_IF (*I/O soft mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*Inputs*) iq_stPtm.i_xAtSpd := i_xAtSpd; iq_stPtm.i_xFault := NOT i_xFault; iq_stPtm.i_xAccel := i_xAccel; iq_stPtm.i_xDecel := i_xDecel; iq_stPtm.i_xWarn := i_xWarn; (*Outputs*) q_RunDO := iq_stPtm.q_RunDO; q_xRemote := iq_stPtm.q_xRemote; // remote out END_ACTION ACTION ACT_Logger: //STATE Logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // ILK logger // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= NOT iq_stPTM.i_xFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_PTM_Pfeiffer ^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Pfeiffer Turbo pump connected to the TM700 and TC400 Controllers. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_PTM_Pfeiffer EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_xExtIlkOK : BOOL; // Connect to external interlock logic, TRUE if not used. END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPTM : ST_PfeifferPTM; END_VAR VAR TOF_RESET: TON :=(PT:=T#1S); (*IO*) i_xAtSpd AT%I*: BOOL; // Normaml operation when true i_xFaultNC AT%I*: BOOL; // Error i_xWarn AT%I*: BOOL; i_xRemote AT%I*: BOOL; q_RunDO AT%Q*: BOOL; // Start/Stop for TC110 link this output to the Pumping station bit q_PumpingStation AT%Q*: BOOL; // Start/Stop interlock q_xRemote AT%Q*: BOOL; q_xReset AT%Q*: BOOL; END_VAR (* Basic pump supervisory section *) q_xRemote := TRUE; (* Interlock Sum *) iq_stPtm.xExtRunOk:= i_xExtILKOk; //For compatability with TC110 (*IF iq_stPTM.xExtRunOk THEN q_PumpingStation:= TRUE; ELSE q_PumpingStation:= FALSE; END_IF*) IF iq_stPTM.xExtRunOk AND NOT (iq_stPTM.i_xFault) (*OR iq_stPTM.i_xTempFault)*) THEN iq_stPTM.q_RunDO:=iq_stPTM.xRunSW; q_PumpingStation:=iq_stPTM.xRunSW; ELSE iq_stPTM.xRunSW:=FALSE; iq_stPTM.q_RunDO:=FALSE; q_PumpingStation:=FALSE; END_IF (*Pump STATE*) IF iq_stPtm.i_xFault THEN iq_stPtm.eState := pumpFAULT; ELSIF NOT iq_stPtm.q_RunDO AND NOT iq_stPTM.i_xFault THEN iq_stPtm.eState := pumpSTOPPED; ELSIF NOT iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTARTING; ELSIF iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpRUNNING; ELSE iq_stPtm.eState := pumpFAULT; END_IF (*Error Reset*) (* to reset the Reset Bit *) IF (iq_stPTM.xResetSW ) THEN iq_stPTM.q_xReset := TRUE; END_IF TOF_RESET (IN:= iq_stPTM.q_xReset, Q => ); IF TOF_RESET.Q THEN iq_stPTM.q_xReset:= FALSE; iq_stPTM.xResetSW := FALSE; END_IF (*Soft IO Mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*outputs*) q_RunDO := iq_stPTM.q_RunDO; q_xReset := iq_stPTM.q_xReset; //q_xRemote := iq_stPTM. //q_PumpingStation AT%Q*: BOOL; // Start/Stop (*inputs*) iq_stPTM.i_xAtSpd:= i_xAtSpd; iq_stPTM.i_xFault:= NOT i_xFaultNC; iq_stPTM.i_xWarn:= i_xWarn ; END_ACTION ACTION ACT_Logger: //STATE Logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // ILK logger // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= NOT iq_stPTM.i_xFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_PTM_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_PTM_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR fb_TwisTorr: FB_PTM_TwisTorr; fb_PTM_Ebara_010M:FB_PTM_Ebara_010M; cycle:INT :=0; (*IO*) q_iSpeedSet AT %I* :INT; i_diCurSpd AT %Q* :INT; i_iRawSpeed AT %Q* : INT; // Link to Analog input i_iTempMon AT %Q* : INT; // Link to Analog input -- input Voltage according to the pump temprature 0->5V 0->100C i_iCurrentMon AT %Q* : INT; //Link to Analog -- input Voltage to the output current of motor 0->5V 0->10A END_VAR M_INIT(); M_Interlock(); M_PTM_EBARA(); cycle:=cycle+1; END_FUNCTION_BLOCK FB_PTM_TurboDrive ^^^^^^^^^^^^^^^^^ :: (* This Function block provides basic turbo control for Leybold Turbo Drive 300, Turbo Drive 400 *) (* TD20 Classic Via Remote X1 Connector 9-way PLC interface*) (* When serial interface is implemented, call Method M_Serial_IO after fb instantiation, in order to add the serial status *) FUNCTION_BLOCK FB_PTM_TurboDrive EXTENDS FB_Pump VAR_IN_OUT END_VAR VAR_INPUT i_xExtILKOk : BOOL; // Connect to external interlock logic, TRUE if not used. END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPtm : ST_LeyboldPTM; END_VAR VAR xRunOk : BOOL; tofRemoteDelay : TOF; (*IO*) i_xAtSpd AT%I*: BOOL; // Normaml operation when true i_xFaultNC AT%I*: BOOL; // Error Active when no Error is present q_RunDO AT%Q*: BOOL; // Start/Stop END_VAR (* Basic Turbo Control *) (* This Function block provides basic turbo control for Leybold Turbo Drive 300, Turbo Drive 400 *) (* TD20 Classic Via Remote X1 Connector 9-way PLC interface*) (* M. Ghaly Jan. 2019 *) (* Interlock Sum *) iq_stPtm.xExtRunOk:= i_xExtILKOk; xRunOk := iq_stPtm.xExtRunOk AND iq_stPtm.i_xNCError; (* Basic pump supervisory section *) IF xRunOk THEN iq_stPtm.q_RunDO := iq_stPTM.xRunSW; ELSE iq_stPtm.xRunSW:=FALSE; iq_stPtm.q_RunDO:=FALSE; END_IF (*Pump States*) IF (iq_stPtm.i_xFault) THEN iq_stPtm.eState := pumpFAULT; iq_stPtm.xRunSW:=FALSE; iq_stPtm.q_RunDO:=FALSE; ELSIF (iq_stPtm.q_RunDO) AND (iq_stPtm.i_xAtSpd) THEN iq_stPtm.eState := pumpRUNNING; ELSIF (iq_stPtm.q_RunDO) AND NOT (iq_stPtm.i_xAtSpd) THEN iq_stPtm.eState := pumpSTARTING; ELSIF NOT (iq_stPtm.q_RunDO) THEN iq_stPtm.eState := pumpSTOPPED; ELSE iq_stPtm.eState := pumpFAULT; END_IF; (*I/O soft mapping*) ACT_IO(); // Log States and triggers This^.ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*Inputs*) iq_stPtm.i_xAtSpd := i_xAtSpd; iq_stPtm.i_xNCError := i_xFaultNC; iq_stPtm.i_xFault := NOT i_xFaultNC; (*Outputs*) q_RunDO := iq_stPtm.q_RunDO; END_ACTION ACTION ACT_Logger: //STATE Logger // ILK logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= NOT iq_stPTM.i_xFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_PTM_TwisTorr ^^^^^^^^^^^^^^^ :: (* This function block does basic controls FOR the Agilent TwisTorr 304 FS with on board Controller. Turns off pump in the event of errors/ warnings. Provides interlocking interface.*) FUNCTION_BLOCK FB_PTM_TwisTorr EXTENDS FB_Pump VAR_INPUT i_xExtILKOk : BOOL; // Connect to external interlock logic, TRUE if not used. END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPtm : ST_PTM; END_VAR VAR xRunOk : BOOL; (*IO*) i_xAtSpd AT%I*: BOOL; // Normaml operation when true i_xFault AT%I*: BOOL; // Error // this open collector output signal is ON when a system fault condition is detected q_RunDO AT%Q*: BOOL; // Start/Stop END_VAR (* Interlock Sum *) iq_stPtm.xExtRunOk:= i_xExtILKOk; xRunOk := iq_stPtm.xExtRunOk AND NOT iq_stPtm.i_xFault; (* Basic pump supervisory section *) IF xRunOk THEN iq_stPtm.q_RunDO := iq_stPTM.xRunSW; ELSE iq_stPtm.xRunSW:=FALSE; iq_stPtm.q_RunDO:=FALSE; END_IF (*Pump STATE*) IF iq_stPtm.i_xFault THEN iq_stPtm.eState := pumpFAULT; ELSIF NOT iq_stPtm.q_RunDO AND NOT iq_stPTM.i_xFault THEN iq_stPtm.eState := pumpSTOPPED; ELSIF NOT iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpSTARTING; ELSIF iq_stPtm.i_xAtSpd AND iq_stPtm.q_RunDO THEN iq_stPtm.eState := pumpRUNNING; ELSE iq_stPtm.eState := pumpFAULT; END_IF (*I/O soft mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*Inputs*) iq_stPtm.i_xAtSpd := i_xAtSpd; iq_stPtm.i_xFault := i_xFault; (*Outputs*) q_RunDO := iq_stPtm.q_RunDO; END_ACTION ACTION ACT_Logger: //STATE Logger // ILK logger IF NOT i_xExtIlkOK AND ePrevState = pumpRUNNING THEN fbLogger(sMsg:='Lost external interlock while pump was running.', eSevr:=TcEventSeverity.Critical); END_IF IF ePrevState <> iq_stPTM.eState THEN CASE iq_stPTM.eState OF pumpFAULT: fbLogger(sMsg:='Pump Fault.', eSevr:=TcEventSeverity.Critical); pumpSTOPPED: fbLogger(sMsg:='Pump stopped.', eSevr:=TcEventSeverity.Critical); pumpSTARTING: fbLogger(sMsg:='Pump starting.', eSevr:=TcEventSeverity.Info); pumpRUNNING: fbLogger(sMsg:='Pump running.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stPTM.eState; END_IF // Log Action tAction(CLK:= iq_stPTM.q_RunDO); IF tAction.Q THEN fbLogger(sMsg:='Pump commanded to start', eSevr:=TcEventSeverity.Info); END_IF // Log FAULT tFault(CLK:= NOT iq_stPTM.i_xFault); IF tFault.Q THEN fbLogger(sMsg:='Pump Lost Alarm OK bit', eSevr:=TcEventSeverity.Critical); END_IF END_ACTION FB_Pump ^^^^^^^ :: FUNCTION_BLOCK FB_Pump VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR // For logging fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_PumpState; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, PUMP_RUN, etc.) tFault : F_TRIG; tILK: TON; END_VAR END_FUNCTION_BLOCK ACTION ACT_Logger: END_ACTION FB_ScrollPump ^^^^^^^^^^^^^ :: (* This Function block provides basic control for Scroll Pumps *) (* Turn on scroll pump if the Turbo Vent Valve is closed. *) (* Cannot turn off scroll pump if turb is running. delay scroll off 150s.*) FUNCTION_BLOCK FB_ScrollPump VAR_IN_OUT END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stPFO : ST_RoughPump; END_VAR VAR_INPUT TurboIsON : BOOL; xExtIlk : BOOL; END_VAR VAR Tmr_TOF : TOF; tTimerDuration : Time:= T#150S; //Previous_RunSW : BOOL; // Digital Output to be linked q_xRunDo AT%Q* : BOOL; END_VAR //Turn on scroll pump if the Turbo Vent Valve is closed. So you are not just // pumping air and accomplishing nothing // cannot turn off scroll pump if turb is running. delay scroll off 150s. iq_stPFO.xIlkOK := xExtIlk;// and NOT Tmr_TOF.Q; Tmr_TOF(IN:=TurboIsON, PT := tTimerDuration); IF iq_stPFO.xIlkOK THEN iq_stPFO.q_xRunDo := (Tmr_TOF.Q OR iq_stPFO.pv_xRunSW); ELSE iq_stPFO.q_xRunDo := FALSE; END_IF iq_stPFO.pv_xRunSW := iq_stPFO.q_xRunDo; (*State evaluation*) IF NOT iq_stPFO.q_xRunDo THEN iq_stPFO.eState := pumpSTOPPED; ELSE iq_stPFO.eState := pumpRUNNING; END_IF (*soft io Mapping*) IO(); END_FUNCTION_BLOCK ACTION IO: q_xRunDo:= iq_stPFO.q_xRunDo; END_ACTION FB_TGCC_ADS ^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* The variable values are read via ADS using the symbol name*) FUNCTION_BLOCK FB_TGCC_ADS Extends FB_ADS VAR_INPUT sNetId : String; //NetID of the Destination PLC controller nPort : uint; // port number sVarName : string;// the variable name of the declared gauge function block. iWatchdog:UDINT;//The watchdog variable name written to by the remote plc END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} IG : ST_VG; bError: BOOL; END_VAR VAR fb_CheckWatchdog: FB_CheckWatchdog; fb_Read_VG: FB_ReadAdsSymByName; ftReset: F_TRIG; xFirstPass: BOOL:= true; END_VAR ftReset(CLK:= fb_Read_VG.bBusy OR xFirstPass); xFirstPass := false; (*calling ADS read function*) //IG.xPRESS_OK := false; fb_Read_VG( bRead:=ftReset.Q , sNetId:= sNetId, nPort:= nPort, sVarName:= CONCAT(sVarName,'.IG'), nDestAddr:= ADR(IG), nLen:= SIZEOF(IG), tTimeout:= , eComMode:=eAdsComModeFastCom , bBusy=> , bError=> , nErrorId=> ); (*Error*) fb_CheckWatchdog( bEnable:= TRUE, tWatchdogTime:= T#900ms, nCnt:= iWatchdog , bWatchdog=> , nLastCnt=> ); bError:= fb_Read_VG.bError OR fb_CheckWatchdog.bWatchdog; tErrorPresent(CLK:=bError); IF (fb_Read_VG.bError OR fb_CheckWatchdog.bWatchdog) THEN IG.xPRESS_OK := FALSE; END_IF; (*Logger*) ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_Logger: IF tErrorPresent.Q THEN IF(fb_Read_VG.bError) THEN fbLogger(sMsg:='ADS Read Error', eSevr:=TcEventSeverity.Critical); END_IF; IF(fb_CheckWatchdog.bWatchdog) THEN fbLogger(sMsg:='ADS Watchdog Error', eSevr:=TcEventSeverity.Critical); END_IF; END_IF END_ACTION FB_TPIP_ADS ^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the Variables in the original structure is required, just few signals *) (* The variable values are read via ADS using the symbol name*) FUNCTION_BLOCK FB_TPIP_ADS Extends FB_ADS VAR_INPUT sNetId : String; //NetID of the Destination PLC controller nPort : uint; // port number sVarName : string;// the variable name of the declared pip/pin function block. iWatchdog:UDINT;//The watchdog variable name written to by the remote plc END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv:'} IG : ST_VG; bError: BOOL; END_VAR VAR fb_CheckWatchdog: FB_CheckWatchdog; fb_Read_VG: FB_ReadAdsSymByName; ftReset: F_TRIG; xFirstPass: BOOL:= true; END_VAR ftReset(CLK:= fb_Read_VG.bBusy OR xFirstPass); xFirstPass := false; (*calling ADS read function*) fb_Read_VG( bRead:=ftReset.Q , sNetId:= sNetId, nPort:= nPort, sVarName:= CONCAT(sVarName,'.q_IG'), nDestAddr:= ADR(IG), nLen:= SIZEOF(IG), tTimeout:= , eComMode:=eAdsComModeFastCom , bBusy=> , bError=> , nErrorId=> ); (*Error*) fb_CheckWatchdog( bEnable:= TRUE, tWatchdogTime:= T#900ms, nCnt:= iWatchdog , bWatchdog=> , nLastCnt=> ); bError:= fb_Read_VG.bError OR fb_CheckWatchdog.bWatchdog; tErrorPresent(CLK:=bError); IF (fb_Read_VG.bError OR fb_CheckWatchdog.bWatchdog) THEN IG.xPRESS_OK := FALSE; END_IF; (*Logger*) ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_Logger: IF tErrorPresent.Q THEN IF(fb_Read_VG.bError) THEN fbLogger(sMsg:='ADS Read Error', eSevr:=TcEventSeverity.Critical); END_IF; IF(fb_CheckWatchdog.bWatchdog) THEN fbLogger(sMsg:='ADS Watchdog Error', eSevr:=TcEventSeverity.Critical); END_IF; END_IF END_ACTION FB_TurboExtLogic ^^^^^^^^^^^^^^^^ :: (*deprecated*) FUNCTION_BLOCK FB_TurboExtLogic VAR_IN_OUT Turbo : ST_AgilentPTM; END_VAR VAR_INPUT BackingGauge: ST_VG; InletGauge : ST_VG; VentValve : ST_VCC_NO; ScrollPump : ST_RoughPump; END_VAR VAR_OUTPUT END_VAR VAR END_VAR END_FUNCTION_BLOCK FB_TurbVentvalve_NO ^^^^^^^^^^^^^^^^^^^ :: (* This function block impelemets Interlock for Agilent Turbo Vent Valve Normally Open*) FUNCTION_BLOCK FB_TurbVentvalve_NO VAR_OUTPUT {attribute 'pytmc' :=' pv:'} q_stValve : ST_VCC_NO; END_VAR VAR_INPUT i_stPTM : ST_AgilentPTM; //Agilent Turbo Pump END_VAR VAR tofTmr : TOF; (*Output*) q_xCLS_DO AT%Q* : BOOL; END_VAR (*If the turbo is on, you cannot open the valve. In order to open the valve, you have to allow the turbopump to have already been turned off for 2 minutes and you have to press the open button on the vent valve If the code looks confusing please use a truth table to verify*) END_FUNCTION_BLOCK ACTION ACT_IO: (*outputs*) q_xCLS_DO:= q_stValve.xCLS_DO; END_ACTION FB_TVGC_ADS ^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the fields in the original structure is required, just few signals *) (*Use with FB_ADS_WATCHDOG on remotePLC*) FUNCTION_BLOCK FB_TVGC_ADS extends FB_ADS VAR_INPUT sNetId : String; //NetID of the Destination PLC controller nPort : UINT; // port number sVarName : string;// the variable name of the (device) declared function block. iWatchdog:UDINT;//The watchdog variable name written to by the remote plc END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv: '} VGC:ST_VGC; bError:BOOL; END_VAR VAR fb_CheckWatchdog: FB_CheckWatchdog; fb_Read_VGC: FB_ReadAdsSymByName; ftReset: F_TRIG; xFirstPass: bool:= true; END_VAR ftReset(CLK:= fb_Read_VGC.bBusy OR xFirstPass); xFirstPass:=false; (*calling ADS read function*) fb_Read_VGC( bRead:= ftReset.Q, sNetId:= sNetId, nPort:= nPort, sVarName:= CONCAT(sVarName,'.iq_stValve'), nDestAddr:= ADR(VGC), nLen:= SIZEOF(VGC), tTimeout:= , eComMode:= eAdsComModeFastCom, bBusy=> , bError=> , nErrorId=> ); (*Error*) fb_CheckWatchdog( bEnable:= TRUE, tWatchdogTime:= T#900ms, nCnt:= iWatchdog , bWatchdog=> , nLastCnt=> ); bError:= fb_Read_VGC.bError OR fb_CheckWatchdog.bWatchdog; ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_Logger: IF tErrorPresent.Q THEN IF(fb_Read_VGC.bError) THEN fbLogger(sMsg:='ADS Read Error', eSevr:=TcEventSeverity.Critical); END_IF; IF(fb_CheckWatchdog.bWatchdog) THEN fbLogger(sMsg:='ADS Watchdog Error', eSevr:=TcEventSeverity.Critical); END_IF; END_IF END_ACTION FB_Valve ^^^^^^^^ :: FUNCTION_BLOCK FB_Valve VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR // For logging fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_ValvePositionState; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, etc.) tOverrideActivated : R_TRIG; END_VAR END_FUNCTION_BLOCK ACTION ACT_Logger: END_ACTION FB_Valve_Interface ^^^^^^^^^^^^^^^^^^ :: (* This function block is created for interface devices between different PLC*) (* Not all the fields in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) FUNCTION_BLOCK FB_Valve_Interface VAR_INPUT END_VAR VAR_OUTPUT {attribute 'pytmc' := 'pv: '} VGC:ST_VGC; END_VAR VAR i_xOpnLS AT%I* : BOOL; i_xClsLS AT%I* : BOOL; END_VAR (* This function block is created for interface devices between different PLC*) (* Not all the fields in the original structure is required, just few signals *) (* They have to be linked to the custom created variables on the EL6692/5 primary side*) VGC.i_xClsLS := i_xClsLS; VGC.i_xOpnLS := i_xOpnLS; END_FUNCTION_BLOCK FB_VCC ^^^^^^ :: (*Deprecated*) (* THE FB_VCC FUNCTION BLOCK IS DEPRECATED. USE FB_VVC INSTEAD. *) (* This Function Block Implements Basic Functionality for Vent Valves VVC *) (* Note Interlock Logic is External *) FUNCTION_BLOCK FB_VCC VAR_IN_OUT END_VAR VAR_INPUT i_xExtILK_OK:BOOL; (*Other External Interlock, Set to True when no external interlock is required*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only*) END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VVC; END_VAR VAR tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; (*IO*) q_xOPN_DO AT%Q*: BOOL; END_VAR iq_stValve.xOPN_OK := i_xExtILK_OK; IF NOT iq_stValve.xOPN_OK THEN iq_stValve.pv_xOPN_SW := FALSE; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q THEN iq_stValve.xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.xOvrdOpn, PT:=T#10S); (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); (*IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; END_ACTION FB_VCC_NO ^^^^^^^^^ :: (*Deprecated*) (* This Function Block Implements Basic Functionality for NO Vent Valves VVC *) (* Note Interlock Logic is External *) FUNCTION_BLOCK FB_VCC_NO VAR_IN_OUT {attribute 'pytmc' := 'pv:'} iq_stValve : ST_VCC_NO; END_VAR VAR_INPUT i_xExtILK_OK:BOOL; (*Other External Interlock, Set to True when no external interlock is required*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) END_VAR VAR_OUTPUT END_VAR VAR tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; (*IO*) xCLS_DO AT%Q*: BOOL; END_VAR IF NOT iq_stValve.xCLS_OK THEN iq_stValve.pv_xCLS_SW := FALSE; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xCLS_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q THEN iq_stValve.pv_xOvrdCls :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdCls, PT:=T#10S); (* Here's where the valve is closed *) iq_stValve.xCLS_DO := (iq_stValve.pv_xCLS_SW AND iq_stValve.xCLS_OK) OR (tonOvrd.Q AND i_xOverrideMode); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) xCLS_DO:= iq_stValve.xCLS_DO; END_ACTION FB_VCN ^^^^^^ :: (* This function implements the Basic functions for the Pfeiffer EVR 116 needle valve*) FUNCTION_BLOCK FB_VCN VAR_INPUT i_xExtIlkOK : BOOL; //External Interlock, SET to TRUE if not used i_ReqPos : REAL; //Requested position END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stVCN : ST_VCN; //Needle valve structure END_VAR VAR_IN_OUT END_VAR VAR CONSTANT rOpenVoltage : REAL := 9.1; // From the EVR 116 manual, A Voltage of 9V it is completely Open rCloseVoltage : REAL := 0.4;// a voltage <0.5 V the valve is closed END_VAR VAR // Requested voltage {attribute 'pytmc' := ' pv: POS_AO; io: i ; '} rReqVoltage: REAL := 0; //iResolution: INT :=16; // (*IO*) q_iRawPosition AT%Q* :INT; END_VAR (* Needle valve control FB A. Wallace 2016-7-21 This FB should be used as a low level control block. It provides: Valve position ceiling Interlock Scaling It is not intended for: Closed-loop control It could be used for: Valve position/flow linearization Note: Raw position calc is based on 0.5 to 9V span, 32767 bits *) // Interlocking iq_stVCN.xIlkOK := i_xExtIlkOK; (*Checking which Control mode is selected*) IF iq_stVCN.xIlkOK THEN IF iq_stVCN.eValveControl = OpenValve THEN iq_stVCN.rReqPosition := iq_stVCN.rUpperLimit;(*Percentage*)// iq_stVCN.rUpperLimit; ELSIF iq_stVCN.eValveControl = CloseValve THEN iq_stVCN.rReqPosition := 0; (*Percentage*) ELSIF (iq_stVCN.eValveControl = ManualControl) (*AND (iq_stVCN.xOPN_SW)*) THEN iq_stVCN.rReqPosition := LIMIT(0, iq_stVCN.rReqPosition, iq_stVCN.rUpperLimit); ELSIF iq_stVCN.eValveControl = PressureControl THEN iq_stVCN.rReqPosition := LIMIT(0, i_ReqPos, iq_stVCN.rUpperLimit); END_IF ELSE iq_stVCN.rReqPosition := 0; iq_stVCN.eValveControl := CloseValve; END_IF // Requested Voltage calculation rReqVoltage := iq_stVCN.rReqPosition * (rOpenVoltage-rCloseVoltage)/100 + rCloseVoltage; rReqVoltage := LIMIT(rCloseVoltage, rReqVoltage, rOpenVoltage); //The requested voltage should remain within this range //Raw position calc iq_stvcn.q_iRawPosition := REAL_TO_INT( 32767/10 * rReqVoltage); (*SOft IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*outputs*) q_iRawPosition := iq_stVCN.q_iRawPosition; (*inputs*) iq_stVCN.i_iPosition := iq_stvcn.q_iRawPosition; END_ACTION FB_VFS ^^^^^^ :: (* This function block implements basic functionality for Fast Shutter/Valves*) (* Create a separate virtual PLC for the fast shutter logic, and tie/map this task to the FAST I/O portion of the ethercat bus, so we can scan these I/Os faster than the rest of the vacuum I/Os. The Fast shutter was tested with PLC task Cycle Base Time 50us with cycle time 0.050ms. *) FUNCTION_BLOCK FB_VFS EXTENDS FB_Valve VAR_INPUT i_xPMPS_OK: BOOL ; (*External MPS interlock, Set to TRUE when no PMPS interlock is required*) i_xExt_OK: BOOL; (*Other External Interlock, Set to True when no external interlock is required*) i_sDevName : T_MaxString := 'VGC'; // Device name for diagnostic END_VAR VAR_IN_OUT io_fbFFHWO : FB_HardwareFFOutput; END_VAR VAR xOPN_OK :BOOL; xERR_ExtFault :BOOL; // PMPS fbFF : FB_FastFault :=( i_DevName := 'VFS', i_Desc := 'Fault occurs when fast valve is not in open state', i_TypeCode := 16#1010); rDiffPressAllowed : REAL := 22.5; // Torr, Default value comes from Vat Valve Manual rDiffPress : REAL; set : BOOL; reset: BOOL; xFirstPass : BOOL := TRUE; //fbFSInit : R_TRIG; rtOPN_SW : R_TRIG; tonDelOK : TON; rtOK : R_TRIG; tonOvrd : TON; tDelOK : TIME := T#60S; tOvrd : TIME := T#10s; xClose: BOOL; xOpen: BOOL; CloseTimer: TON; TimDur: TIME := T#20MS; OpenTimer: TON; OpenTimDur: TIME := T#2S; (* Timeouts*) tCLSTimeOutDuration: TIME:= T#0.5S; tOPNTimeOutDuration: TIME:= T#10S; tOPNtimeout: TON; tCLStimeout:TON; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xClose_A AT%Q*: BOOL; q_xClose_B AT%Q*: BOOL; q_xClose_C AT%Q*: BOOL; q_xOPN_DO AT%Q*: BOOL; i_xTrigger AT%I*: BOOL; // Interface i_xPress_OK AT%I*:BOOL; (*To be linked to the Fast sensor structure signal IG.xPRESS_OK, to make sure that the device is connected*) i_xOPN_SW AT%I*:BOOL; i_xCLS_SW AT%I*:BOOL; (*external open signal e.g epics*) i_xVAC_FAULT_Reset AT%I*: BOOL;(*Valve Vacuum OK, is set to False when there is Vacuum Fault*) i_xOverrideMode AT%I*: BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) i_xOverrideOpen AT%I*: BOOL; q_xTrigger AT%Q*: BOOL; (*Interface*) q_xVFS_Open AT%Q*: BOOL; (*Interface*) q_xVFS_Closed AT%Q*: BOOL; (*Interface*) q_xVAC_FAULT_OK AT%Q*: BOOL; (*Valve Vacuum OK, is set to False when there is Vacuum Fault*) q_xMPS_OK AT%Q*: BOOL; (*MPS Fault OK, is set when the Valve is Open and there is no trigger*) q_eVFS_State AT%Q*: E_VGC; (*Interface*) END_VAR (* Fast Shutter/Valve Function Block*) (* M.Ghaly Feb 2019*) (* Fast Shutters do not function as complete isolation valves, but are intended to rapidly limit conductance between vacuum regions. They should be installed in conjunction with isolation gate valves. The conductance of the fast shutter in the closed state may be sufficiently low as to create an isolated vacuum region. Thus, at least a pirani gauge, that can measure a pressure down to 1x10-4 Torr, needs to be installed between the shutter and the isolation gate valve. In the case where a gauge is not installed, the neigboring gate Valves should not be opened unless the shutter is Open. *) (* On first PLC pass, put valve into vented state, which implies a closed valve *) IF xFirstPass THEN fbFF.i_DevName := i_sDevName; xFirstPass := FALSE; END_IF (* Input Trigger to close the Fast Shutter*) (* Neighboring Valves Should also be triggered to Close*) IF (i_xTrigger) OR NOT (i_xPress_OK) OR NOT (i_xExt_OK) OR i_xCLS_SW THEN xClose:= TRUE; xOpen:= FALSE; q_xVAC_FAULT_OK:= FALSE; IF (NOT i_xCLS_SW) THEN xERR_ExtFault := TRUE; END_IF; i_xCLS_SW := TRUE; q_xOPN_DO := FALSE; //i_xOPN_SW :=FALSE; xOpen:=FALSE; ELSIF ((rtOPN_SW.Q) AND xOPN_OK AND NOT i_xOpnLS) OR (tonOvrd.Q AND i_xOverrideMode) THEN q_xOPN_DO := TRUE; END_IF IF (xClose) AND (NOT i_xClsLS) THEN q_xClose_A := TRUE; q_xClose_B := TRUE; q_xClose_C := TRUE; END_IF IF(CloseTimer.Q) OR (i_xClsLS) THEN q_xClose_A := FALSE; q_xClose_B := FALSE; q_xClose_C := FALSE; xClose := FALSE; //i_xOPN_SW := FALSE; END_IF IF(OpenTimer.Q) AND (i_xOpnLS) THEN q_xOPN_DO := FALSE; i_xOPN_SW := FALSE; xOpen:=FALSE; END_IF (* Neighboring Valves Interlock bit is reset When the Vacuum event error is reset and the Shutter is opened *) IF (NOT i_xTrigger) AND (NOT xERR_ExtFault) AND i_xOpnLS THEN q_xVAC_FAULT_OK := TRUE; END_IF (*OPN OK evaluation bit *) xOPN_OK := (NOT xERR_ExtFault) AND NOT i_xTrigger; (* If Epics Command to close the Valve, check if PMPS, otherwise Keep command set to open valve *) // is there really a PMPS requirment. (* When the valve is open MPS is OK*) q_xMPS_OK := i_xOpnLS AND NOT i_xTrigger AND i_xPress_OK AND NOT(i_xCLS_SW); (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=xOPN_OK, PT:=tDelOK); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND i_xOverrideOpen THEN i_xOverrideOpen :=FALSE; //if (iq_stValve.eState = OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //for seamless transition //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF //Override timer tonOvrd(IN:=i_xOverrideOpen, PT:=tOvrd); //Assign valve state IF (i_xTrigger) THEN q_eVFS_State := E_VGC.Triggered; ELSIF xERR_ExtFault THEN q_eVFS_State := E_VGC.Vac_Fault; ELSIF NOT i_xClsLS AND tCLStimeout.Q THEN q_eVFS_State := E_VGC.Cls_Timeout; ELSIF NOT i_xOpnLS AND tOPNtimeout.Q THEN q_eVFS_State := E_VGC.Opn_Timeout; ELSE q_eVFS_State := E_VGC.At_Vac; END_IF (*Timers*) CloseTimer ( IN:=xClose, PT:= TimDur, Q=>); OpenTimer ( IN:=i_xOpnLS, PT:= OpenTimDur, Q=>); tOPNtimeout(IN:= q_xOPN_DO, PT := tOPNTimeOutDuration); tCLStimeout(IN:= q_xClose_A, PT := tCLSTimeOutDuration); rtOPN_SW(CLK:= i_xOPN_SW); (* Soft IO Mapping*) IO(); (*Alarm reset *) ACT_ResetAlarms(); (*FAST FAULT*) fbFF(i_xOK := q_xMPS_OK, i_xReset := i_xVAC_FAULT_Reset, i_xAutoReset :=TRUE, io_fbFFHWO := io_fbFFHWO); END_FUNCTION_BLOCK ACTION ACT_ResetAlarms: //iq_stValve.xERR_DifPres R= i_xResetFault; //iq_stValve.bErrorPresent R= i_xResetFault; IF NOT i_xTrigger AND i_xPress_OK THEN xERR_ExtFault R= i_xVAC_FAULT_Reset; i_xVAC_FAULT_Reset:=FALSE; END_IF END_ACTION ACTION IO: q_xTrigger := i_xTrigger; q_xVFS_Closed:= i_xClsLS; q_xVFS_Open:= i_xOpnLS; END_ACTION FB_VFS_Interface ^^^^^^^^^^^^^^^^ :: (*Used as soft IO mapping to create a psuedo valve to communicate over two task on the same PLC.*) (*for FAST shutter control*) FUNCTION_BLOCK FB_VFS_Interface EXTENDS FB_Valve VAR_INPUT IG : ST_VG; // The MKS422 Cold Cathode Data Structure END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: ; '} iq_stValve : ST_VFS; (* All valve data and states will be in this struct*) i_xVAC_FAULT_OK AT%I*: BOOL; (*Valve Vacuum OK, is set to False when there is Vacuum Fault*) END_VAR VAR tonOvrd : TON; tOvrd : TIME := T#10s; (*outputs*) q_xPRESS_OK AT%Q*: BOOL; q_xOPN_SW AT%Q*:BOOL; q_xCLS_SW AT%Q*:BOOL; (*external open signal e.g epics*) q_xVAC_FAULT_Reset AT%Q*: BOOL; (*Valve Vacuum OK, is set to False when there is Vacuum Fault*) q_xOverrideMode AT%Q*: BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) q_xOverrideOpen AT%Q*: BOOL; (*inputs*) i_xTrigger AT %I* : BOOL; i_xVFS_Open AT %I* : BOOL; i_xVFS_Closed AT %I* : BOOL; {attribute 'pytmc' := ' pv: MPS_FAULT_OK field: ZNAM MPS FAULT ; field: ONAM MPS OK ; io: i ; '} i_xMPS_OK AT%I*: BOOL;(*MPS Fault OK, is set when the Valve is Open and there is no trigger*) i_eVFS_State AT%I*: E_VGC; (*Interface*) END_VAR (*soft IO Mapping*) q_xPRESS_OK := IG.xPRESS_OK; (* Soft IO Mapping*) IO(); ///Check valve postion IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=OPEN; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF //Override timer tonOvrd(IN:=q_xOverrideOpen, PT:=tOvrd); (* Soft IO Mapping*) IO(); (*Logger*) ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_Logger: // ILK logger IF i_xTrigger AND ePrevState = OPEN THEN fbLogger(sMsg:='Fast valve triggered to close', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> iq_stValve.eState THEN CASE iq_stValve.eState OF INVALID: fbLogger(sMsg:='Valve invalid position.', eSevr:=TcEventSeverity.Critical); MOVING: fbLogger(sMsg:='Valve moving', eSevr:=TcEventSeverity.Warning); OPEN: fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info); CLOSED: fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stValve.eState; END_IF // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND q_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF END_ACTION ACTION IO: (*inputs*) iq_stValve.i_xTrigger := i_xTrigger; iq_stValve.i_xOpnLS := i_xVFS_Open; iq_stValve.i_xClsLS:= i_xVFS_Closed; iq_stValve.eVGC_State := i_eVFS_State; iq_stValve.i_xVAC_FAULT_OK:= i_xVAC_FAULT_OK; (*output*) q_xOPN_SW := iq_stValve.pv_xOPN_SW; q_xCLS_SW:= iq_stValve.xCLS_SW; q_xVAC_FAULT_Reset := iq_stValve.pv_xAlmRst; q_xOverrideMode :=iq_stValve.xOverrideMode; q_xOverrideOpen := iq_stValve.pv_xOvrdOpn ; iq_stValve.sGFS := IG.sPath; //reset the commands IF ( iq_stValve.i_xOpnLS) THEN iq_stValve.pv_xOPN_SW := FALSE; END_IF IF ( iq_stValve.i_xClsLS) THEN iq_stValve.xCLS_SW := FALSE; END_IF END_ACTION FB_VGC ^^^^^^ :: (* This function block implements basic functionality for Isolation Gate Valves*) (* This function block interlock is as follows: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint.*) (*This function block also implements PMPS and EPS interlocks, as well as Fast MPS trigger*) {attribute 'no_check'} {attribute 'reflection'} FUNCTION_BLOCK FB_VGC Extends FB_Valve VAR_IN_OUT END_VAR VAR_INPUT (*Upstream Gauge, usually ion gauge*) i_stUSG : ST_VG; (*Downstream Gauge, usually ion gauge*) i_stDSG : ST_VG; {attribute 'pytmc' := ' pv: Dis_DPIlk '} i_xDis_DPIlk : BOOL := FALSE; (* Set to true when calling the function to disable the differential pressure interlock *) i_xPMPS_OK: BOOL ; (*Set to True To switch off the bptm and PMPS Arbiter*) {attribute 'pytmc' := ' pv: EPS_OK '} i_xEPS_OK: BOOL := TRUE; (*External EPS interlock, Set to TRUE when no EPS interlock is required, otherwise set to correct interlock signal*) i_xExt_OK: BOOL; (*Other External Interlock, Set to True when no external interlock is required. If this Valve is neigboring a Fast Shutter this should be linked to the fast shutter xVAC_FAULT_OK*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) // Reset fault {attribute 'pytmc' := ' pv: FF_Reset '} i_xReset: BOOL; i_xIsAperture:BOOL :=FALSE; // Set tp True if this is an Aperture Valve, the MPS Fault will trip only when moving. i_sDevName : T_MaxString := 'VGC'; // Device name for diagnostic i_nTransitionRootID: UDINT; //A unique transition Root ID that is equal to or greater than 1000 END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VGC; (* All valve data and states will be in this struct*) {attribute 'pytmc' := ' pv: MPS_FAULT_OK field: ZNAM MPS FAULT ; field: ONAM MPS OK ; '} xMPS_OK: BOOL; (*MPS Fast OK, is set when the Valve is Open*) END_VAR VAR_IN_OUT io_fbFFHWO : FB_HardwareFFOutput; fbArbiter: FB_Arbiter(); END_VAR VAR // PMPS {attribute 'pytmc' := ' pv: MPS_OK '} xPMPS_OK: BOOL ; (*PMPS interlock*) bMoving : BOOL; bDone :BOOL; tBPTMtimeout:TON; bptm: BeamParameterTransitionManager; FFO : FB_FastFault :=( i_DevName := 'VGC', i_Desc := 'Fault occurs when the valve is not in open state', i_TypeCode := 16#1010); //g_FastFaultOutput1 : FB_HardwareFFOutput; {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; rDiffPressAllowed : REAL := 22.5; // Torr, Default value comes from Vat Valve Manual rDiffPress : REAL; set : BOOL; reset: BOOL; xFirstPass : BOOL; fbFSInit : R_TRIG; tonDelOK : TON; rtOK : R_TRIG; tonOvrd : TON; rtOpen : R_TRIG; ftClose: F_TRIG; tDelOK : TIME := T#60S; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; // For logging eVGCPrevState : E_VGC; END_VAR (* Vacuum gate valve A. Wallace 16-10-29 A gate valve isolates vacuum volumes. Ideally it can be opened when a system is vented to allow for faster pumping, and will close when high vacuum is lost. The following behavior is good for valves in something like a gas attenuator. This function block does the following: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint. Alternatively, the differential pressure interlock can be disabled so the valve may only be opened if the pressure on both sides is lower than the at-vacuum-setpoint. You want this behavior if the valve is to be used in a UHV section. Hysteresis is employed to ensure a smooth transition from vented/pumping down, to at-vac. Finally, an override system is built in so you can bypass all the interlocking logic and get back online. *) (* 10/1/2018 Margaret Ghaly included EPS, PMPS Checkes and MPS trigger signals*) fbFSInit( CLK := TRUE, Q => xFirstPass); (*IO Mapping*) ACT_IO(); (* On first PLC pass, put valve into vented state, which implies a closed valve *) IF xFirstPass THEN iq_stValve.eVGC_State := Vented; iq_stValve.pv_xOPN_SW := FALSE; FFO.i_DevName := i_sDevName; END_IF ///Check valve position IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN_F; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF // Update hysteresis /////////////////////////////////////////////////// IF iq_stValve.rAT_VAC_SP_LAST <> iq_stValve.rAT_VAC_SP OR xFirstPass THEN iq_stValve.rAT_VAC_SP_LAST := iq_stValve.rAT_VAC_SP; iq_stValve.rAT_VAC_HYS := iq_stValve.rHYST_PERC * iq_stValve.rAT_VAC_SP; END_IF iq_stValve.rAT_VAC_HYS := LIMIT(0, iq_stValve.rAT_VAC_HYS, iq_stValve.rAT_VAC_SP); IF iq_stValve.rAT_VAC_SP <> 0 THEN IF iq_stValve.rHYST_PERC <> (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) THEN iq_stValve.rHYST_PERC := LIMIT(0, (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) ,1); END_IF END_IF // Valve at vacuum check /////////////////////////////////////////////////// set := i_stUSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stDSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC S= set; reset := i_stUSG.rPRESS > iq_stValve.rAT_VAC_SP OR i_stDSG.rPRESS > iq_stValve.rAT_VAC_SP OR NOT i_stUSG.xPRESS_OK OR NOT i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC R= reset; // Differential pressure check /////////////////////////////////////////////////// (* Calc the differential pressure across the valve *) rDiffPress := ABS(i_stUSG.rPRESS - i_stDSG.rPRESS); (* As long as the differential pressure is less than 30mbar, the valve is allowed to open *) IF rDiffPress <= rDiffPressAllowed AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK THEN iq_stValve.xDP_OK := TRUE; ELSE iq_stValve.xDP_OK := FALSE; (* If the differential pressure is exceeded, even when the valve is open, the state is unexpected and triggers a fault, because it suggests that something is wrong with the valve, or gauges or mapping or there is a major leak on one side.*) iq_stValve.eVGC_State := ERR_DiffPress; iq_stValve.xERR_DifPres := TRUE; END_IF // Valve state /////////////////////////////////////////////////// CASE iq_stValve.eVGC_State OF Vented: (* The Vented state is used during pump down *) (* Assuming the pump down went well, we are now at vacuum on both sides, so we move to the vacuum state, otherwise remain in the vented state *) IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) AND (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF AtVacuum: IF iq_stValve.xAT_VAC THEN (* If both pressure setpoints are made, then enable the differential pressure interlock, regardless of the valve state assuming we're using some kind of ion gauge where a pressure setpoint cannot be higher than 1E-4 T *) iq_stValve.eVGC_State := AtVacuum; ELSE (* If the valve is open (or in an unknown state) and either gauge is not at it's vacuum setpoint, we have a loss of vacuum error *) IF (iq_stValve.i_xOpnLS OR (NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.i_xClsLS)) THEN iq_stValve.eVGC_State := ERR_LostVac; iq_stValve.xERR_SP := TRUE; //TAW changed this to the ERR_SP bool because I think that's what it was intended to be? (* Alternatively, if the valve is already closed and we lose pressure on one side, it was probably intentional venting, so we calmly move back to venting mode, and disable the differential pressure interlock *) //?? ELSIF iq_stValve.i_xClsLS THEN iq_stValve.eVGC_State := Vented; END_IF END_IF ERR_DiffPress: IF NOT (iq_stValve.xERR_DifPres) THEN IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) AND (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF END_IF ERR_LostVac: IF NOT (iq_stValve.xERR_SP) THEN IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) AND (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF END_IF ERR_ExtFault: IF NOT (iq_stValve.xERR_ExtFault) AND (i_xExt_OK) THEN IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) AND (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF END_IF END_CASE IF (NOT i_xExt_OK) AND (iq_stValve.eState=OPEN) THEN iq_stValve.eVGC_State := ERR_ExtFault; iq_stValve.xERR_ExtFault := TRUE; END_IF // Interlock evaluation - with bypass for DP ILK bypass /////////////////////////////////////////////// IF i_xDis_DPIlk THEN iq_stValve.xOPN_OK := iq_stValve.xEXT_OK; // AND iq_stValve.xDP_OK ; ELSE iq_stValve.xOPN_OK := iq_stValve.xEXT_OK AND iq_stValve.xAT_VAC; END_IF (* Valve operation *) (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=tDelOK); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND iq_stValve.pv_xOvrdOpn THEN iq_stValve.pv_xOvrdOpn :=FALSE; if (iq_stValve.eState = OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //for seamless transition //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF // Release the Force Open bit when the system override is false IF NOT (i_xOverrideMode) THEN iq_stValve.pv_xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=tOvrd); (* Valve operation *) (* Here's where we evaluate sw actions and operate the valve *) (* Valve operation *) ftClose(CLK:= iq_stValve.pv_xOPN_SW); rtOpen(CLK:= iq_stValve.pv_xOPN_SW); (*PMPS*) IF NOT(i_xPMPS_OK) THEN ACT_PMPS(); END_IF (*when interlock is lost the Valve closes regardless of the status of the PMPS and EPS*) xPMPS_OK := i_xPMPS_OK OR (bptm.q_xTransitionAuthorized OR tBPTMtimeout.Q); IF NOT (iq_stValve.xOPN_OK) THEN iq_stValve.pv_xOPN_SW:= FALSE; // Reset switch after inlk is lost iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); ELSIF xPMPS_OK THEN iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); //ELSIF NOT (i_xPMPS_OK) THEN // Only override mode //iq_stValve.q_xOPN_DO := (tonOvrd.Q AND i_xOverrideMode); END_IF (*MPS Fault setting*) If (i_xIsAperture ) THEN (* When the valve is open or in Closed position MPS is OK, Fault while moving*) xMPS_OK := (i_xOpnLS AND q_xOPN_DO) XOR (i_xClsLS AND NOT q_xOPN_DO); ELSE (* When the valve is open MPS is OK*) xMPS_OK := i_xOpnLS AND NOT i_xClsLS AND q_xOPN_DO; END_IF ///Check valve moving postion timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := 'Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := 'Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := CONCAT(sPath,'Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); // Alarm reset ////////////////////////////////////// ACT_ResetAlarms(); (*IO Mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); (*FAST FAULT*) FFO(i_xOK := xMPS_OK, i_xReset := i_xReset, i_xAutoReset :=TRUE, io_fbFFHWO := io_fbFFHWO); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; iq_stValve.xEXT_OK := i_xEXT_OK; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; (*ILK Devices*) iq_stValve.sIlkUSDeviceName := This^.i_stUSG.sPath; iq_stValve.sIlkDSDeviceName := This^.i_stDSG.sPath; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT (iq_stValve.xOPN_OK) AND (ePrevState = OPEN) AND NOT (TonOvrd.Q) THEN fbLogger(sMsg:='Lost interlock ok bit while valve was open.', eSevr:=TcEventSeverity.Critical); END_IF //Positiong STATE Logger IF ePrevState <> iq_stValve.eState THEN CASE iq_stValve.eState OF INVALID: fbLogger(sMsg:='Valve invalid position.', eSevr:=TcEventSeverity.Critical); MOVING: fbLogger(sMsg:='Valve moving', eSevr:=TcEventSeverity.Warning); OPEN: fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info); OPEN_F: fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info); CLOSED: fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stValve.eState; END_IF //Pressure STATE Logger IF eVGCPrevState <> iq_stValve.eVGC_State THEN CASE iq_stValve.eVGC_State OF Vented: fbLogger(sMsg:='Vented', eSevr:=TcEventSeverity.Info); AtVacuum: fbLogger(sMsg:='Vacuum setpoint satisfied', eSevr:=TcEventSeverity.Info); ERR_DiffPress: fbLogger(sMsg:='Potential accidental vent.', eSevr:=TcEventSeverity.Critical); ERR_LostVac: fbLogger(sMsg:='Unexpected loss of vacuum while valve was open or moving.', eSevr:=TcEventSeverity.Critical); ERR_ExtFault: fbLogger(sMsg:='Lost external interlock while valve was open.', eSevr:=TcEventSeverity.Critical); END_CASE eVGCPrevState := iq_stValve.eVGC_State; END_IF // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF END_ACTION ACTION ACT_PMPS: bMoving := (iq_stValve.pv_xOPN_SW AND iq_stValve.i_xClsLS) XOR (NOT iq_stValve.pv_xOPN_SW AND i_xOpnLS); bDone := (iq_stValve.pv_xOPN_SW AND iq_stValve.i_xOpnLS) XOR (NOT iq_stValve.pv_xOPN_SW AND i_xClsLS); If (i_xIsAperture ) OR (iq_stValve.pv_xOPN_SW )THEN bptm.i_stRequestedAssertion := PMPS_GVL.cstFullBeam; ELSE bptm.i_stRequestedAssertion := PMPS_GVL.cst0RateBeam; END_IF bptm(fbArbiter:=fbArbiter, i_TransitionAssertionID:=i_nTransitionRootID+2, i_stTransitionAssertion:=PMPS_GVL.cst0RateBeam, i_nRequestedAssertionID:=i_nTransitionRootID+ BOOL_TO_UDINT( iq_stValve.pv_xOPN_SW), i_stRequestedAssertion:=, i_xMoving:=bMoving, i_xDoneMoving:= bDone, stCurrentBeamParameters:=PMPS_GVL.stCurrentBeamParameters, q_xTransitionAuthorized=>); // Timeout tBPTMtimeout(IN:= bMoving AND NOT bptm.q_xTransitionAuthorized , PT:=T#1S); //xPMPS_OK := bptm.q_xTransitionAuthorized OR tBPTMtimeout.Q; //Timeout and clear request IF (tBPTMtimeout.Q) THEN fbArbiter.RemoveRequest(bptm.i_TransitionAssertionID); fbArbiter.RemoveRequest(bptm.i_nRequestedAssertionID); END_IF END_ACTION ACTION ACT_ResetAlarms: iq_stValve.xERR_DifPres R= iq_stValve.pv_xAlmRst; iq_stValve.xERR_SP R= iq_stValve.pv_xAlmRst; iq_stValve.bErrorPresent R= iq_stValve.pv_xAlmRst; iq_stValve.xERR_ExtFault R= iq_stValve.pv_xAlmRst; IF ( iq_stValve.pv_xAlmRst) THEN iq_stValve.sErrorMessage :=''; iq_stValve.pv_xAlmRst := FALSE; END_IF END_ACTION FB_VGC_AP ^^^^^^^^^ :: (*Deprectated*) (* This function block implements basic functionality for Isolation Gate Valves with Appertures*) (* This function block interlock is as follows: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint.*) (*This function block also implements PMPS and EPS interlocks, as well as Fast MPS trigger*) (* MPS is triggered differently than the VGC *) {attribute 'no_check'} FUNCTION_BLOCK FB_VGC_AP VAR_IN_OUT END_VAR VAR_INPUT (*Upstream Gauge, usually ion gauge*) i_stUSG : ST_VG; (*Downstream Gauge, usually ion gauge*) i_stDSG : ST_VG; {attribute 'pytmc' := ' pv: Dis_DPIlk '} i_xDis_DPIlk : BOOL := FALSE; (* Set to true when calling the function to disable the differential pressure interlock *) {attribute 'pytmc' := ' pv: EPS_OK '} i_xEPS_OK: BOOL := TRUE; (*External EPS interlock, Set to TRUE when no EPS interlock is required, otherwise set to correct interlock signal*) {attribute 'pytmc' := ' pv: MPS_OK '} i_xPMPS_OK: BOOL ; (*External MPS interlock, Set to TRUE when no PMPS interlock is required*) i_xExt_OK: BOOL; (*Other External Interlock, Set to True when no external interlock is required. If this Valve is neigboring a Fast Shutter this should be linked to the fast shutter xVAC_FAULT_OK*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) // Reset fault {attribute 'pytmc' := ' pv: FF_Reset '} i_xReset: BOOL; i_sDevName : T_MaxString := 'VGC'; // Device name for diagnostic END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VGC; (* All valve data and states will be in this struct*) {attribute 'pytmc' := ' pv: MPS_FAULT_OK '} xMPS_OK: BOOL; (*MPS Fast OK, is set when the Valve is Open*) END_VAR VAR_IN_OUT io_fbFFHWO : FB_HardwareFFOutput; END_VAR VAR // PMPS fbFF : FB_FastFault :=( i_DevName := 'VGC Aptr', i_Desc := 'Fault occurs when the valve is not in open state', i_TypeCode := 16#1010); //g_FastFaultOutput1 : FB_HardwareFFOutput; {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; rDiffPressAllowed : REAL := 22.5; // Torr, Default value comes from Vat Valve Manual rDiffPress : REAL; set : BOOL; reset: BOOL; xFirstPass : BOOL; fbFSInit : R_TRIG; tonDelOK : TON; rtOK : R_TRIG; tonOvrd : TON; tDelOK : TIME := T#60S; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; // For logging fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_VGC; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, PUMP_RUN, etc.) tOverrideActivated : R_TRIG; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; END_VAR (* Vacuum gate valve A. Wallace 16-10-29 A gate valve isolates vacuum volumes. Ideally it can be opened when a system is vented to allow for faster pumping, and will close when high vacuum is lost. The following behavior is good for valves in something like a gas attenuator. This function block does the following: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint. Alternatively, the differential pressure interlock can be disabled so the valve may only be opened if the pressure on both sides is lower than the at-vacuum-setpoint. You want this behavior if the valve is to be used in a UHV section. Hysteresis is employed to ensure a smooth transition from vented/pumping down, to at-vac. Finally, an override system is built in so you can bypass all the interlocking logic and get back online. *) (* 10/1/2018 Margaret Ghaly included EPS, PMPS Checkes and MPS trigger signals*) fbFSInit( CLK := TRUE, Q => xFirstPass); (*IO Mapping*) ACT_IO(); (* On first PLC pass, put valve into vented state, which implies a closed valve *) IF xFirstPass THEN iq_stValve.eVGC_State := Vented; iq_stValve.pv_xOPN_SW := FALSE; fbFF.i_DevName := i_sDevName; END_IF ///Check valve position IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN_F; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF // Update hysteresis /////////////////////////////////////////////////// IF iq_stValve.rAT_VAC_SP_LAST <> iq_stValve.rAT_VAC_SP OR xFirstPass THEN iq_stValve.rAT_VAC_SP_LAST := iq_stValve.rAT_VAC_SP; iq_stValve.rAT_VAC_HYS := iq_stValve.rHYST_PERC * iq_stValve.rAT_VAC_SP; END_IF iq_stValve.rAT_VAC_HYS := LIMIT(0, iq_stValve.rAT_VAC_HYS, iq_stValve.rAT_VAC_SP); IF iq_stValve.rAT_VAC_SP <> 0 THEN IF iq_stValve.rHYST_PERC <> (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) THEN iq_stValve.rHYST_PERC := LIMIT(0, (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) ,1); END_IF END_IF // Valve at vacuum check /////////////////////////////////////////////////// set := i_stUSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stDSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC S= set; reset := i_stUSG.rPRESS > iq_stValve.rAT_VAC_SP OR i_stDSG.rPRESS > iq_stValve.rAT_VAC_SP OR NOT i_stUSG.xPRESS_OK OR NOT i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC R= reset; // Differential pressure check /////////////////////////////////////////////////// (* Calc the differential pressure across the valve *) rDiffPress := ABS(i_stUSG.rPRESS - i_stDSG.rPRESS); (* As long as the differential pressure is less than 30mbar, the valve is allowed to open *) IF rDiffPress <= rDiffPressAllowed AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK THEN iq_stValve.xDP_OK := TRUE; ELSE iq_stValve.xDP_OK := FALSE; (* If the differential pressure is exceeded, even when the valve is open, the state is unexpected and triggers a fault, because it suggests that something is wrong with the valve, or gauges or mapping or there is a major leak on one side.*) iq_stValve.eVGC_State := ERR_DiffPress; END_IF // Valve state /////////////////////////////////////////////////// CASE iq_stValve.eVGC_State OF Vented: (* The Vented state is used during pump down *) //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Vented', eSevr:=TcEventSeverity.Info); ePrevState := iq_stValve.eVGC_State; END_IF (* Assuming the pump down went well, we are now at vacuum on both sides, so we move to the vacuum state, otherwise remain in the vented state *) IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) and (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF AtVacuum: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Vacuum setpoint satisfied', eSevr:=TcEventSeverity.Info); ePrevState := iq_stValve.eVGC_State; END_IF IF iq_stValve.xAT_VAC THEN (* If both pressure setpoints are made, then enable the differential pressure interlock, regardless of the valve state assuming we're using some kind of ion gauge where a pressure setpoint cannot be higher than 1E-4 T *) iq_stValve.eVGC_State := AtVacuum; ELSE (* If the valve is open (or in an unknown state) and either gauge is not at it's vacuum setpoint, we have a loss of vacuum error *) IF (iq_stValve.i_xOpnLS OR (NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.i_xClsLS)) THEN iq_stValve.eVGC_State := ERR_LostVac; iq_stValve.xERR_SP := TRUE; //TAW changed this to the ERR_SP bool because I think that's what it was intended to be? (* Alternatively, if the valve is already closed and we lose pressure on one side, it was probably intentional venting, so we calmly move back to venting mode, and disable the differential pressure interlock *) //?? ELSIF iq_stValve.i_xClsLS THEN iq_stValve.eVGC_State := Vented; END_IF END_IF ERR_DiffPress: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Potential accidental vent.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_DifPres) THEN iq_stValve.eVGC_State := Vented; END_IF ERR_LostVac: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Unexpected loss of vacuum while valve was open.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_SP) THEN iq_stValve.eVGC_State := Vented; END_IF ERR_ExtFault: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Lost external interlock while valve was open.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_ExtFault) AND (NOT i_xExt_OK) THEN iq_stValve.eVGC_State := Vented; END_IF END_CASE IF (NOT i_xExt_OK) THEN iq_stValve.eVGC_State := ERR_ExtFault; iq_stValve.xERR_ExtFault := TRUE; END_IF // Interlock evaluation - with bypass for DP ILK bypass /////////////////////////////////////////////// IF i_xDis_DPIlk THEN iq_stValve.xOPN_OK := iq_stValve.xEXT_OK; // AND iq_stValve.xDP_OK ; ELSE iq_stValve.xOPN_OK := iq_stValve.xEXT_OK AND iq_stValve.xAT_VAC; END_IF (* Valve operation *) (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=tDelOK); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND iq_stValve.pv_xOvrdOpn THEN iq_stValve.pv_xOvrdOpn :=FALSE; if (iq_stValve.eState = OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //for seamless transition //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=tOvrd); (* If Epics Command to close the Valve, check if PMPS and EPS are ok, otherwise Keep command set to open valve *) IF NOT(iq_stValve.pv_xOPN_SW) AND ((NOT i_xPMPS_OK) OR (NOT i_xEPS_OK)) THEN //iq_stValve.pv_xOPN_SW := TRUE; // plc to only reset never to set this signal END_IF (* Reset the EPICS command to open the valve if the interlock is lost *) (* based on EPS ok state, EPS overrides OPN_OK*) IF NOT iq_stValve.xOPN_OK THEN IF i_xEPS_OK THEN iq_stValve.pv_xOPN_SW := FALSE; iq_stValve.sErrorMessage := 'ILK Active'; ELSE //iq_stValve.pv_xOPN_SW := TRUE; // plc to only reset never to set this signal END_IF END_IF (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); (* When the valve is open MPS is OK*) xMPS_OK := i_xOpnLS XOR i_xClsLS; ///Check valve moving postion timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := 'Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := 'Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := CONCAT(sPath,'Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF // Alarm reset ////////////////////////////////////// ACT_ResetAlarms(); (*IO Mapping*) ACT_IO(); (*FAST FAULT*) fbFF(i_xOK := xMPS_OK, i_xReset := i_xReset, i_xAutoReset :=TRUE, io_fbFFHWO := io_fbFFHWO); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; iq_stValve.xEXT_OK := i_xEXT_OK; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; END_ACTION ACTION ACT_ResetAlarms: iq_stValve.xERR_DifPres R= iq_stValve.pv_xAlmRst; iq_stValve.xERR_SP R= iq_stValve.pv_xAlmRst; iq_stValve.bErrorPresent R= iq_stValve.pv_xAlmRst; IF ( iq_stValve.pv_xAlmRst) THEN iq_stValve.sErrorMessage :=''; END_IF END_ACTION FB_VGC_EBD ^^^^^^^^^^ :: (*Deprecated*) (* This function block is similar to the FB_VGC, except it has 2 outputs to actuate the Valve*) (* This function block implements basic functionality for Isolation Gate Valves*) (* This function block interlock is as follows: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint.*) (*This function block also implements PMPS and EPS interlocks, as well as Fast MPS trigger*) {attribute 'no_check'} FUNCTION_BLOCK FB_VGC_EBD VAR_IN_OUT END_VAR VAR_INPUT (*Upstream Gauge, usually ion gauge*) i_stUSG : ST_VG; (*Downstream Gauge, usually ion gauge*) i_stDSG : ST_VG; {attribute 'pytmc' := ' pv: Dis_DPIlk '} i_xDis_DPIlk : BOOL := FALSE; (* Set to true when calling the function to disable the differential pressure interlock *) {attribute 'pytmc' := ' pv: EPS_OK '} i_xEPS_OK: BOOL := TRUE; (*External EPS interlock, Set to TRUE when no EPS interlock is required, otherwise set to correct interlock signal*) {attribute 'pytmc' := ' pv: MPS_OK '} i_xPMPS_OK: BOOL ; (*External MPS interlock, Set to TRUE when no PMPS interlock is required*) i_xExt_OK: BOOL; (*Other External Interlock, Set to True when no external interlock is required*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) // Reset fault {attribute 'pytmc' := ' pv: FF_Reset '} i_xReset: BOOL; i_sDevName : T_MaxString := 'VGC'; // Device name for diagnostic END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VGC; (* All valve data and states will be in this struct*) {attribute 'pytmc' := ' pv: MPS_FAULT_OK field: ZNAM MPS FAULT ; field: ONAM MPS OK ; '} xMPS_OK: BOOL; (*MPS Fast OK, is set when the Valve is Open*) END_VAR VAR_IN_OUT io_fbFFHWO : FB_HardwareFFOutput; END_VAR VAR // PMPS fbFF : FB_FastFault :=( i_DevName := 'VGC', i_Desc := 'Fault occurs when the valve is not in open state', i_TypeCode := 16#1010); {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; rDiffPressAllowed : REAL := 22.5; // Torr, Default value comes from Vat Valve Manual rDiffPress : REAL; set : BOOL; reset: BOOL; xFirstPass : BOOL; fbFSInit : R_TRIG; tonDelOK : TON; rtOK : R_TRIG; tonOvrd : TON; tDelOK : TIME := T#60S; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; // For logging fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM); ePrevState : E_VGC; tErrorPresent : R_TRIG; tAction : R_TRIG; // Primary action of this device (OPN_DO, PUMP_RUN, etc.) tOverrideActivated : R_TRIG; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; q_xOPN_DO_2 AT%Q*: BOOL; END_VAR (* Vacuum gate valve A. Wallace 16-10-29 A gate valve isolates vacuum volumes. Ideally it can be opened when a system is vented to allow for faster pumping, and will close when high vacuum is lost. The following behavior is good for valves in something like a gas attenuator. This function block does the following: 1. The valve can be opened when the difference between the pressures on both sides is less than the maximum differential pressure. 2. This rule persists until the pressures on both sides are lower than the vacuum-setpoint. 3. Once at-vac, the valve will close if the pressure on either side rises above the setpoint. Alternatively, the differential pressure interlock can be disabled so the valve may only be opened if the pressure on both sides is lower than the at-vacuum-setpoint. You want this behavior if the valve is to be used in a UHV section. Hysteresis is employed to ensure a smooth transition from vented/pumping down, to at-vac. Finally, an override system is built in so you can bypass all the interlocking logic and get back online. *) (* 10/1/2018 Margaret Ghaly included EPS, PMPS Checkes and MPS trigger signals*) fbFSInit( CLK := TRUE, Q => xFirstPass); (*IO Mapping*) ACT_IO(); (* On first PLC pass, put valve into vented state, which implies a closed valve *) IF xFirstPass THEN iq_stValve.eVGC_State := Vented; iq_stValve.pv_xOPN_SW := FALSE; fbFF.i_DevName := i_sDevName; END_IF ///Check valve postion IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF // Update hysteresis /////////////////////////////////////////////////// IF iq_stValve.rAT_VAC_SP_LAST <> iq_stValve.rAT_VAC_SP OR xFirstPass THEN iq_stValve.rAT_VAC_SP_LAST := iq_stValve.rAT_VAC_SP; iq_stValve.rAT_VAC_HYS := iq_stValve.rHYST_PERC * iq_stValve.rAT_VAC_SP; END_IF iq_stValve.rAT_VAC_HYS := LIMIT(0, iq_stValve.rAT_VAC_HYS, iq_stValve.rAT_VAC_SP); IF iq_stValve.rAT_VAC_SP <> 0 THEN IF iq_stValve.rHYST_PERC <> (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) THEN iq_stValve.rHYST_PERC := LIMIT(0, (iq_stValve.rAT_VAC_HYS / iq_stValve.rAT_VAC_SP) ,1); END_IF END_IF // Valve at vacuum check /////////////////////////////////////////////////// set := i_stUSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stDSG.rPRESS < iq_stValve.rAT_VAC_HYS AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC S= set; reset := i_stUSG.rPRESS > iq_stValve.rAT_VAC_SP OR i_stDSG.rPRESS > iq_stValve.rAT_VAC_SP OR NOT i_stUSG.xPRESS_OK OR NOT i_stDSG.xPRESS_OK; iq_stValve.xAT_VAC R= reset; // Differential pressure check /////////////////////////////////////////////////// (* Calc the differential pressure across the valve *) rDiffPress := ABS(i_stUSG.rPRESS - i_stDSG.rPRESS); (* As long as the differential pressure is less than 30mbar, the valve is allowed to open *) IF rDiffPress <= rDiffPressAllowed AND i_stUSG.xPRESS_OK AND i_stDSG.xPRESS_OK THEN iq_stValve.xDP_OK := TRUE; ELSE iq_stValve.xDP_OK := FALSE; (* If the differential pressure is exceeded, even when the valve is open, the state is unexpected and triggers a fault, because it suggests that something is wrong with the valve, or gauges or mapping or there is a major leak on one side.*) iq_stValve.eVGC_State := ERR_DiffPress; iq_stValve.xERR_DifPres := TRUE; END_IF // Valve state /////////////////////////////////////////////////// CASE iq_stValve.eVGC_State OF Vented: (* The Vented state is used during pump down *) //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Vented', eSevr:=TcEventSeverity.Info); ePrevState := iq_stValve.eVGC_State; END_IF (* Assuming the pump down went well, we are now at vacuum on both sides, so we move to the vacuum state, otherwise remain in the vented state *) IF iq_stValve.xAT_VAC AND (NOT (iq_stValve.xERR_DifPres)) AND (NOT (iq_stValve.xERR_SP)) and (NOT (iq_stValve.xERR_ExtFault))THEN iq_stValve.eVGC_State := AtVacuum; ELSE iq_stValve.eVGC_State := Vented; END_IF AtVacuum: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Vacuum setpoint satisfied', eSevr:=TcEventSeverity.Info); ePrevState := iq_stValve.eVGC_State; END_IF IF iq_stValve.xAT_VAC THEN (* If both pressure setpoints are made, then enable the differential pressure interlock, regardless of the valve state assuming we're using some kind of ion gauge where a pressure setpoint cannot be higher than 1E-4 T *) iq_stValve.eVGC_State := AtVacuum; ELSE (* If the valve is open (or in an unknown state) and either gauge is not at it's vacuum setpoint, we have a loss of vacuum error *) IF (iq_stValve.i_xOpnLS OR (NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.i_xClsLS)) THEN iq_stValve.eVGC_State := ERR_LostVac; iq_stValve.xERR_SP := TRUE; //TAW changed this to the ERR_SP bool because I think that's what it was intended to be? (* Alternatively, if the valve is already closed and we lose pressure on one side, it was probably intentional venting, so we calmly move back to venting mode, and disable the differential pressure interlock *) //?? ELSIF iq_stValve.i_xClsLS THEN iq_stValve.eVGC_State := Vented; END_IF END_IF ERR_DiffPress: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Potential accidental vent.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_DifPres) THEN iq_stValve.eVGC_State := Vented; END_IF ERR_LostVac: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Unexpected loss of vacuum while valve was open.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_SP) THEN iq_stValve.eVGC_State := Vented; END_IF ERR_ExtFault: //Log new state IF ePrevState <> iq_stValve.eVGC_State THEN fbLogger(sMsg:='Lost external interlock while valve was open.', eSevr:=TcEventSeverity.Critical); ePrevState := iq_stValve.eVGC_State; END_IF IF NOT (iq_stValve.xERR_ExtFault) AND (NOT i_xExt_OK) THEN iq_stValve.eVGC_State := Vented; END_IF END_CASE IF (NOT i_xExt_OK) THEN iq_stValve.eVGC_State := ERR_ExtFault; iq_stValve.xERR_ExtFault := TRUE; END_IF // Interlock evaluation - with bypass for DP ILK bypass /////////////////////////////////////////////// IF i_xDis_DPIlk THEN iq_stValve.xOPN_OK := iq_stValve.xEXT_OK; // AND iq_stValve.xDP_OK ; ELSE iq_stValve.xOPN_OK := iq_stValve.xEXT_OK AND iq_stValve.xAT_VAC; END_IF (* Valve operation *) (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=tDelOK); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND iq_stValve.pv_xOvrdOpn THEN iq_stValve.pv_xOvrdOpn :=FALSE; if (iq_stValve.eState = OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //for seamless transition //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=tOvrd); (* If Epics Command to close the Valve, check if PMPS and EPS are ok, otherwise Keep command set to open valve *) IF NOT(iq_stValve.pv_xOPN_SW) AND ((NOT i_xPMPS_OK) OR (NOT i_xEPS_OK)) THEN //iq_stValve.pv_xOPN_SW := TRUE; // plc to only reset never to set this signal END_IF (* Reset the EPICS command to open the valve if the interlock is lost *) (* based on EPS ok state, EPS overrides OPN_OK*) IF NOT iq_stValve.xOPN_OK THEN IF i_xEPS_OK THEN iq_stValve.pv_xOPN_SW := FALSE; iq_stValve.sErrorMessage := 'ILK Active'; ELSE //iq_stValve.pv_xOPN_SW := TRUE; // plc to only reset never to set this signal END_IF END_IF (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); (* When the valve is open MPS is OK*) xMPS_OK := (iq_stValve.eState=OPEN) OR (iq_stValve.eState=OPEN_F);//iq_stValve.i_xOpnLS; ///Check valve moving postion timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := CONCAT(sPath,' Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF // Alarm reset ////////////////////////////////////// ACT_ResetAlarms(); (*IO Mapping*) ACT_IO(); (*FAST FAULT*) fbFF(i_xOK := xMPS_OK, i_xReset := i_xReset, i_xAutoReset :=TRUE, io_fbFFHWO := io_fbFFHWO); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; iq_stValve.xEXT_OK := i_xEXT_OK; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; q_xOPN_DO_2:= iq_stValve.q_xOPN_DO; (*ILK Devices*) iq_stValve.sIlkUSDeviceName := This^.i_stUSG.sPath; iq_stValve.sIlkDSDeviceName := This^.i_stDSG.sPath; END_ACTION ACTION ACT_ResetAlarms: iq_stValve.xERR_DifPres R= iq_stValve.pv_xAlmRst; iq_stValve.xERR_SP R= iq_stValve.pv_xAlmRst; iq_stValve.bErrorPresent R= iq_stValve.pv_xAlmRst; IF ( iq_stValve.pv_xAlmRst) THEN iq_stValve.sErrorMessage :=''; END_IF END_ACTION FB_VGC_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_VGC_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR VGC: FB_VGC; US_IG : ST_VG; DS_IG : ST_VG; io_fbFFHWO : FB_HardwareFFOutput; TotalTests : INT:=0; ex : __SYSTEM.ExceptionCode; END_VAR M_INIT(); M_Interlock(); END_FUNCTION_BLOCK FB_VRC ^^^^^^ :: (* This Function Block Implements Basic Functionality for certain types of valves e.g Turbo Isolation valves, Apperture Valve. This function block is interloked by an input (i_xExtILK_OK). This is so developers can interface with custom interlocking logic outside this function block.*) (* Note Interlock Logic is External *) {attribute 'no_check'} FUNCTION_BLOCK FB_VRC EXTENDS FB_Valve VAR_IN_OUT END_VAR VAR_INPUT i_xExtILK_OK : BOOL; //Connect to Interlock logic condition(e.g F_TURBO_VRC_ILK Function), otherwise, Set to True if the valve is not interlocked i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VRC; END_VAR VAR {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; xFirstPass : BOOL; fbFSInit : R_TRIG; tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; END_VAR (* On first PLC pass, put valve into vented state, which implies a closed valve *) fbFSInit( CLK := TRUE, Q => xFirstPass); IF xFirstPass THEN iq_stValve.eVGC_State := Vented; iq_stValve.pv_xOPN_SW := FALSE; ePrevState := INVALID; END_IF ///Check valve position IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN_F; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF (*evaluate Valve open external interlock*) iq_stValve.xOPN_OK := i_xExtILK_OK; iq_stValve.xEXT_OK := i_xExtILK_OK; IF NOT iq_stValve.xOPN_OK and NOT tonOvrd.Q THEN iq_stValve.pv_xOPN_SW := FALSE; iq_stValve.eVGC_State := ERR_ExtFault; END_IF IF (iq_stValve.q_xOPN_DO) AND (iq_stValve.xOPN_OK) THEN iq_stValve.eVGC_State := AtVacuum; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND iq_stValve.pv_xOvrdOpn THEN iq_stValve.pv_xOvrdOpn :=FALSE; if (iq_stValve.eState=OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF // Release the Force Open bit when the system override is false IF NOT(i_xOverrideMode) THEN iq_stValve.pv_xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=tOvrd); (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); ///Check valve moving position timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := CONCAT(sPath,' Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); (*Soft IO Mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT i_xExtILK_OK AND ePrevState = OPEN THEN fbLogger(sMsg:='Lost external interlock while valve was open.', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> iq_stValve.eState THEN CASE iq_stValve.eState OF INVALID: fbLogger(sMsg:='Valve invalid position.', eSevr:=TcEventSeverity.Critical); MOVING: fbLogger(sMsg:='Valve moving', eSevr:=TcEventSeverity.Warning); OPEN: fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info); CLOSED: fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stValve.eState; END_IF // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF END_ACTION FB_VRC_EBD ^^^^^^^^^^ :: (* This function block is similar to the FB_VGC, except it has 2 outputs to actuate the Valve*) (* This Function Block Implements Basic Functionality for certain types of valves e.g Turbo Isolation valves, Apperture Valve *) (* Note Interlock Logic is External *) FUNCTION_BLOCK FB_VRC_EBD EXTENDS FB_Valve VAR_IN_OUT END_VAR VAR_INPUT i_xExtILK_OK : BOOL; //Connect to Interlock logic condition(e.g F_TURBO_VRC_ILK Function), otherwise, Set to True if the valve is not interlocked i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VRC; END_VAR VAR {attribute 'instance-path'} {attribute 'noinit'} sPath: STRING; xFirstPass : BOOL; fbFSInit : R_TRIG; tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xOPN_DO AT%Q*: BOOL; q_xOPN_DO_2 AT%Q*: BOOL; END_VAR (* On first PLC pass, put valve into vented state, which implies a closed valve *) fbFSInit( CLK := TRUE, Q => xFirstPass); IF xFirstPass THEN iq_stValve.eVGC_State := Vented; iq_stValve.pv_xOPN_SW := FALSE; END_IF ///Check valve position IF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=OPEN; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF (*evaluate Valve open external interlock*) iq_stValve.xOPN_OK := i_xExtILK_OK; iq_stValve.xEXT_OK := i_xExtILK_OK; IF NOT iq_stValve.xOPN_OK and NOT tonOvrd.Q THEN iq_stValve.pv_xOPN_SW := FALSE; iq_stValve.eVGC_State := ERR_ExtFault; END_IF IF (iq_stValve.q_xOPN_DO) AND (iq_stValve.xOPN_OK) THEN iq_stValve.eVGC_State := AtVacuum; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND iq_stValve.pv_xOvrdOpn THEN iq_stValve.pv_xOvrdOpn :=FALSE; if (iq_stValve.eState=OPEN) AND (i_xOverrideMode) THEN iq_stValve.pv_xOPN_SW := TRUE; END_IF //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=T#10S); (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); ///Check valve moving postion timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := ' Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; iq_stValve.sErrorMessage := CONCAT(sPath,' Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); (*Soft IO Mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; q_xOPN_DO_2:= iq_stValve.q_xOPN_DO; END_ACTION ACTION ACT_Logger: // ILK logger IF NOT i_xExtILK_OK AND ePrevState = OPEN THEN fbLogger(sMsg:='Lost external interlock while valve was open.', eSevr:=TcEventSeverity.Critical); END_IF //STATE Logger IF ePrevState <> iq_stValve.eState THEN CASE iq_stValve.eState OF INVALID: fbLogger(sMsg:='Valve invalid position.', eSevr:=TcEventSeverity.Critical); MOVING: fbLogger(sMsg:='Valve moving', eSevr:=TcEventSeverity.Warning); OPEN: fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info); CLOSED: fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info); END_CASE ePrevState := iq_stValve.eState; END_IF // Log valve timeouts tErrorPresent(CLK:=iq_stValve.bErrorPresent); IF tErrorPresent.Q THEN fbLogger(sMsg:=iq_stValve.sErrorMessage, eSevr:=TcEventSeverity.Warning); END_IF // Log valve open tAction(CLK:= iq_stValve.q_xOPN_DO); IF tAction.Q THEN fbLogger(sMsg:='Valve commanded open', eSevr:=TcEventSeverity.Info); END_IF // Log override mode enabled tOverrideActivated(CLK:= (tonOvrd.Q AND i_xOverrideMode)); IF tOverrideActivated.Q THEN fbLogger(sMsg:='Valve override mode activated', eSevr:=TcEventSeverity.Warning); END_IF END_ACTION FB_VRC_NO ^^^^^^^^^ :: (* This function block is different from the regular VRC in that CLOSING must be permitted. *) FUNCTION_BLOCK FB_VRC_NO EXTENDS FB_Valve VAR_IN_OUT END_VAR VAR_INPUT {attribute 'pytmc' := ' pv: EXT_ILK_OK ; field: ZNAM NOT OK ; field: ONAM OK ; io: i ; '} i_xExtILK_OK : BOOL; //Connect to Interlock logic condition(e.g F_TURBO_VRC_ILK Function), otherwise, Set to True if the valve is not interlocked i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*) END_VAR VAR_OUTPUT iq_stValve : ST_VRC; END_VAR VAR {attribute 'noinit'} sPath: STRING; xFirstPass : BOOL; fbFSInit : R_TRIG; tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; tOvrd : TIME := T#10s; (* Timeouts*) tTimeOutDuration: TIME:= T#30S; tOPNtimeout: TON; tCLStimeout:TON; {attribute 'pytmc' := ' pv: CLS_SW ; field: ONAM CLOSE; field: ZNAM OPEN; io: io ; '} pv_xCLS_SW : BOOL; {attribute 'pytmc' := ' pv: FORCE_CLS; io: io; '} pv_xOvrdCls : BOOL; (*IO*) i_xOpnLS AT%I*: BOOL; i_xClsLS AT%I*: BOOL; q_xCLS_DO AT%Q*: BOOL; END_VAR (* This function block is different from the regular VRC in that CLOSING must be permitted. *) (* On first PLC pass, put valve into vented state, which implies a closed valve *) fbFSInit( CLK := TRUE, Q => xFirstPass); IF xFirstPass THEN pv_xCLS_SW := FALSE; END_IF ///Check valve position IF iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS THEN iq_stValve.eState:=INVALID; ELSIF NOT iq_stValve.i_xClsLS AND iq_stValve.i_xOpnLS AND iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=OPEN; ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND NOT iq_stValve.q_xOPN_DO THEN iq_stValve.eState:=CLOSED; ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS THEN iq_stValve.eState:=MOVING; ELSE iq_stValve.eState:=INVALID; END_IF (*evaluate Valve CLose external interlock*) iq_stValve.xCLS_OK := i_xExtILK_OK; IF NOT iq_stValve.xCLS_OK and NOT tonOvrd.Q THEN pv_xCLS_SW := FALSE; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xCLS_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q AND pv_xOvrdCls THEN pv_xOvrdCls :=FALSE; if (iq_stValve.eState=CLOSED) AND (i_xOverrideMode) THEN pv_xCLS_SW := TRUE; END_IF //Log fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning); END_IF //Override timer tonOvrd(IN:=pv_xOvrdCls, PT:=tOvrd); (* Here's where the valve Actuates *) q_xCLS_DO := ((iq_stValve.xCLS_OK AND pv_xCLS_SW)OR (tonOvrd.Q AND i_xOverrideMode)); iq_stValve.q_xOPN_DO := NOT q_xCLS_DO; ///Check valve moving position timout IF NOT iq_stValve.i_xClsLS AND tCLStimeout.Q THEN iq_stValve.bErrorPresent := TRUE; //iq_stValve.sErrorMessage := ' Close Timeout'; ELSIF NOT iq_stValve.i_xOpnLS AND tOPNtimeout.Q THEN iq_stValve.bErrorPresent := TRUE; //iq_stValve.sErrorMessage := ' Open Timeout'; END_IF IF (iq_stValve.eState=INVALID) THEN iq_stValve.bErrorPresent := TRUE; //iq_stValve.sErrorMessage := CONCAT(sPath,' Invalid Valve Position'); END_IF (*Timers*) tOPNtimeout(IN:= iq_stValve.q_xOPN_DO, PT := tTimeOutDuration ); tCLStimeout(IN:= NOT iq_stValve.q_xOPN_DO, PT := tTimeOutDuration); (*Soft IO Mapping*) ACT_IO(); // Log States and triggers ACT_Logger(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.i_xOpnLS := i_xOpnLS; iq_stValve.i_xClsLS:= i_xClsLS; //iq_stValve. := i_xEXT_OK; iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) //q_xOPN_DO:= iq_stValve.q_xOPN_DO; END_ACTION FB_VRC_OK_CLS ^^^^^^^^^^^^^ :: (* This function block is different from the regular VRC in that CLOSING must be permitted. *) FUNCTION_BLOCK FB_VRC_OK_CLS VAR_IN_OUT iq_stValve : ST_VRC; END_VAR VAR_INPUT i_xOverrideMode : BOOL; END_VAR VAR_OUTPUT END_VAR VAR tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; END_VAR (* This function block is different from the regular VRC in that CLOSING must be permitted. *) (* Implemented for the funky valve 04 on LAMP *) IF NOT iq_stValve.xOPN_OK and NOT tonOvrd.Q THEN iq_stValve.pv_xOPN_SW := FALSE; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q THEN iq_stValve.pv_xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.pv_xOvrdOpn, PT:=T#10S); (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := NOT iq_stValve.xCLS_OK OR ((iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode)); END_FUNCTION_BLOCK FB_VRC_Test ^^^^^^^^^^^ :: FUNCTION_BLOCK FB_VRC_Test EXTENDS TcUnit.FB_TestSuite VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR VRC: FB_VRC; ex : __SYSTEM.ExceptionCode; END_VAR M_INIT(); M_Interlock(); END_FUNCTION_BLOCK FB_VVC ^^^^^^ :: (* This Function Block Implements Basic Functionality for Vent Valves VVC. This function block is also intended for the general use case of solenoid driven (non-variable) valves that lack status readbacks or position indicators. *) (* Note Interlock Logic is External *) FUNCTION_BLOCK FB_VVC VAR_IN_OUT END_VAR VAR_INPUT i_xExtILK_OK:BOOL; (*Other External Interlock, Set to True when no external interlock is required*) i_xOverrideMode : BOOL; (*To be linked to global override bit. This Overrides Vacuum logic only*) END_VAR VAR_OUTPUT {attribute 'pytmc' := ' pv: '} iq_stValve : ST_VVC; END_VAR VAR tonOvrd : TON; tonDelOK : TON; rtOK : R_TRIG; (*IO*) q_xOPN_DO AT%Q*: BOOL; pv_xOvrdOpn: INT; END_VAR iq_stValve.xOPN_OK := i_xExtILK_OK; IF NOT iq_stValve.xOPN_OK THEN iq_stValve.pv_xOPN_SW := FALSE; END_IF (* Override logic *) (* Goal: give ability to override, but do so in a way that people won't forget it. Solution: Override only after ten seconds of override, protect against blips, when the valve permission goes true for more than ten seconds consistently, remove override *) tonDelOK(IN:=iq_stValve.xOPN_OK, PT:=T#10S); rtOK(CLK:=tonDelOK.Q); IF rtOK.Q THEN iq_stValve.xOvrdOpn :=FALSE; END_IF // Release the Force Open bit when the system override is false IF NOT (i_xOverrideMode) THEN iq_stValve.xOvrdOpn :=FALSE; END_IF //Override timer tonOvrd(IN:=iq_stValve.xOvrdOpn, PT:=T#10S); (* Here's where the valve opens *) iq_stValve.q_xOPN_DO := (iq_stValve.pv_xOPN_SW AND iq_stValve.xOPN_OK) OR (tonOvrd.Q AND i_xOverrideMode); (*IO Mapping*) ACT_IO(); END_FUNCTION_BLOCK ACTION ACT_IO: (*inputs*) iq_stValve.xOverrideMode := i_xOverrideMode; (*outputs*) q_xOPN_DO:= iq_stValve.q_xOPN_DO; END_ACTION PRG_Test ^^^^^^^^ :: PROGRAM PRG_Test VAR fb_VGC_Test: FB_VGC_Test; fb_VRC_Test: FB_VRC_Test; fb_PIP_Test: FB_PIP_Test; fb_PTM_Test: FB_PTM_Test; fb_GPI_Test:FB_GPI_Test; fb_GCC_Test:FB_GCC_Test; TotalTests : INT:=0; END_VAR TcUnit.RUN(); END_PROGRAM V_TO_INT ^^^^^^^^ :: {attribute 'no_check'} FUNCTION V_TO_INT : INT VAR_INPUT analog : REAL; END_VAR VAR iBits :INT := 32767; iFullRange:INT := 10; END_VAR if ( iFullRange = 0 ) THEN iFullRange := 10;END_IF V_TO_INT := REAL_TO_INT(analog * 32767 /iFullRange); END_FUNCTION