DUTs
E_2OO3_MODE
TYPE E_2OO3_MODE :
(
MODE_2OO3 := 0,
IG1 := 1,
IG2 := 2,
IG3 := 3
);
END_TYPE
- Related:
E_ControlMode
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_ControlMode :
(
Init := 0,
Synch := 1,
ManualControl := 2,
CloseValve := 3,
OpenValve := 4,
PressureControl := 5,
HoldValve := 6,
LearnProcedure := 7,
InterlockOpen := 8,
InterlockClose := 9,
MaintenanceOpen := 10,
MaintenanceClose := 11,
PowerFailure := 12,
Safety := 13,
FatalError := 14
) USINT := Init;
END_TYPE
E_ControlModeSP
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_ControlModeSP :
(
ManualControl := 2,
CloseValve := 3,
OpenValve := 4,
PressureControl := 5,
HoldValve := 6,
LearnProcedure := 7
) USINT := CloseValve;
END_TYPE
E_FatalErrors
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_FatalErrors :
(
NO_FATAL_ERROR := 0,
NoStopDetectedSynch := 20,
ValveBlockedSynch := 21,
ValveBlocked := 22,
StepLossSynch := 25,
MotorDriverFault := 40
) UINT := NO_FATAL_ERROR;
END_TYPE
E_PressureState
TYPE E_PressureState :
(
// Invalid states
Off,// //gc_GaugeValidState - 1,
GaugeDisconnected, //gc_GaugeValidState -2,
OoR, //gc_GaugeValidState -6,
// Ion gauges
PressInvalid, //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
- Related:
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_VAT590_AccessMode
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_VAT590_AccessMode :
(
LOCAL := 0,
REMOTE := 1,
LOCKED := 2
) USINT := REMOTE;
END_TYPE
E_VAT590_PID_CTRL
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_VAT590_PID_CTRL :
(
ADAPTIVE_DS := 0,
FIXED_1 := 1,
FIXED_2 := 2,
SOFT_PUMP := 3
) USINT := FIXED_1;
END_TYPE
E_VCN
TYPE E_VCN :
(
CloseValve := 0,
OpenValve := 1,
PressureControl := 2,
ManualControl := 3
);
END_TYPE
E_VCN_VAT590
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_VCN_VAT590 :
(
ManualControl := 0,
CloseValve := 1,
OpenValve := 2,
PressureControl := 3,
HoldValve := 4
) USINT := CloseValve;
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
- Related:
ST_CoEIndSub
TYPE ST_CoEIndSub :
STRUCT
nIndex : WORD;
nSubIndex : BYTE;
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
- Related:
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;
{attribute 'pytmc' := '
pv: RST_SW;
field: ZNAM FALSE;
field: ONAM TRUE;
io: io;
'}
q_xReset : BOOL; //For resetting faults
END_STRUCT
END_TYPE
- Related:
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
- Related:
ST_ExtendedWarnings
TYPE ST_ExtendedWarnings :
STRUCT
Bit0RemoteControlNotPossible: BIT;
Bit1ControlModeSPNotAllowed : BIT;
Bit2ZeroDisabled : BIT;
Bit3PFODeactivated : BIT;
Bit4Reserved : BIT;
Bit5PressureSPOutOfRange : BIT;
Bit6PositionSPOutOfRange : BIT;
Bit7Reserved : BIT;
Bit8Reserved : BIT;
Bit9Reserved : BIT;
Bit10CtrlSPOutOfRange : BIT;
Bit11GeneralCtrlSPOutOfRange : BIT;
Bit12ProcDataSettingsNotvalid : BIT;
Bit13Reserved5 : BIT;
Bit14Reserved6 : BIT;
Bit15Reserved7 : BIT;
END_STRUCT
END_TYPE
ST_GeneralControlSP
TYPE ST_GeneralControlSP :
STRUCT
Bit0ExecuteZero: BIT;
Bit1Reserved : BIT;
Bit2PingPongTxBit : BIT;
Bit3Reserved : BIT;
Bit4AccessMode : BIT;
Bit5Reserved : BIT;
Bit6Reserved : BIT;
Bit7Reserved : BIT;
Bit8Reserved : BIT;
Bit9Reserved : BIT;
Bit10Reserved : BIT;
Bit11Reserved : BIT;
Bit12Reserved : BIT;
Bit13Reserved : BIT;
Bit14Reserved : BIT;
Bit15Reserved : BIT;
END_STRUCT
END_TYPE
ST_GeneralStatus
TYPE ST_GeneralStatus :
STRUCT
Bit0FieldBusDatavalid: BIT;
Bit1ZeroExecuted : BIT;
Bit2PingPongRxBit : BIT;
Bit3PressureSimulation : BIT;
Bit4PressureSPReached : BIT;
Bit5Reserved : BIT;
Bit6Reserved : BIT;
Bit7AccessModeBit0 : BIT;
Bit8AccessModeBit1 : BIT;
Bit9WarningsActive : BIT;
Bit10Reserved : BIT;
Bit11Reserved : BIT;
Bit12Reserved : BIT;
Bit13Reserved : BIT;
Bit14Reserved : BIT;
Bit15Reserved : BIT;
END_STRUCT
END_TYPE
ST_GeneralWarnings
TYPE ST_GeneralWarnings :
STRUCT
Bit0ServiceRequest: BIT;
Bit1Reserved : BIT;
Bit2Reserved : BIT;
Bit3PowerFailureBattery : BIT;
Bit4Reserved : BIT;
Bit5Reserved : BIT;
Bit6Reserved : BIT;
Bit7Reserved : BIT;
Bit8Reserved : BIT;
Bit9Reserved : BIT;
Bit10SensMeasUnitFault : BIT;
Bit11Reserved : BIT;
Bit12Reserved : BIT;
Bit13Reserved : BIT;
Bit14Reserved : BIT;
Bit15Reserved : BIT;
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_xRemote : 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
- Related:
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: PWR_RBV;
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;
i_xRemote : BOOL; (* Remote control enabled *)
END_STRUCT
END_TYPE
- Related:
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: PWR_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;
{attribute 'pytmc' := '
pv: SET_SPEED;
io: io;
field: EGU "Hz"
'}
i_dSetSpd : DINT;
{attribute 'pytmc' := '
pv: SET_SPEED_MODE;
io: io;
field: ZNAM FALSE;
field: ONAM TRUE;
'}
q_bSetSpd : BOOL;
//Controls
q_uiPowerPct : UINT := 100; //Should normally be 100
END_STRUCT
END_TYPE
- Related:
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:io;
'}
xAutoOn : BOOL:=TRUE;
{attribute 'pytmc' := '
pv: AutoOn_timer;
io:i;
'}
iAutoOnTimer : INT;
{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
- Related:
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
- Related:
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
- Related:
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
- Related:
ST_VAT590_COE
TYPE ST_VAT590_COE :
STRUCT
{attribute 'pytmc' := '
pv: PID_CTRL
io: io
'}
ePIDCtrl: E_VAT590_PID_CTRL;
(*Adaptive Downstream Parameters*)
{attribute 'pytmc' := '
pv: SENS_DELAY_AD
io: io
field: EGU "s"
field: DRVL 0.0
field: DRVH 1.0
'}
fSensorDelay_AD : REAL;
{attribute 'pytmc' := '
pv: RAMP_TIME_AD
io: io
field: EGU "s"
field: DRVL 0.0
field: DRVH 1000000.0
'}
fRampTime_AD : REAL;
{attribute 'pytmc' := '
pv: RAMP_MODE_AD
io: io
field: ZNAM CONST_TIME
field: ONAM CONST_SLOPE
'}
bRampMode_AD : BOOL;
{attribute 'pytmc' := '
pv: GAIN_FACTOR_AD
io: io
field: PREC 4
field: DRVL 0.0001
field: DRVH 7.5
'}
fGainFactor_AD : REAL;
(*Fixed 1 Parameters*)
{attribute 'pytmc' := '
pv: RAMP_TIME_FIX1
io: io
field: EGU "s"
field: DRVL 0.0
field: DRVH 1000000.0
'}
fRampTime_FIX1 : REAL;
{attribute 'pytmc' := '
pv: RAMP_MODE_FIX1
io: io
field: ZNAM CONST_TIME
field: ONAM CONST_SLOPE
'}
bRampMode_FIX1 : BOOL;
{attribute 'pytmc' := '
pv: CTRL_DIR_FIX1
io: io
field: ZNAM DOWNSTREAM
field: ONAM UPSTREAM
'}
bCtrlDir_FIX1 : BOOL;
{attribute 'pytmc' := '
pv: PGAIN_FIX1
io: io
field: DRVL 0.001
field: DRVH 100.0
'}
fPGain_FIX1 : REAL;
{attribute 'pytmc' := '
pv: IGAIN_FIX1
io: io
field: DRVL 0.0
field: DRVH 100.0
'}
fIGain_FIX1 : REAL;
(*Fixed 2 Parameters*)
{attribute 'pytmc' := '
pv: RAMP_TIME_FIX2
io: io
field: EGU "s"
field: DRVL 0.0
field: DRVH 1000000.0
'}
fRampTime_FIX2 : REAL;
{attribute 'pytmc' := '
pv: RAMP_MODE_FIX2
io: io
field: ZNAM CONST_TIME
field: ONAM CONST_SLOPE
'}
bRampMode_FIX2 : BOOL;
{attribute 'pytmc' := '
pv: CTRL_DIR_FIX2
io: io
field: ZNAM DOWNSTREAM
field: ONAM UPSTREAM
'}
bCtrlDir_FIX2 : BOOL;
{attribute 'pytmc' := '
pv: PGAIN_FIX2
io: io
field: DRVL 0.001
field: DRVH 100.0
'}
fPGain_FIX2 : REAL;
{attribute 'pytmc' := '
pv: IGAIN_FIX2
io: io
field: DRVL 0.0
field: DRVH 100.0
'}
fIGain_FIX2 : REAL;
(*Soft Pump Parameters*)
{attribute 'pytmc' := '
pv: RAMP_TIME_SP
io: io
field: EGU "s"
field: DRVL 0.0
field: DRVH 1000000.0
'}
fRampTime_SP : REAL;
{attribute 'pytmc' := '
pv: RAMP_MODE_SP
io: io
field: ZNAM CONST_TIME
field: ONAM CONST_SLOPE
'}
bRampMode_SP : BOOL;
{attribute 'pytmc' := '
pv: PGAIN_SP
io: io
field: DRVL 0.001
field: DRVH 100.0
'}
fPGain_SP : REAL;
(*Position Limit Parameters*)
{attribute 'pytmc' := '
pv: POSITION_LIMIT_OPN
io: io
field: EGU "%"
field: DRVL 0.0
field: DRVH 100.0
'}
fPositionLimitOpen : REAL;
{attribute 'pytmc' := '
pv: POSITION_LIMIT_OPN_CTRL
io: io
field: EGU "%"
field: DRVL 0.0
field: DRVH 100.0
'}
fPositionLimitOpenControl : REAL;
END_STRUCT
END_TYPE
- Related:
ST_VAT590_STATUS
TYPE ST_VAT590_STATUS :
STRUCT
{attribute 'pytmc' := '
pv: CTRL_MODE
io: i
'}
eControlMode : E_ControlMode; // Valve control mode readback
{attribute 'pytmc' := '
pv: FATAL_ERR
io: i
'}
eFatalErrors : E_FatalErrors; // Valve Fatal Error status readback
(*General Status readback*)
{attribute 'pytmc' := '
pv: GEN_STATUS:ECAT_DATA_VALID
io: i
field: ZNAM NOT_VALID
field: ONAM VALID
field: ZSV MAJOR
field: OSV NO_ALARM
'}
bFieldBusDatavalid: BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:ZERO_EXECUTED
io: i
field: ZNAM NOT_EXECUTED
field: ONAM EXECUTED
field: ZSV NO_ALARM
field: OSV NO_ALARM
'}
bZeroExecuted : BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:ECAT_RxBIT
io: i
field: ZNAM NOT_INVERTED
field: ONAM INVERTED
field: ZSV NO_ALARM
field: OSV NO_ALARM
'}
bPingPongRxBit : BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:PRES_SIM
io: i
field: ZNAM OFF
field: ONAM ON
field: ZSV NO_ALARM
field: OSV NO_ALARM
'}
bPressureSimulation : BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:PRES_SP_REACHED
io: i
field: ZNAM NOT_AT_SP
field: ONAM AT_SP
field: ZSV NO_ALARM
field: OSV NO_ALARM
'}
bPressureSPReached : BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:WARN_STATUS
io: i
field: ZNAM NOT_ACTIVE
field: ONAM ACTIVE
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bWarningsActive : BOOL;
{attribute 'pytmc' := '
pv: GEN_STATUS:REM_CTRL
io: i
'}
eRemoteStatus : E_VAT590_AccessMode;
(*General Warnings readback*)
{attribute 'pytmc' := '
pv: GEN_WARN:SERVICE_REQ
io: i
field: ZNAM OK
field: ONAM SERVICE
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bServiceRequest: BOOL;
{attribute 'pytmc' := '
pv: GEN_WARN:POWER_FAIL_BATT
io: i
field: ZNAM READY
field: ONAM NOT_READY
field: ZSV NO_ALARM
field: OSV MINOR
'}
bPowerFailureBattery : BOOL;
{attribute 'pytmc' := '
pv: GEN_WARN:ADC_UNIT_STATUS
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bSensMeasUnitFault : BOOL;
(*Extended Warnings readback*)
{attribute 'pytmc' := '
pv: EXT_WARN:REM_NOT_POSSIBLE
io: i
field: ZNAM ENABLED
field: ONAM DISABLED
field: ZSV NO_ALARM
field: OSV MINOR
'}
bRemoteControlNotPossible: BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:CTRL_SP_NOT_ALLOWED
io: i
field: ZNAM ENABLED
field: ONAM DISABLED
field: ZSV NO_ALARM
field: OSV MINOR
'}
bControlModeSPNotAllowed : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:ZERO_STATUS
io: i
field: ZNAM ENABLED
field: ONAM DISABLED
field: ZSV NO_ALARM
field: OSV MINOR
'}
bZeroDisabled : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:PFO_STATUS
io: i
field: ZNAM ENABLED
field: ONAM DISABLED
field: ZSV NO_ALARM
field: OSV MINOR
'}
bPFODeactivated : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:PRES_SP_OOR
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bPressureSPOutOfRange : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:POS_SP_OOR
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bPositionSPOutOfRange : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:CTRL_SP_OOR
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bCtrlSPOutOfRange : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:GENCTRL_SP_OOR
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bGeneralCtrlSPOutOfRange : BOOL;
{attribute 'pytmc' := '
pv: EXT_WARN:PROC_DATA_NOT_VALID
io: i
field: ZNAM OK
field: ONAM FAULT
field: ZSV NO_ALARM
field: OSV MAJOR
'}
bProcDataSettingsNotvalid : BOOL;
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
{attribute 'pytmc' := '
pv: ILK_SP;
io: o;
field: HOPR 1000
field: LOPR 0
field: PREC 2
field: EGU "TORR"
autosave_pass1: VAL DESC
'}
rIlk_SP : REAL := 0.0005; // Interlock setpoint for gauges on both sides of valve
{attribute 'pytmc' := '
pv: Limit;
io: io;
autosave_pass1: VAL DESC
'}
rUpperLimit : REAL:=100; // Percentage//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
- Related:
ST_VCN_VAT590
TYPE ST_VCN_VAT590 :
STRUCT
(* VCN - Valve Controlled Needle
Used for VAT590 needle valves *)
{attribute 'pytmc' := '
pv: POS_RAW
io: i
'}
nRawPosition : DINT; //Position readback
{attribute 'pytmc' := '
pv: PRES_RAW
io: i
'}
nRawPressure : DINT; //Pressure readback
{attribute 'pytmc' := '
pv: PRES_TORR
field: EGU "TORR"
io: i
'}
fPressure : REAL; //Pressure readback in Torr units
{attribute 'pytmc' := '
pv: POS_SP
io: i
'}
nPositionSP : DINT; //Position setpoint readback
{attribute 'pytmc' := '
pv: PRES_SP
io: i
'}
nPressureSP : DINT; //Pressure setpoint readback
{attribute 'pytmc' := '
pv: ILK_OK
field: ZNAM NOT OK
field: ONAM OK
field: ZSV MAJOR
field: OSV NO_ALARM
io: i
'}
bIlkOK : BOOL := FALSE; //Interlock Bit status
{attribute 'pytmc' := '
pv: POS_LIMIT
io: io
autosave_pass1: VAL DESC
'}
fPosSetLimit : REAL := 100; //Percentage Upper limit on valve open
{attribute 'pytmc' := '
pv: PRES_SET_LIMIT
io: io
autosave_pass1: VAL DESC
'}
fPresSetLimit : REAL := 1E-2; //Upper limit for pressure control
{attribute 'pytmc' := '
pv: PRES_RDBK_LIMIT
io: io
autosave_pass1: VAL DESC
'}
fPresRdbkLimit : REAL := 1E-4; //Upper limit for pressure reading before valve close
{attribute 'pytmc' := '
pv: POS_REQ
field: DRVL 0.0
field: DRVH 100.0
io: io
'}
fReqPosition : REAL; //Requested position (0.0-100.0%)
{attribute 'pytmc' := '
pv: PRES_REQ
field: EGU "TORR"
io: io;
'}
fReqPressure : REAL; //Requested pressure in Torr units
{attribute 'pytmc' := '
pv: STATE
io: io
'}
eValveControl : E_VCN_VAT590; //Valve control mode
{attribute 'pytmc' := '
pv:
'}
stValveStatus : ST_VAT590_STATUS; //All Valve status readbacks
{attribute 'pytmc' := '
pv: REM_CTRL
io: io
'}
eRemoteControl : E_VAT590_AccessMode; //Select remote control mode
{attribute 'pytmc' := '
pv: ZERO
io: io
'}
bZero : BOOL; //Activate Zero function bit
{attribute 'pytmc' := '
pv: COE_PARAM
'}
stCoeParameters : ST_VAT590_COE; //Coe Parameters
{attribute 'pytmc' := '
pv: COE_ERROR
field: ZNAM OK
field: ONAM ERROR
field: ZSV NO_ALARM
field: OSV MINOR
io: i
'}
bCoeError : BOOL; //Coe Interface Error
{attribute 'pytmc' := '
pv: COE_RESET
io: io
'}
bResetCoeError : BOOL; //Reset Coe Interface Error
{attribute 'pytmc' := '
pv: ETHCAT_COMM_STATUS
field: ZNAM OK
field: ONAM ERROR
field: ZSV NO_ALARM
field: OSV MAJOR
io: i
'}
bValveEthCatStatus : BOOL; //Status of the Ethercat Communication with device
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;
{attribute 'pytmc' := '
pv: VETO_DEVICE;
io: i;
'}
sVetoDeviceName: STRING;
END_STRUCT
END_TYPE
- Related:
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 Off;
field: ONST GaugeDisconnected;
field: TWST OoR;
field: THST PressInvalid;
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
{attribute 'pytmc' := '
pv: Auto_On;
field: ZNAM FALSE;
field: ONAM TRUE;
io:io;
'}
xAutoOn : BOOL := TRUE;
{attribute 'pytmc' := '
pv: AutoOn_timer;
io:i;
'}
iAutoOnTimer : INT;
/// 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
- Related:
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;
sDevName :STRING;
(*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
- Related:
ST_VGC_2S
TYPE ST_VGC_2S EXTENDS ST_VGC:
STRUCT
{attribute 'pytmc' := '
pv: AT_VAC_SP_DS;
io: o;
field: HOPR 1000
field: LOPR 0
field: PREC 2
field: EGU "TORR"
autosave_pass1: VAL DESC
'}
rAT_VAC_SP_DS : REAL := 0.000001; // Interlock setpoint for gauges on the downstream side of the valve
{attribute 'pytmc' := '
pv: AT_VAC_HYS_DS;
io: o;
field: HOPR 1000
field: LOPR 0
field: PREC 2
field: EGU "TORR"
autosave_pass1: VAL DESC
'}
rAT_VAC_HYS_DS : REAL :=0.000001; // Hysteresis of the vacuum sp downstream
rAT_VAC_SP_LAST_DS : REAL :=0.000001 ;
END_STRUCT
END_TYPE
- Related:
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
- Related:
ST_VRC_NO
TYPE ST_VRC_NO EXTENDS ST_ValveBase:
STRUCT
(* Readbacks *)
//In case VRC is normally open
{attribute 'pytmc' := '
pv: CLS_OK;
field: ONAM TRUE;
field: ZNAM FALSE;
io: i;
'}
xCLS_OK : BOOL := TRUE;
{attribute 'pytmc' := '
pv: CLS_SW ;
field: ONAM CLOSE;
field: ZNAM OPEN;
io: io ;
'}
pv_xCLS_SW : BOOL;
{attribute 'pytmc' := '
pv: CLS_DO ;
field: ONAM CLOSE;
field: ZNAM OPEN;
io: io ;
'}
q_xCLS_DO : BOOL;
{attribute 'pytmc' := '
pv: FORCE_CLS;
field: ONAM FORCE CLOSE;
field: ZNAM FALSE;
io: io;
'}
pv_xOvrdCls : BOOL;
END_STRUCT
END_TYPE
- Related:
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 FORCE OPEN;
io: io;
'}
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 *)
{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
ST_VWC_NO
TYPE ST_VWC_NO EXTENDS ST_VGC:
STRUCT
(* Readbacks *)
//In case VRC is normally open
{attribute 'pytmc' := '
pv: CLS_OK;
field: ONAM CLOSE;
field: ZNAM OPEN;
io: i;
'}
xCLS_OK : BOOL := TRUE;
{attribute 'pytmc' := '
pv: CLS_SW ;
field: ONAM CLOSE;
field: ZNAM OPEN;
io: io ;
'}
pv_xCLS_SW : BOOL;
{attribute 'pytmc' := '
pv: FORCE_CLS;
field: ONAM FORCE CLOSE;
field: ZNAM FALSE;
io: io;
'}
pv_xOvrdCls : BOOL;
END_STRUCT
END_TYPE
- Related:
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;
TaskInfo:PlcTaskSystemInfo;
END_VAR
- Related:
Global_Version
{attribute 'TcGenerated'}
{attribute 'no-analysis'}
{attribute 'linkalways'}
// This function has been automatically generated from the project information.
VAR_GLOBAL CONSTANT
{attribute 'const_non_replaced'}
stLibVersion_LCLS_Vacuum : ST_LibVersion := (iMajor := 2, iMinor := 3, iBuild := 5, iRevision := 0, nFlags := 1, sVersion := '2.3.5');
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
- Related:
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
- Related:
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
- Related:
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
- Related:
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
- Related:
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
- Related:
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
- Related:
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
- Related:
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
- Related:
FB_901_ECAT
(* works for 901 with Ethercat interface*)
{attribute 'no_check'}
FUNCTION_BLOCK FB_901_ECAT EXTENDS FB_GaugeBase
VAR_INPUT
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
PG : ST_VG;
END_VAR
VAR_IN_OUT
END_VAR
VAR
bInit: BOOL:= TRUE;(*IO*)
xPress_OK AT%I* : BOOL;
xOverrange AT%I* : BOOL;
xUnderrange AT%I* : BOOL;
i_iPRESS_R AT%I* : REAL; // input Pressure // Link to analog Input
bWcstate AT%I* : BOOL; // 0: date valid, 1: date invalid
END_VAR
(* Standard MKS 9XX series conversion *)
(* works for 901 with Ethercat interface*)
// no div by zero
PG.rPRESS :=i_iPRESS_R;
PG.xPRESS_OK := xPress_OK and NOT bWcstate ;
IF xPress_OK and NOT bWcstate THEN
PG.xPRESS_OK := TRUE;
PG.eState := Valid; //Press OK
ELSIF xOverrange and NOT bWcstate THEN
PG.xPRESS_OK := FALSE;
PG.eState := OoR;
ELSIF xUnderrange and NOT bWcstate THEN
PG.xPRESS_OK := FALSE;
PG.eState := PressInvalid;
END_IF
(*Logger*)
ACT_Logger();
(* Soft IO Mapping*)
ACT_IO();
END_FUNCTION_BLOCK
ACTION ACT_Init:
IF (bInit) THEN
bInit := False;
//set pressure units to Torr
// value 0x00A10000 SDO index 0xf840, si 0x01 UDINT
END_IF
END_ACTION
ACTION ACT_IO:
IF PG.xPRESS_OK THEN
PG.i_iPRESS_R := REAL_TO_INT(i_iPRESS_R);
ELSE
PG.rPRESS := 0.0;
END_IF
PG.sPath := sPath;
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
- Related:
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) AND (rV <= 7) 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;
IF (rV <= 1) THEN PG.eState := GaugeDisconnected;
ELSE PG.eState := OoR;
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;
PG.sPath := sPath;
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
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
rVMin : REAL:= 0.9;
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
VAR CONSTANT
(*Default set point 50 mT*)
rDefaultVAC_SP: REAL := 5E-2;
END_VAR
(* Standard MKS 9XX series conversion *)
(* works for 972 and 925 *)
//Default setpoint
IF PG.rVAC_SP = 0 THEN
PG.rVAC_SP := rDefaultVAC_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 >=rVMin THEN
IF rV >= rVMin 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
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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_CCM501
(* This function is for the Cold Cathode CCM 501 fro instrutech (hornet). *)
(*This function provides ILK and Set Point Protection for the Cold Cathode*)
FUNCTION_BLOCK FB_CCM501 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
tRecoverDelay:TIME:=T#600S; (*Delay Time after the first cycle to start the device. Default is 600S*)
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
IG : ST_VG;
END_VAR
VAR
rV : REAL;
GaugeTurnOnTmr : TON;
tStartupTimer:TON;
iTermBits: UINT := 30518 ; // 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
binit:BOOL:=TRUE;
END_VAR
VAR CONSTANT
// Ranges 1.8 to 8.7 Vdc, 0.8V/decade analog output - Torr
vBase : REAL := 1.25;
vDisconnected : REAL:= 1.8;
vTorr : REAL := 12.875;
vGaugeOff: REAL := 10.0;
//vNoDischarge: REAL := 9.3;
rMinPressure: REAL := 1E-10;
cDefaultPressure : REAL := 0;
rDeadband : REAL := 1.0;
END_VAR
VAR PERSISTENT
bWasOn : BOOL;
bAutoRecover:BOOL;
bAutoRecoverWrite:BOOL;
END_VAR
(*Startup time*)
IF (bInit) THEN
tStartupTimer.IN:=TRUE;
bInit :=FALSE;
END_IF
tStartupTimer(PT:=T#3S);
If (iTermBits=0) THEN iTermBits := 30518;END_IF
rV := 10*INT_TO_REAL(IG.i_iPRESS_R)/iTermBits;
(* Set Guage State based on the Analog voltage*)
(* Set Guage State based on the Analog voltage*)
IF rV <= vDisconnected AND IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := ValidLo;
IG.rPRESS := rMinPressure;
ELSIF rV <= vDisconnected AND Not IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := GaugeDisconnected;
IG.rPRESS := cDefaultPressure;
ELSIF rV > vDisconnected AND rV < (vGaugeOff -rDeadband) AND IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := Valid;
IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV*vBase)- vTorr)));
ELSIF rV >= (vGaugeOff -rDeadband) THEN
IG.eState := Off;
IG.rPRESS := cDefaultPressure;
ELSE
IG.eState := OoR;
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 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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Run Auto Recovery*)
ACT_Recover();
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
tState(CLK:= ePrevState <> IG.eState);
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF ( rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF ( rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF ( rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
IF ( bAutoRecoverWrite) THEN
IG.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP:= IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR:= IG.rHYS_PR;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (bAutoRecover = IG.xAutoOn) THEN
bAutoRecover := IG.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT:=T#10S);
IF (stateTimer.Q) THEN
bWasOn := (IG.eState >=Valid );
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(IN:= , PT:=tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover ) THEN
This^.M_Recover();
tRecover.IN := FALSE;
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
METHOD M_HVE : BOOL
VAR_INPUT
enable:bool; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW:= enable;
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_HVE(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
FB_CVM201
(* 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 CVM 201 convectron SuperBee*)
{attribute 'no_check'}
FUNCTION_BLOCK FB_CVM201 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;
(*Default set point 50 mT*)
rDefaultVAC_SP: REAL := 5E-2;
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rPRO_SP <> 0) THEN
PG.rPRO_SP := rPRO_SP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (PG.rVAC_SP = rVAC_SP) THEN
rVAC_SP:= PG.rVAC_SP;
END_IF;
END_ACTION
ACTION IO:
PG.i_iPRESS_R :=i_iPRESS_R;
PG.sPath := sPath;
END_ACTION
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
- Related:
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;
stPump.q_xRemote := 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;
q_xResetAlarm:=stPump.q_xReset;
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
- Related:
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
- Related:
FB_GaugeBase
{attribute 'reflection'}
FUNCTION_BLOCK FB_GaugeBase
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;
tState : R_TRIG;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath: STRING;
// For Persistent Data
bRestorePersistentData : BOOL:=TRUE;
stateTimer:TON;
fbGetCurTaskIdx : GETCURTASKINDEX;
fbWritePersistentData : WritePersistentData;
tRecover: TON;
END_VAR
VAR PERSISTENT
rVAC_SP : REAL;
rPRO_SP : REAL;
rHYS_PR: REAL;
END_VAR
END_FUNCTION_BLOCK
- Related:
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
METHOD M_GetBit : BOOL
VAR_INPUT
iIndex:INT;
iBit:INT;
END_VAR
Case iBit OF
nZero:
M_GetBit:=i_PLC_nBits[iIndex].nZero;
nOne:
M_GetBit:=i_PLC_nBits[iIndex].nOne;
nTwo:
M_GetBit:=i_PLC_nBits[iIndex].nTwo;
nThree:
M_GetBit:=i_PLC_nBits[iIndex].nThree;
nFour:
M_GetBit:=i_PLC_nBits[iIndex].nFour;
nFive:
M_GetBit:=i_PLC_nBits[iIndex].nFive;
nSix:
M_GetBit:=i_PLC_nBits[iIndex].nSix;
nSeven:
M_GetBit:=i_PLC_nBits[iIndex].nSeven;
END_CASE
END_METHOD
- Related:
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
METHOD M_CheckRange
VAR
Expected:REAL;
Actual:REAL;
eStateExpected: E_PressureState;
eStateActual: E_PressureState;
END_VAR
TEST('Check_Range');
Case This^.cycle of
12:
This^.i_iPRESS_R := V_TO_INT(0.61);
This^.PG.rPRESS := 1E-4;
This^.PG.xPRESS_OK := True;
fb_MKS422.M_HVE(TRUE);
15:
TEST('MKS422_Range_937B');
Expected :=1E-11 ;
eStateExpected := Valid;
Actual := This^.fb_MKS422.IG.rPRESS;
eStateActual := This^.fb_MKS422.IG.eState;
AssertTrue(This^.fb_MKS422.IG.xPRESS_OK, 'MKS422B xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS422B eState Fail');
AssertEquals_REAL(Expected, Actual,1E-12, 'MKS422B rPress Fail');
TEST_FINISHED_NAMED('MKS422_Range_937B');
16:
This^.i_iPRESS_R := V_TO_INT(2.4);
This^.fb_MKS422.b937A:=TRUE;
20:
TEST('MKS422_Range_937A');
Expected :=1E-8 ;
Actual := This^.fb_MKS422.IG.rPRESS;
eStateExpected := Valid;
eStateActual := This^.fb_MKS422.IG.eState;
AssertTrue(This^.fb_MKS422.IG.xPRESS_OK, 'MKS422A xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS422A eState Fail');
AssertEquals_REAL(Expected, Actual,1E-9, 'MKS422A rPress Fail');
TEST_FINISHED_NAMED('MKS422_Range_937A');
21:
This^.i_iPRESS_R := V_TO_INT(0.1);
24:
TEST('MKS422_Range_Off');
Expected :=0 ;
eStateExpected := GaugeDisconnected;
Actual := This^.fb_MKS422.IG.rPRESS;
eStateActual := This^.fb_MKS422.IG.eState;
AssertFalse(This^.fb_MKS422.IG.xPRESS_OK, 'MKS422A xPRESS_OK is set to True');
AssertEquals(eStateExpected,eStateActual,'MKS422A eState Fail');
AssertEquals_REAL(Expected, Actual,1, 'MKS422A rPress Fail');
TEST_FINISHED_NAMED('MKS422_Range_Off');
25:
This^.i_iPRESS_R := V_TO_INT(10);
29:
TEST('MKS422_Range_Invalid');
Expected :=0 ;
eStateExpected := PressInvalid;
Actual := This^.fb_MKS422.IG.rPRESS;
eStateActual := This^.fb_MKS422.IG.eState;
AssertFalse(This^.fb_MKS422.IG.xPRESS_OK, 'MKS422A xPRESS_OK is set to True');
AssertEquals(eStateExpected,eStateActual,'MKS422A eState Fail');
AssertEquals_REAL(Expected, Actual,1, 'MKS422A rPress Fail');
TEST_FINISHED_NAMED('MKS422_Range_Invalid');
TEST_FINISHED_NAMED('Check_Range');
END_CASE
This^.fb_MKS422(PG:=This^.PG, IG=>);
END_METHOD
METHOD M_INIT
VAR
END_VAR
TEST('MKS422_Startup');
This^.PG.rPRESS := 1E-4;
This^.PG.xPRESS_OK := TRUE;
This^.fb_MKS422(PG:=This^.PG, IG=>);
IF( cycle = 3) THEN
AssertFalse(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to True');
AssertTrue(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to False');
TEST_FINISHED_NAMED('MKS422_Startup');
End_IF;
END_METHOD
METHOD M_Interlock
VAR_INPUT
END_VAR
TEST('MKS422_Interlock_Active');
TEST('MKS422_Interlock_OK');
TEST('MKS422_Interlock_Lost');
Case cycle of
1:
This^.PG.rPRESS := 1E-1;
This^.PG.xPRESS_OK := TRUE;
fb_MKS422.M_HVE(TRUE);
3:
AssertFalse(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to True');
AssertTrue(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to False');
TEST_FINISHED_NAMED('MKS422_Interlock_Active');
4:
This^.PG.rPRESS := 1E-4;
This^.PG.xPRESS_OK := True;
fb_MKS422.M_HVE(TRUE);
7:
AssertTrue(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to False');
AssertFalse(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to True');
TEST_FINISHED_NAMED('MKS422_Interlock_OK');
8:
This^.PG.rPRESS := 1E-1;
This^.PG.xPRESS_OK := False;
This^.fb_MKS422(PG:=This^.PG, IG=>);
11:
AssertFalse(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to True');
AssertTrue(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to False');
TEST_FINISHED_NAMED('MKS422_Interlock_Lost');
END_CASE
This^.fb_MKS422(PG:=This^.PG, IG=>);
END_METHOD
METHOD M_SelfProtection
VAR_INPUT
END_VAR
// Switching the Cold cathode backon
TEST('MKS422_Interlock_OK_1');
This^.PG.rPRESS := 1E-4;
This^.PG.xPRESS_OK := True;
fb_MKS422.M_HVE(TRUE);
This^.fb_MKS422(PG:=This^.PG, IG=>);
AssertTrue(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to False');
AssertFalse(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to True');
TEST_FINISHED_NAMED('MKS422_Interlock_OK_1');
TEST('MKS422_Self_Protection');
This^.PG.rPRESS := 1E-3;
This^.PG.xPRESS_OK := TRUE;
This^.fb_MKS422(PG:=This^.PG, IG=>);
AssertFalse(This^.fb_MKS422.IG.xHV_SW, 'MKS422 xHV_SW set to True');
AssertTrue(This^.fb_MKS422.IG.q_xHV_DIS, 'MKS422 q_xHV_DIS set to False');
TEST_FINISHED_NAMED('MKS422_Self_Protection');
END_METHOD
- Related:
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;
PG.eState := Valid;
ELSE
PG.xPRESS_OK := FALSE;
PG.eState := PressInvalid;
END_IF
(*IO soft linking*)
ACT_IO();
END_FUNCTION_BLOCK
ACTION ACT_IO:
PG.i_iPRESS_R :=i_iPRESS_R;
END_ACTION
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
METHOD M_CheckRange
VAR
Expected:REAL;
Actual:REAL;
eStateExpected: E_PressureState;
eStateActual: E_PressureState;
END_VAR
TEST('MKS275_Check_Range');
Case cycle of
4:
This^.i_iPRESS_R := V_TO_INT(3.674);
Expected :=5 ;
eStateExpected := Valid;
7:
TEST('MKS275_Range_R2');
Actual := This^.fb_MKS275.PG.rPRESS;
eStateActual := This^.fb_MKS275.PG.eState;
AssertTrue(This^.fb_MKS275.PG.xPRESS_OK, 'MKS275 xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS275 eState Fail');
AssertEquals_REAL(Expected, Actual,0.1, 'MKS275 rPress Fail');
TEST_FINISHED_NAMED('MKS275_Range_R2');
8:
This^.i_iPRESS_R := V_TO_INT(5.614);
Expected :=900 ;
eStateExpected := Valid;
12:
TEST('MKS275_Range_R3');
Actual := This^.fb_MKS275.PG.rPRESS;
eStateActual := This^.fb_MKS275.PG.eState;
AssertTrue(This^.fb_MKS275.PG.xPRESS_OK, 'MKS275 xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS275 eState Fail');
AssertEquals_REAL(Expected, Actual,1, 'MKS275 rPress Fail');
TEST_FINISHED_NAMED('MKS275_Range_R3');
13:
This^.i_iPRESS_R := V_TO_INT(0.3);
Expected :=-1 ;
eStateExpected := GaugeDisconnected;
17:
TEST('MKS275_Range_Off');
Actual := This^.fb_MKS275.PG.rPRESS;
eStateActual := This^.fb_MKS275.PG.eState;
AssertTrue(This^.fb_MKS275.PG.xPRESS_OK, 'MKS275 xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS275 eState Fail');
AssertEquals_REAL(Expected, Actual,1, 'MKS275 rPress Fail');
TEST_FINISHED_NAMED('MKS275_Range_Off');
18:
This^.i_iPRESS_R := V_TO_INT(5.66);
Expected :=-1 ;
eStateExpected := OoR;
22:
TEST('MKS275_Range_OoR');
Actual := This^.fb_MKS275.PG.rPRESS;
eStateActual := This^.fb_MKS275.PG.eState;
AssertTrue(This^.fb_MKS275.PG.xPRESS_OK, 'MKS275 xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS275 eState Fail');
AssertEquals_REAL(Expected, Actual,1, 'MKS275 rPress Fail');
TEST_FINISHED_NAMED('MKS275_Range_OoR');
TEST_FINISHED_NAMED('MKS275_Check_Range');
END_CASE
This^.fb_MKS275(PG=>);
This^.fb_MKS317(PG=>);
END_METHOD
METHOD M_INIT
VAR
Expected:REAL;
Actual:REAL;
eStateExpected: E_PressureState;
eStateActual: E_PressureState;
END_VAR
// Start the Test
TEST('MKS275_Startup');
//Assign Voltage to FB
This^.i_iPRESS_R := V_TO_INT(0.384);
// Assigne expected result corresponing to the test input
Expected :=0.001 ;
eStateExpected := Valid;
//Instantiate the function block
This^.fb_MKS275(PG=>);
// Wait for few cycles
IF( cycle = 3) THEN
// Check the results
Actual := This^.fb_MKS275.PG.rPRESS;
eStateActual := This^.fb_MKS275.PG.eState;
AssertTrue(This^.fb_MKS275.PG.xPRESS_OK, 'MKS275 xPRESS_OK is set to False');
AssertEquals(eStateExpected,eStateActual,'MKS275 eState Fail');
AssertEquals_REAL(Expected, Actual,0.0001, 'MKS275 rPress Fail');
TEST_FINISHED_NAMED('MKS275_Startup');
END_IF
END_METHOD
- Related:
FB_IMG401
(* 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
IMG401-to-EP box interface because the EP boxes do not natively support the 5v IO
signals on the gauge.
This function provides ILK and Set Point Protection for the hot Cathode.
*)
FUNCTION_BLOCK FB_IMG401 EXTENDS FB_GaugeBase
VAR_IN_OUT
END_VAR
VAR_INPUT
PG : ST_VG;
tRecoverDelay:TIME:=T#600S; (*Delay Time after the first cycle to start the device. Default is 600S*)
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
IG : ST_VG;
END_VAR
VAR
rV : REAL;
GaugeTurnOnTmr : TON;
tStartupTimer:TON;
iTermBits: UINT := 30518 ; // 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' (EL1084) ^Input
// i_xDisc_Active AT %I* : BOOL;// Discharge Current Active // 'TcLinkTo' (EL1124) ^Input
binit:BOOL:=TRUE;
END_VAR
VAR CONSTANT
vBase : REAL := 1.0;
vDisconnected : REAL:= 0.001; //shift from 0 to eliminate single noise effect
vTorr : REAL := 10;
vGaugeOff: REAL := 10;
//vNoDischarge: REAL := 9.3;
rMinPressure: REAL := 1E-10;
cDefaultPressure : REAL := 0;
rDeadband : REAL :=1.0;
END_VAR
VAR PERSISTENT
bWasOn : BOOL;
bAutoRecover:BOOL;
bAutoRecoverWrite:BOOL;
END_VAR
(*Startup time*)
IF (bInit) THEN
tStartupTimer.IN:=TRUE;
bInit :=FALSE;
END_IF
tStartupTimer(PT:=T#3S);
(* 401 Logarithmic Output Conversion, factory default configuration
EL3174, EL3174-xxxx iTermbits 30518, 10V
*)
If (iTermBits=0) THEN iTermBits := 30518;END_IF
rV := 10*INT_TO_REAL(IG.i_iPRESS_R)/iTermBits;
(* Set Guage State based on the Analog voltage*)
IF rV <= vDisconnected AND IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := ValidLo;
IG.rPRESS := rMinPressure;
ELSIF rV <= vDisconnected AND Not IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := GaugeDisconnected;
IG.rPRESS := cDefaultPressure;
ELSIF rV > vDisconnected AND rV < (vGaugeOff -rDeadband) AND IG.i_xHV_ON and IG.q_xHV_DIS THEN
IG.eState := Valid;
IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV*vBase)- vTorr)));
ELSIF rV >= (vGaugeOff -rDeadband) THEN
IG.eState := Off;
IG.rPRESS := cDefaultPressure;
ELSE
IG.eState := OoR;
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 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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Run Auto Recovery*)
ACT_Recover();
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
tState(CLK:= ePrevState <> IG.eState);
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;
stateTimer.IN := TRUE;
END_IF
END_IF
END_ACTION
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF ( rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF ( rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF ( rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
IF ( bAutoRecoverWrite) THEN
IG.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP:= IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR:= IG.rHYS_PR;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (bAutoRecover = IG.xAutoOn) THEN
bAutoRecover := IG.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT:=T#10S);
IF (stateTimer.Q) THEN
bWasOn := (IG.eState >=Valid );
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(IN:= , PT:=tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover ) THEN
This^.M_Recover();
tRecover.IN := FALSE;
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;
IG.iAutoOnTimer:= TIME_TO_INT(tRecover.PT);
END_ACTION
METHOD M_HVE : BOOL
VAR_INPUT
enable:bool; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW:= enable;
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_HVE(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
If (iTermBits=0) THEN iTermBits := 32767;END_IF
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
- Related:
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_xRemote 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_xRemote 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_xRemote := i_xRemote;
q_stPump.i_xAlarmOK := i_xAlarm;
q_stPump.i_xWarningOK := i_xWarning;
q_stPump.i_xIsRun := i_xIsRun;
END_ACTION
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.q_stPump.pv_xRunSW := run;
END_METHOD
- Related:
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
- Related:
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;
(*Default set point 50 mT*)
rDefaultVAC_SP: REAL := 5E-2;
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 := rDefaultVAC_SP;
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rPRO_SP <> 0) THEN
PG.rPRO_SP := rPRO_SP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (PG.rVAC_SP = rVAC_SP) THEN
rVAC_SP:= PG.rVAC_SP;
END_IF;
END_ACTION
ACTION IO:
PG.i_iPRESS_R :=i_iPRESS_R;
PG.sPath := sPath;
END_ACTION
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rPRO_SP <> 0) THEN
PG.rPRO_SP := rPRO_SP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (PG.rVAC_SP = rVAC_SP) THEN
rVAC_SP:= PG.rVAC_SP;
END_IF;
END_ACTION
ACTION IO:
PG.i_iPRESS_R :=i_iPRESS_R;
PG.sPath := sPath;
END_ACTION
- Related:
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 *)
{attribute 'obsolete' := 'FB_MKS317A has been deprecated and is not valid!'}
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
- Related:
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
- Related:
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
tRecoverDelay:TIME:=T#600S; (*Delay Time after the first cycle to start the device. Default is 600S*)
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-11;
vDisconnected : REAL:= 0.18;
vMaxValid : REAL:=9.6;
vMax : REAL:=9.9;
vValidLo:REAL :=0.22;
vMin : REAL:=0.6;
cDefaultPressure : REAL := 0;
END_VAR
VAR PERSISTENT
bWasOn : BOOL;
bAutoRecover:BOOL;
bAutoRecoverWrite:BOOL;
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 <= vMaxValid ) AND (rV>=vMin) AND (IG.xHV_SW) THEN
IG.eState := Valid; // normal
ELSIF rV >= vDisconnected AND rV <= vValidLo AND (IG.xHV_SW) THEN
IG.eState := ValidLo; //LO
ELSIF rV > vMaxValid AND rV<= vMax AND (IG.xHV_SW) THEN
IG.eState := ValidHi; //HIGH
ELSIF rV < vDisconnected THEN //
IG.eState := GaugeDisconnected; //other fault - gauge disconnected, controller powering up etc
IG.rPRESS :=cDefaultPressure;
ELSIF NOT (IG.xHV_SW) THEN
IG.eState := Off; //not on
IG.rPRESS :=cDefaultPressure;
ELSIF IG.rPRESS < MinPressure THEN
IG.eState := PressInvalid; //
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 <=vMax ) AND (rV>=vMin);
(* Setpoint evaluation *)
IG.xAT_VAC := IG.xPRESS_OK AND IG.rPRESS < IG.rVAC_SP;
(*Logger*)
ACT_Logger();
(*Soft IO Linking*)
// check ethercat Diagnostics
IO();
(*Load or save the persistent variables*)
ACT_Persistent();
timer(IN:= NOT IG.q_xHV_DIS, PT:= T#2s);
(*Run Auto Recovery*)
ACT_Recover();
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;
stateTimer.IN := TRUE;
END_IF
END_IF
END_ACTION
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF ( rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF ( rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF ( rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
IF ( bAutoRecoverWrite) THEN
IG.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP:= IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR:= IG.rHYS_PR;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (IG.xAutoOn = bAutoRecover) THEN
bAutoRecover := IG.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT:=T#10S);
IF (stateTimer.Q) THEN
bWasOn := (IG.eState >=Valid );
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(IN:= , PT:=tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover ) THEN
This^.M_Recover();
tRecover.IN := FALSE;
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;
IG.iAutoOnTimer:= TIME_TO_INT(tRecover.PT);
END_ACTION
METHOD M_AutoOn : BOOL
VAR_INPUT
END_VAR
this^.IG.q_xHV_DIS:= FALSE;
END_METHOD
METHOD M_HVE : BOOL
VAR_INPUT
enable:bool; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW:= (enable);
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_HVE(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
- Related:
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 because the EP boxes do not natively support the 5v IO
signals on the MKS500 gauge.
A setup process is required for the MKS500 to be usable.
See here: https://confluence.slac.stanford.edu/display/PCDS/MKS+500+-+Cold+Cathode
This function provides ILK and Set Point Protection for the Cold Cathode.
*)
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
tRecoverDelay:TIME:=T#600S; (*Delay Time after the first cycle to start the device. Default is 600S*)
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
IG : ST_VG;
END_VAR
VAR
rV : REAL;
GaugeTurnOnTmr : TON;
tStartupTimer: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
binit:BOOL:=TRUE;
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
VAR PERSISTENT
bWasOn : BOOL;
bAutoRecover:BOOL;
bAutoRecoverWrite:BOOL;
END_VAR
(*Startup time*)
IF (bInit) THEN
tStartupTimer.IN:=TRUE;
bInit :=FALSE;
END_IF
tStartupTimer(PT:=T#3S);
(* 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 NOT (tStartupTimer.Q) THEN // Give time for the gauge controller to start
IG.eState := Off;
IG.rPRESS := cDefaultPressure;
ELSIF rV < vDisconnected THEN
IG.eState := GaugeDisconnected;
IG.rPRESS := cDefaultPressure;
ELSIF rV >= vDisconnected AND rV < (vNoDischarge -rDeadband) AND (IG.xHV_SW) THEN
IG.eState := Valid;
IG.rPRESS := LREAL_TO_REAL(EXPT(10,((rV-vBase)/vSlope+LOG(pBase))));
ELSIF rV >= (vNoDischarge -rDeadband) AND rV <(vGaugeOff -rDeadband) AND (IG.xHV_SW) 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;
ELSIF IG.rPRESS < MinPressure THEN
IG.eState := PressInvalid;
//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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Run Auto Recovery*)
ACT_Recover();
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
tState(CLK:= ePrevState <> IG.eState);
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;
stateTimer.IN := TRUE;
END_IF
END_IF
END_ACTION
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF ( rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF ( rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF ( rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
IF ( bAutoRecoverWrite) THEN
IG.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP:= IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR:= IG.rHYS_PR;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (bAutoRecover = IG.xAutoOn) THEN
bAutoRecover := IG.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT:=T#10S);
IF (stateTimer.Q) THEN
bWasOn := (IG.eState >=Valid );
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(IN:= , PT:=tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover ) THEN
This^.M_Recover();
tRecover.IN := FALSE;
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;
IG.iAutoOnTimer:= TIME_TO_INT(tRecover.PT);
END_ACTION
METHOD M_HVE : BOOL
VAR_INPUT
enable:bool; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW:= enable;
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_HVE(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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 *)
{attribute 'obsolete' := 'FB_MKS500_EP has been deprecated and is not valid!'}
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 *)
{attribute 'obsolete' := 'FB_VCC_NO has been deprecated and is not valid!'}
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
METHOD M_HVE : BOOL
VAR_INPUT
enable:bool; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW:= NOT(enable);
END_METHOD
- Related:
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
- Related:
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*)
rDefaultVAC_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 := rDefaultVAC_SP;
END_IF
// check no div by zero
If (iTermBits=0) THEN iTermBits := 30518;END_IF
rV := 10*INT_TO_REAL(IG.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
IG.q_xHV_DIS := 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 := FALSE;
IG.xHV_SW := FALSE;
END_IF
ELSE
IG.q_xHV_DIS := FALSE;
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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Logger*)
THIS^.ACT_Logger();
(*Load or save the persistent variables*)
ACT_Persistent();
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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF ( rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF ( rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF ( rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP:= IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR:= IG.rHYS_PR;
END_IF;
END_ACTION
METHOD M_AutoOn : BOOL
VAR_INPUT
END_VAR
this^.IG.q_xHV_DIS:= FALSE;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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
- Related:
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
- Related:
FB_MX7M
(* This function is for the Cold Cathode MX7M from Televac. *)
(*This function provides ILK and Set Point Protection for the Cold Cathode*)
FUNCTION_BLOCK FB_MX7M EXTENDS FB_GaugeBase
VAR_IN_OUT
END_VAR
VAR_INPUT
PG : ST_VG;
tRecoverDelay : TIME := T#600S; (*Delay Time after the first cycle to start the device. Default is 600S*)
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
IG : ST_VG;
END_VAR
VAR
rV : REAL; //Calculated raw voltage from ADC on analog input
rPress : REAL; //Calculated pressure
tGaugeTurnOnTmr : TON;
iTermBits : UINT := 30518 ; //The terminal's maximum value in bits
(*IOs to be linked*)
//Controls and I/Os
i_iPRESS_R AT %I* : INT; //Pressure reading // 'TcLinkTo' (EL3174-0002 or EP3174-0002)^Input
q_xHV_DIS AT %Q* : BOOL; //Enable High Voltage when True // 'TcLinkTo' (EL2624 or EP2624-0002)^Output
END_VAR
VAR CONSTANT
//Ranges 1.0 to 10.0 Vdc (1E-11 to 1E-2) 1.0V to ~1.7V indicates that the pressure is lower than 5E-11(lowest value)
//Must set the analog output scaling to logaritmic 2!!
cSlope : REAL := 1.0;
cBaseV : REAL := 1.0;
cBaseP : REAL := 1E-11;
cMinP : REAL := 5E-11;
cGaugeMinV : REAL := 1.0;
cGaugeLowV : REAL := 1.69;
cGaugeMaxV : REAL := 10.0;
cGaugeStartingV : REAL := 10.5;
cDefaultP : REAL := 0.0;
cDeadbandV : REAL := 0.1;
END_VAR
VAR PERSISTENT
bWasOn : BOOL;
bAutoRecover : BOOL;
bAutoRecoverWrite : BOOL;
END_VAR
(*Calculate raw voltage*)
IF (iTermBits = 0) THEN iTermBits := 30518; END_IF
rV := 10*INT_TO_REAL(i_iPRESS_R)/iTermBits;
(*Calculate pressure*)
rPRESS := LREAL_TO_REAL(EXPT(10,((rV-cBaseV)/cSlope+LOG(cBaseP))));
(*Set Guage State based on the Analog voltage*)
IF IG.q_xHV_DIS THEN
IF rV >= cGaugeLowV AND rV <= cGaugeMaxV THEN
IG.eState := Valid;
IG.rPRESS := rPRESS;
ELSIF rV < cGaugeLowV AND rV >= cGaugeMinV - cDeadbandV THEN
IG.eState := Valid;
IG.rPRESS := cMinP;
ELSIF rV > cGaugeStartingV - cDeadbandV THEN
IG.eState := Starting;
IG.rPRESS := cDefaultP;
ELSIF rV < cGaugeMinV - cDeadbandV THEN
IG.eState := GaugeDisconnected;
IG.rPRESS := cDefaultP;
ELSIF rV > cGaugeMaxV AND rV < cGaugeStartingV - cDeadbandV THEN
IG.eState := OoR;
IG.rPRESS := cDefaultP;
ELSE
IG.eState := PressInvalid;
IG.rPRESS := cDefaultP;
END_IF
ELSE
IG.eState := Off;
IG.rPRESS := cDefaultP;
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*)
tGaugeTurnOnTmr(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);
(*Logging*)
ACT_Logger();
(*Soft IO Linking*)
IO();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Run Auto Recovery*)
ACT_Recover();
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rVAC_SP <> 0) THEN
IG.rVAC_SP := rVAC_SP;
END_IF;
IF (rPRO_SP <> 0) THEN
IG.rPRO_SP := rPRO_SP;
END_IF;
IF (rHYS_PR <> 0) THEN
IG.rHYS_PR := rHYS_PR;
END_IF;
IF (bAutoRecoverWrite) THEN
IG.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (IG.rVAC_SP = rVAC_SP) THEN
rVAC_SP := IG.rVAC_SP;
END_IF;
IF NOT (IG.rPRO_SP = rPRO_SP) THEN
rPRO_SP := IG.rPRO_SP;
END_IF;
IF NOT (IG.rHYS_PR = rHYS_PR) THEN
rHYS_PR := IG.rHYS_PR;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (bAutoRecover = IG.xAutoOn) THEN
bAutoRecover := IG.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT := T#10S);
IF (stateTimer.Q) THEN
bWasOn := (IG.eState >= Valid);
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s);
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(PT := tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover) THEN
This^.M_Recover();
tRecover.IN := FALSE;
END_IF
END_ACTION
ACTION IO:
(*soft link inputs*)
IG.i_iPRESS_R := i_iPRESS_R;
IG.i_xHV_ON := IG.q_xHV_DIS; //not a real readback, assuming turning on HV output, HV status is ON and vice versa
(*soft link outputs*)
q_xHV_DIS := IG.q_xHV_DIS;
(*Gauge name link*)
IG.sPath := sPath;
END_ACTION
METHOD M_HVE : BOOL
VAR_INPUT
bEnable : BOOL; // set to true to enable, false to disable;
END_VAR
this^.IG.xHV_SW := bEnable;
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_HVE(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
- Related:
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*)
tRecoverDelay:TIME:=T#900S; (*Delay Time after the first cycle to start the device. Default is 900S*)
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
rPRESS : REAL;
rV : REAL;
timer:TON;
(* 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
stateTimer:TON;
//Overrides
tonOvrd : TON;
tonDelOK : TON;
rtOK : R_TRIG;
tOvrd : TIME := T#10s;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath: STRING;
fbGetCurTaskIdx : GETCURTASKINDEX;
fbWritePersistentData : WritePersistentData;
tRecover: TON;
END_VAR
VAR CONSTANT
rDefaultHVEna_SP : REAL :=1E-4; // Default protection setpoint as per the gamma QPCe manual
END_VAR
VAR PERSISTENT
rHVEna_SP : REAL;
bWasOn : BOOL;
bAutoRecover:BOOL;
bAutoRecoverWrite:BOOL;
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 > rDefaultHVEna_SP) THEN
stPump.rHVEna_SP := rDefaultHVEna_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
stPump.q_xHVEna_DO := stPump.xHVEna_SW OR ((tonOvrd.Q AND i_xOverrideMode));
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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*Run Auto Recovery*)
ACT_Recover();
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;
stateTimer.IN := TRUE;
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_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rHVEna_SP <> 0) THEN
stPump.rHVEna_SP := rHVEna_SP;
END_IF;
IF ( bAutoRecoverWrite) THEN
stPump.xAutoOn := bAutoRecover;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (stPump.rHVEna_SP = rHVEna_SP) THEN
rHVEna_SP := stPump.rHVEna_SP;
END_IF;
IF NOT(bAutoRecoverWrite) OR NOT (stPump.xAutoOn = bAutoRecover) THEN
bAutoRecover := stPump.xAutoOn;
bAutoRecoverWrite := TRUE;
END_IF;
stateTimer(PT:=T#10S);
IF (stateTimer.Q) THEN
bWasOn := (stPump.eState = pumpRUNNING);
stateTimer.IN := FALSE;
fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
END_IF;
END_ACTION
ACTION ACT_Recover:
fbGetCurTaskIdx();
IF (TwinCAT_SystemInfoVarList._TaskInfo[fbGetCurTaskIdx.index].FirstCycle) THEN
tRecover.IN := TRUE;
END_IF
tRecover(IN:= , PT:=tRecoverDelay);
IF NOT(bAutoRecoverWrite) OR (tRecover.Q AND bAutoRecover ) THEN
This^.M_Recover();
tRecover.IN := FALSE;
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;
stPump.iAutoOnTimer:= TIME_TO_INT(tRecover.PT);
END_ACTION
METHOD M_AutoOn : BOOL
VAR_INPUT
END_VAR
this^.stPump.xAutoOn:= TRUE; // Pump is set to auto start when the pressure is low enough
END_METHOD
METHOD M_Recover : BOOL
VAR_INPUT
END_VAR
This^.M_Run(This^.bWasOn);
M_Recover := bWasOn;
IF (bWasOn) THEN fbLogger(sMsg:='Auto Recovery Initiated.', eSevr:=TcEventSeverity.Info); END_IF;
END_METHOD
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.stPump.xHVEna_SW := run;
END_METHOD
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
- Related:
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
- Related:
FB_PIP_SIP
(* This function block does basic controls FOR the ION pump connected to a SAES SIP controller.
Provides interlocking interface. Enable HV only when interlock gauge press is less than 1.0E-4 Torr *)
{attribute 'reflection'}
FUNCTION_BLOCK FB_PIP_SIP 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
rPRESS : REAL;
iTermBits: UINT := 32767 ; // The terminal's maximum value in bits
rV : REAL;
timer:TON;
(* IO Controls *)
q_xHVEna_DO AT %Q*: BOOL; // Enable High Voltage when TRUE
i_iPRESS AT %I*: INT; //
i_xIP_ON AT %I* : BOOL;
i_xError AT %I* : BOOL;
// 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
VAR CONSTANT
rDefaultHVEna_SP : REAL :=1E-4; // Default protection setpoint as per the gamma QPCe manual
END_VAR
VAR PERSISTENT
rHVEna_SP : REAL;
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 > rDefaultHVEna_SP) THEN
stPump.rHVEna_SP := rDefaultHVEna_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 OR i_xError 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();
(*Load or save the persistent variables*)
ACT_Persistent();
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_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rHVEna_SP <> 0) THEN
stPump.rHVEna_SP := rHVEna_SP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (stPump.rHVEna_SP = rHVEna_SP) THEN
rHVEna_SP := stPump.rHVEna_SP;
END_IF;
END_ACTION
ACTION ACT_SetGauge:
(*MG*)
(* convert the analog input into readable pressure*)
(* use logarithmic Pressure calculation, Output= Normal and Offset = 0*)
If (iTermBits=0) THEN iTermBits := 32767;END_IF
rV := 10*INT_TO_REAL(i_iPress)/iTermBits;
//Manual Page 29
IF (rV >= 2.5) THEN
rPRESS := LREAL_TO_REAL((0.000000001*EXP(1.8421*rV))/65);
ELSE
rPRESS := LREAL_TO_REAL((0.0000000008*EXP(1.9315*rV))/65);
END_IF
// Min. measured pressure
IF (rPRESS < 1E-11) AND NOT (rPRESS = 0) THEN
rPRESS := 1E-11;
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 := i_xError;// (stPump.eState = pumpFAULT);
stPump.xOverrideMode := i_xOverrideMode;
(*inputs*)
stPump.i_iPRESS:= i_iPRESS;
stPump.i_xHV_DI:= i_xIP_ON;
(*output*)
q_xHVEna_DO:= stPump.q_xHVEna_DO; //According to Manual pin should be grounded to activate function
This^.stPump.sPath:= sPath;
END_ACTION
METHOD M_AutoOn : BOOL
VAR_INPUT
END_VAR
this^.stPump.xAutoOn:= TRUE; // Pump is set to auto start when the pressure is low enough
END_METHOD
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.stPump.xHVEna_SW := run;
END_METHOD
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
METHOD M_AutoStart
VAR_INPUT
END_VAR
TEST('PIP_Auto_Restart');
This^.PG.rPRESS := 1E-6;
This^.PG.xPRESS_OK := TRUE;
This^.fb_PIP_GAMMA(i_stGauge:=PG, stPump=>, q_IG=>);
AssertTrue(This^.fb_PIP_GAMMA.stPump.q_xHVEna_DO, 'PIP HVEna_DO set to false');
TEST_FINISHED_NAMED('PIP_Auto_Restart');
END_METHOD
METHOD M_Check_State
VAR_INPUT
END_VAR
END_METHOD
METHOD M_INIT
VAR_INPUT
END_VAR
TEST('PIP_Startup');
This^.PG.rPRESS := 1E-1;
This^.PG.xPRESS_OK := TRUE;
This^.fb_PIP_GAMMA(i_stGauge:=PG, stPump=>, q_IG=>);
AssertFalse(This^.fb_PIP_GAMMA.stPump.xHVEna_SW, 'PIP xHVEna_SW set to True');
AssertFalse(This^.fb_PIP_GAMMA.stPump.q_xHVEna_DO, 'PIP HVEna_DO set to True');
TEST_FINISHED_NAMED('PIP_Startup');
END_METHOD
METHOD M_Interlock
VAR_INPUT
END_VAR
TEST('PIP_Interlock_Active');
This^.PG.rPRESS := 1E-1;
This^.PG.xPRESS_OK := True;
This^.fb_PIP_GAMMA.M_Run(TRUE);
This^.fb_PIP_GAMMA(i_stGauge:=PG, stPump=>, q_IG=>);
AssertFalse(This^.fb_PIP_GAMMA.stPump.xHVEna_SW, 'PIP xHVEna_SW set to True');
AssertFalse(This^.fb_PIP_GAMMA.stPump.q_xHVEna_DO, 'PIP HVEna_DO set to True');
TEST_FINISHED_NAMED('PIP_Interlock_Active');
TEST('PIP_Interlock_OK');
This^.PG.rPRESS := 1E-9;
This^.PG.xPRESS_OK := True;
This^.fb_PIP_GAMMA.M_Run(TRUE);
This^.fb_PIP_GAMMA(i_stGauge:=PG, stPump=>, q_IG=>);
AssertTrue(This^.fb_PIP_GAMMA.stPump.xHVEna_SW, 'PIP xHVEna_SW set to False');
AssertTrue(This^.fb_PIP_GAMMA.stPump.q_xHVEna_DO, 'PIP HVEna_DO set to False');
TEST_FINISHED_NAMED('PIP_Interlock_OK');
TEST('PIP_Interlock_Lost');
This^.PG.rPRESS := 1E-9;
This^.PG.xPRESS_OK := False;
This^.fb_PIP_GAMMA(i_stGauge:=PG, stPump=>, q_IG=>);
AssertFalse(This^.fb_PIP_GAMMA.stPump.xHVEna_SW, 'PIP xHVEna_SW set to True');
AssertFalse(This^.fb_PIP_GAMMA.stPump.q_xHVEna_DO, 'PIP HVEna_DO set to True');
TEST_FINISHED_NAMED('PIP_Interlock_Lost');
END_METHOD
- Related:
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
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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 ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
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
METHOD BackingPressureSetPoint : REAL
VAR_INPUT
i_SetPoint : REAL;
rMaxBackingPressure: REAL;
END_VAR
BackingPressureSetPoint := Min(i_SetPoint , rMaxBackingPressure);
END_METHOD
METHOD PUBLIC ErrorMessage : STRING
VAR_INPUT
nErrorCode : INT;
eState: E_PumpState;
END_VAR
IF (eState = pumpFAULT ) THEN
CASE nErrorCode OF
950:
ErrorMessage := 'Pump Starting Fault';
930:
ErrorMessage := 'Pump could not achieve running speed';
920:
ErrorMessage := 'Running at speed signal lost multiple times';
900:
ErrorMessage := 'Pump Controller Fault';
END_CASE
END_IF
END_METHOD
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
METHOD M_SetBits : BOOL
VAR_INPUT
TermBits : UINT; // The terminal's maximum value in bits
END_VAR
This^.iTermBits := TermBits;
END_METHOD
METHOD M_SetCurrentScale : BOOL
VAR_INPUT
Scale:REAL;//In Amps
END_VAR
This^.rCurrentScale := Scale;
END_METHOD
METHOD M_SetMaxSpeed : BOOL
VAR_INPUT
iMaxSpeed: INT; //963Hz for 3.1,300HT and 1010Hz for 401 and 1350 for 81
END_VAR
this^.iMaxSpeed := iMaxSpeed;
END_METHOD
METHOD M_SetPowerScale : BOOL
VAR_INPUT
Scale:REAL;//In Watt
END_VAR
This^.rPowerScale := Scale;
END_METHOD
METHOD PUBLIC M_TurboIsOn : BOOL
VAR_INPUT
END_VAR
M_TurboIsOn := (This^.iq_stPtm.eState = E_PumpState.pumpSTARTING ) OR (This^.iq_stPtm.eState = E_PumpState.pumpRUNNING );
END_METHOD
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC BackingPressureSetPoint : REAL
VAR_INPUT
i_SetPoint : REAL;
rMaxBackingPressure: REAL;
END_VAR
BackingPressureSetPoint := Min(i_SetPoint , rMaxBackingPressure);
END_METHOD
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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 *)
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 )
(*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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC BackingPressureSetPoint : REAL
VAR_INPUT
i_SetPoint : REAL;
rMaxBackingPressure: REAL;
END_VAR
BackingPressureSetPoint := Min(i_SetPoint , rMaxBackingPressure);
END_METHOD
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
- Related:
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_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;
q_PumpingStation := TRUE;
END_IF
IF (iq_stPTM.xResetSW) AND (iq_stPTM.i_xFault) THEN
q_PumpingStation := 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();
(*Load or save the persistent variables*)
ACT_Persistent();
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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
METHOD PUBLIC M_Serial_IO : BOOL
VAR_INPUT
st_Pfeiffer_RBK: ST_PfeifferStatus;
END_VAR
VAR_IN_OUT
st_Pfeiffer_CTRL: ST_PfeifferControl;
END_VAR
(* This function block is used to map the IO points from the pfeiffer structures
to the generic turbo structures *)
(*
if (bInit) Then
bInit := False;
st_Pfeiffer_CTRL.diSetRotationSpeed_309 := st_Pfeiffer_RBK.diActiveRotationSpeed_309;
END_IF*)
(* Controls *) (* Controls is via DIO*)
//st_Pfeiffer_CTRL.xPumpingStation_010 := iq_stPTM.q_RunDO;
//st_Pfeiffer_CTRL.xMotorPump_023 := 1; //Setting always true because parameter 10 alone can enable/disable the pump
//st_Pfeiffer_CTRL.uiPowerPct_708 := iq_stPTM.q_uiPowerPct;
//st_Pfeiffer_CTRL.xErrorAck_009 := iq_stPTM.xResetSW;
(* Readbacks *)
iq_stPTM.i_xAccel := st_Pfeiffer_RBK.xPumpAccelerating_307;
//iq_stPTM.i_xAtSpd := st_Pfeiffer_RBK.xRotationSpeedAttn_306; The Atspd is already linked via Digital I/Os
iq_stPTM.i_diCurSpd := st_Pfeiffer_RBK.diActiveRotationSpeed_309;
iq_stPTM.i_diPwr := st_Pfeiffer_RBK.diDrivePower_316;
iq_stPTM.i_diElecTemp := st_Pfeiffer_RBK.diTempElec_326;
iq_stPTM.i_diBtmTemp := st_Pfeiffer_RBK.diTempPmpBot_330;
iq_stPTM.i_diBrngTemp := st_Pfeiffer_RBK.diTempBearng_342;
iq_stPTM.i_diMtrTemp := st_Pfeiffer_RBK.diTempMotor_346;
iq_stPTM.i_uiPowerPctRbk := st_Pfeiffer_RBK.uiPowerPct_708;
iq_stPTM.i_rCurrentMon := st_Pfeiffer_RBK.lrDriveCurrent_310;
iq_stPTM.xCommTimeout := st_Pfeiffer_RBK.xTimeout;
iq_stPTM.i_iErrorCode := st_Pfeiffer_RBK.iErrorCode_303;
iq_stPTM.i_iWarningCode := st_Pfeiffer_RBK.iWarningCode_303;
(* Check that error acknowledgment is processed *)
IF st_Pfeiffer_RBK.xErrorAcknowledged THEN
st_Pfeiffer_RBK.xErrorAcknowledged := FALSE;
st_Pfeiffer_CTRL.xErrorAck_009 := FALSE;
iq_stPTM.xResetSW := FALSE;
END_IF
IF st_Pfeiffer_RBK.sErrorCode_303 = '' THEN
;
ELSIF st_Pfeiffer_RBK.sErrorCode_303 <> '000000' THEN
iq_stPTM.i_xFault := TRUE;
ELSE
iq_stPTM.i_xFault := FALSE;
END_IF
st_Pfeiffer_CTRL.xSetSpdMode_026:= iq_stPTM.q_bSetSpd;
iq_stPTM.i_dSetSpd := LIMIT (0, iq_stPTM.i_dSetSpd, 1500); // 1500 nominal frequency
IF ( iq_stPTM.i_dSetSpd <>0) THEN
st_Pfeiffer_CTRL.iSetRotationSpeed_707 := DINT_TO_INT(iq_stPTM.i_dSetSpd * 100/1500);
st_Pfeiffer_CTRL.iSetRotationSpeed_707 := LIMIT(20, st_Pfeiffer_CTRL.iSetRotationSpeed_707 ,100); // Manual Page 18
END_IF
END_METHOD
- Related:
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
METHOD M_INIT : BOOL
VAR_INPUT
END_VAR
TEST('PTM_Twistorr_Startup');
fb_TwisTorr(i_xExtILKOk:= TRUE , iq_stPtm=> );
IF( cycle = 3) THEN
AssertFalse(fb_TwisTorr.iq_stPtm.xRunSW ,'PTM Twistorr xRunSW is set to true');
AssertFalse(fb_TwisTorr.iq_stPtm.q_RunDO ,'PTM Twistorr q_RunDO is set to true');
TEST_FINISHED_NAMED('PTM_Twistorr_Startup');
END_IF
TEST('PTM_Ebara011M_Startup');
fb_PTM_Ebara_010M(i_xExtILKOk:= TRUE , iq_stPtm=> );
IF( cycle = 3) THEN
AssertFalse(fb_PTM_Ebara_010M.iq_stPtm.xRunSW ,'PTM Ebara011M xRunSW is set to true');
AssertFalse(fb_PTM_Ebara_010M.iq_stPtm.q_RunDO ,'PTM Ebara011M q_RunDO is set to true');
AssertFalse(fb_PTM_Ebara_010M.iq_stPtm.q_xProtection ,'PTM Ebara011M q_xProtection is set to True');
TEST_FINISHED_NAMED('PTM_Ebara011M_Startup');
END_IF
END_METHOD
METHOD M_Interlock
VAR_INPUT
END_VAR
TEST('PTM_Twistorr_Interlock_Active');
fb_TwisTorr.M_Run(TRUE);
fb_TwisTorr(i_xExtILKOk:= FALSE , iq_stPtm=> );
IF( cycle = 3) THEN
AssertFalse(fb_TwisTorr.iq_stPtm.xRunSW ,'PTM Twistorr xRunSW is set to true');
AssertFalse(fb_TwisTorr.iq_stPtm.q_RunDO ,'PTM Twistorr q_RunDO is set to true');
TEST_FINISHED_NAMED('PTM_Twistorr_Interlock_Active');
END_IF
TEST('PTM_Twistorr_Interlock_OK');
fb_TwisTorr.M_Run(TRUE);
fb_TwisTorr(i_xExtILKOk:= TRUE , iq_stPtm=> );
IF( cycle = 3) THEN
AssertTrue(fb_TwisTorr.iq_stPtm.xRunSW ,'PTM Twistorr xRunSW is set to False');
AssertTrue(fb_TwisTorr.iq_stPtm.q_RunDO ,'PTM Twistorr q_RunDO is set to False');
TEST_FINISHED_NAMED('PTM_Twistorr_Interlock_OK');
END_IF
TEST('PTM_Twistorr_Interlock_Lost');
fb_TwisTorr(i_xExtILKOk:= FALSE , iq_stPtm=> );
IF( cycle = 3) THEN
AssertFalse(fb_TwisTorr.iq_stPtm.xRunSW ,'PTM Twistorr xRunSW is set to true');
AssertFalse(fb_TwisTorr.iq_stPtm.q_RunDO ,'PTM Twistorr q_RunDO is set to true');
TEST_FINISHED_NAMED('PTM_Twistorr_Interlock_Lost');
END_IF
END_METHOD
METHOD M_PTM_EBARA
VAR
Expected:REAL;
Actual:REAL;
diExpected:DINT;
diActual: DINT;
END_VAR
TEST('PTM_EBARA_AI');
This^.i_iRawSpeed := v_to_int(5);
This^.i_iTempMon := v_to_int(2.5);
THIS^.i_iCurrentMon := v_to_int(2.5);
//THIS^.q_iSpeedSet := 560;
fb_PTM_Ebara_010M(
i_xExtILKOk:= TRUE ,
i_rMaxBackingPressure:= 1,
iq_stPTM=> );
IF( cycle = 10) THEN
Expected := 5;
Actual := (fb_PTM_Ebara_010M.iq_stPTM.i_rCurrentMon);
AssertEquals_REAL(Expected, Actual,0.1, 'PTM EBARA010 i_iCurrentMon Fail');
Expected := 50;
Actual := (fb_PTM_Ebara_010M.iq_stPTM.i_rTempMon);
AssertEquals_REAL(Expected, Actual,0.1, 'PTM EBARA010 i_iTempMon Fail');
diExpected := 0;
diActual := fb_PTM_Ebara_010M.iq_stPTM.q_iSpeedSet;
AssertEquals(diExpected, diActual, 'PTM EBARA010 q_iSpeedSet Fail');
diExpected := 560;
diActual := fb_PTM_Ebara_010M.iq_stPTM.i_diCurSpd;
AssertEquals(diExpected, diActual, 'PTM EBARA010 i_diCurSpd Fail');
TEST_FINISHED_NAMED('PTM_EBARA_AI');
End_IF;
END_METHOD
- Related:
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_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
q_RunDO := iq_stPTM.xRunSW;
ELSE
iq_stPtm.xRunSW:=FALSE;
q_RunDO:=FALSE;
END_IF
(*Pump States*)
IF (iq_stPtm.i_xFault) THEN
iq_stPtm.eState := pumpFAULT;
iq_stPtm.xRunSW:=FALSE;
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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*)
iq_stPtm.q_RunDO := 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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
METHOD PUBLIC M_Serial_IO : BOOL
VAR_INPUT
i_st_TD20_RBK : ST_TD20Status;
END_VAR
(* This function block is used to map the IO points from the Oerlikon structures
to the generic turbo structures *)
(* Readbacks *)
iq_stPTM.i_xAccel := i_st_TD20_RBK.wStatusWord.4;
iq_stPTM.i_xDecel := i_st_TD20_RBK.wStatusWord.5;
//iq_stPTM.i_xAtSpd := i_st_TD20_RBK.wStatusWord.10;
iq_stPTM.i_xRemote := i_st_TD20_RBK.wStatusWord.15; (* Remote control enabled *)
iq_stPTM.i_diCurSpd := WORD_TO_DINT(i_st_TD20_RBK.wFrequency_P3);
iq_stPTM.i_rCurrentMon := WORD_TO_INT(i_st_TD20_RBK.wMotorCurrent_P5);
iq_stPTM.i_diPwr := WORD_TO_DINT(i_st_TD20_RBK.wMotorCurrent_P5 * i_st_TD20_RBK.wVoltage_P4);
iq_stPTM.i_diElecTemp := WORD_TO_DINT(i_st_TD20_RBK.wConverterTemp_P11);
iq_stPTM.i_rTempMon := WORD_TO_INT(i_st_TD20_RBK.wPumpTemp_P127);
(* Faults and warnings *)
//iq_stPTM.i_xFault := i_st_TD20_RBK.wStatusWord.3;
iq_stPTM.i_xTempFault := i_st_TD20_RBK.wStatusWord.7; (* specified as a temperature warning but it could be a fault *)
iq_stPTM.i_xWarn := i_st_TD20_RBK.wStatusWord.14; (* warning summary bit *)
END_METHOD
- Related:
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();
(*Load or save the persistent variables*)
ACT_Persistent();
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
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rBackingPressureSP <> 0) THEN
iq_stPTM.rBackingPressureSP := rBackingPressureSP;
END_IF;
IF (rInletPressureSP <> 0) THEN
iq_stPTM.rInletPressureSP := rInletPressureSP;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stPTM.rBackingPressureSP = rBackingPressureSP) THEN
rBackingPressureSP:= iq_stPTM.rBackingPressureSP;
END_IF;
IF NOT (iq_stPTM.rInletPressureSP = rInletPressureSP) THEN
rInletPressureSP := iq_stPTM.rInletPressureSP;
END_IF;
END_ACTION
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPtm.xRunSW := run;
END_METHOD
FB_Pump
FUNCTION_BLOCK FB_Pump
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;
// For Persistent Data
bRestorePersistentData : BOOL:=TRUE;
END_VAR
VAR PERSISTENT
rBackingPressureSP : REAL;
rInletPressureSP : REAL;
END_VAR
END_FUNCTION_BLOCK
ACTION ACT_Logger:
END_ACTION
- Related:
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
METHOD PUBLIC M_Run : BOOL
VAR_INPUT
run:bool; // set to true to run, false to stop;
END_VAR
this^.iq_stPFO.pv_xRunSW := run;
END_METHOD
- Related:
FB_TBOOL_ADS
(* This function block is created for interface a boolean singal between different PLCs*)
(* The variable values are read via ADS using the symbol name*)
FUNCTION_BLOCK FB_TBOOL_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: value'}
bBool : BOOL;
{attribute 'pytmc' := 'pv: error'}
bError: BOOL;
END_VAR
VAR
fb_CheckWatchdog: FB_CheckWatchdog;
fb_Read: FB_ReadAdsSymByName;
ftReset: F_TRIG;
xFirstPass: BOOL:= true;
END_VAR
ftReset(CLK:= fb_Read.bBusy OR xFirstPass);
xFirstPass := false;
(*calling ADS read function*)
fb_Read(
bRead:=ftReset.Q ,
sNetId:= sNetId,
nPort:= nPort,
sVarName:= sVarName,
nDestAddr:= ADR(bBool),
nLen:= SIZEOF(bBool),
tTimeout:= ,
eComMode:=eAdsComModeFastCom ,
bBusy=> ,
bError=> ,
nErrorId=> );
(*Error*)
fb_CheckWatchdog(
bEnable:= TRUE,
tWatchdogTime:= T#900ms,
nCnt:= iWatchdog ,
bWatchdog=> ,
nLastCnt=> );
bError:= fb_Read.bError OR fb_CheckWatchdog.bWatchdog;
tErrorPresent(CLK:=bError);
IF (fb_Read.bError OR fb_CheckWatchdog.bWatchdog) THEN
bBool := FALSE; // If there is an error in connection the signal turns to FALSE
END_IF;
(*Logger*)
ACT_Logger();
END_FUNCTION_BLOCK
ACTION ACT_Logger:
IF tErrorPresent.Q THEN
IF(fb_Read.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
- Related:
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_TGPI_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_TGPI_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,'.PG'),
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
- Related:
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
METHOD M_Close : BOOL
VAR_INPUT
close:BOOL; //True to CLose, False to OPEN
END_VAR
This^.q_stValve.pv_xCLS_SW :=Close;
END_METHOD
- Related:
FB_TVGC_2S_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_2S_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_2S;
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
- Related:
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
- Related:
FB_Valve
FUNCTION_BLOCK FB_Valve
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
// For logging
fbLogger : FB_LogMessage := (eSubsystem:=E_SubSystem.VACUUM, nMinTimeViolationAcceptable:=10);
ePrevState : E_ValvePositionState;
tErrorPresent : R_TRIG;
tAction : R_TRIG; // Primary action of this device (OPN_DO, etc.)
tOverrideActivated : R_TRIG;
// For Persistent Data
bRestorePersistentData : BOOL:=TRUE;
//fbWritePersistentData : WritePersistentData;
END_VAR
END_FUNCTION_BLOCK
ACTION ACT_Logger:
END_ACTION
- Related:
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
- Related:
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := NOT This^.q_xOPN_DO;
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := This^.q_xOPN_DO;
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL; //True to SET, False to RESET
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
FB_VCC_NO
(*Deprecated*)
(* This Function Block Implements Basic Functionality for NO Vent Valves VVC *)
(* Note Interlock Logic is External *)
{attribute 'obsolete' := 'FB_VCC_NO has been deprecated and is not valid!'}
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
METHOD M_Close : BOOL
VAR_INPUT
close:BOOL; //True to CLose, False to OPEN
END_VAR
This^.iq_stValve.pv_xCLS_SW :=Close;
END_METHOD
- Related:
FB_VCN
(* This function implements the Basic functions for the Pfeiffer EVR 116 needle valve*)
FUNCTION_BLOCK FB_VCN EXTENDS FB_Valve
VAR_INPUT
i_xExtIlkOK : BOOL; //External Interlock, SET to TRUE if not used
i_ReqPos : REAL; //Requested position
i_xPurge : BOOL:= FALSE;// When purge is set the maximum threshold is ignored, has to be set evey cycle.
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
iq_stVCN : ST_VCN; //Needle valve structure
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
VAR PERSISTENT
rUpperLimit : REAL;
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
*)
iq_stVCN.rUpperLimit := LIMIT(0, iq_stVCN.rUpperLimit, 100);
// 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*)
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
IF (i_xPurge) THEN
iq_stVCN.rReqPosition := LIMIT(0, i_ReqPos, 100);
ELSE iq_stVCN.rReqPosition := LIMIT(0, i_ReqPos, iq_stVCN.rUpperLimit);
END_IF
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();
(*Load or save the persistent variables*)
ACT_Persistent();
END_FUNCTION_BLOCK
ACTION ACT_IO:
(*outputs*)
q_iRawPosition := iq_stVCN.q_iRawPosition;
(*inputs*)
iq_stVCN.i_iPosition := iq_stvcn.q_iRawPosition;
END_ACTION
ACTION ACT_Logger:
// ILK logger
IF NOT (iq_stVCN.xIlkOK ) AND NOT(ePrevState = CloseValve) THEN
fbLogger(sMsg:='Lost interlock ok bit while valve was open.', eSevr:=TcEventSeverity.Critical);
END_IF
//Control STATE Logger
IF ePrevState <> iq_stVCN.eValveControl THEN
CASE iq_stVCN.eValveControl OF
ManualControl:
fbLogger(sMsg:='Valve in manual control mode', eSevr:=TcEventSeverity.Warning);
OpenValve:
fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info);
PressureControl:
fbLogger(sMsg:='Valve in pressure control mode.', eSevr:=TcEventSeverity.Info);
CloseValve:
fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info);
END_CASE
ePrevState := iq_stVCN.eValveControl;
END_IF
// Log valve open
tAction(CLK:= (iq_stVCN.eValveControl = OpenValve) );
IF tAction.Q THEN fbLogger(sMsg:='Valve commanded to fully open', eSevr:=TcEventSeverity.Info); END_IF
END_ACTION
ACTION ACT_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (bRestorePersistentData) THEN
bRestorePersistentData := FALSE;
IF (rUpperLimit <> 0) THEN
iq_stVCN.rUpperLimit := rUpperLimit;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stVCN.rUpperLimit = rUpperLimit) THEN
rUpperLimit := iq_stVCN.rUpperLimit;
//fbWritePersistentData(NETID:='', PORT:=851, START:=TRUE, TMOUT:=T#1s );
ELSE
// fbWritePersistentData( START:=FALSE);
END_IF;
END_ACTION
METHOD M_SetThrottle : BOOL
VAR_INPUT
rUpperLimit : REAL;
END_VAR
iq_stVCN.rUpperLimit := LIMIT(0, rUpperLimit, 100);
END_METHOD
METHOD M_ValveControl : BOOL
VAR_INPUT
state:E_VCN; // Close, Open, Pressure, Manual
END_VAR
iq_stVCN.eValveControl := state;
END_METHOD
FB_VCN_VAT590
(* This function implements the Basic functions for the VAT590 needle valve*)
FUNCTION_BLOCK FB_VCN_VAT590 EXTENDS FB_Valve
VAR_INPUT
bExtIlkOK : BOOL; // External Interlock, SET to TRUE if not used
IG : ST_VG; // Input from a cold cathode gauge used in control loop
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv:
'}
stVcnVat590 : ST_VCN_VAT590; // Needle valve VAT590 structure
END_VAR
VAR CONSTANT
cOpenPosition : REAL := 1E+5; // Default position setpoint upper scale value
cClosePosition : REAL := 0.0; // Default position setpoint lower scale value
cPresSPUpLim : REAL := 1E+6; // Default pressure setpoint upper scale value
cPresSPLowLim : REAL := 0; // Default pressure setpoint lower scale value
cMinPress : REAL := 1e-11; // Default Min. pressure in Torr that can be used and requested
cMaxPress : REAL := 1e-2; // Default Max. pressure in Torr that can be used and requested
cBasePress : REAL := 1E-11; // Pressure sensor base value
cPressRawSlope : REAL := 1E+5; // Slope (in Units per decade) used for conversion of pressure in Torr to valve External Digital pressure sensor 1 input raw value (0-1000000)
cPressRawBase : REAL := 1E+5; // Valve analog output base(in Units per decade) defined for how much the curve is shifted
cPressCtrl : ST_CoEIndSub := (nIndex := 16#2199, nSubIndex := 0); // Selected pressure controller CoE register
cSensorDelay_AD : ST_CoEIndSub := (nIndex := 16#219A, nSubIndex := 0); // Adaptive DS controller Sensor Delay CoE register
cRampTime_AD : ST_CoEIndSub := (nIndex := 16#219B, nSubIndex := 0); // Adaptive DS controller Ramp Time CoE register
cRampMode_AD : ST_CoEIndSub := (nIndex := 16#219C, nSubIndex := 0); // Adaptive DS controller Ramp Mode CoE register
cGainFactor_AD : ST_CoEIndSub := (nIndex := 16#219E, nSubIndex := 0); // Adaptive DS controller Gain factor CoE register
cRampTime_FIX1 : ST_CoEIndSub := (nIndex := 16#21A5, nSubIndex := 0); // Fixed 1 controller Ramp Time CoE register
cRampMode_FIX1 : ST_CoEIndSub := (nIndex := 16#21A6, nSubIndex := 0); // Fixed 1 controller Ramp Mode CoE register
cCtrlDir_FIX1 : ST_CoEIndSub := (nIndex := 16#21A7, nSubIndex := 0); // Fixed 1 controller Control Direction CoE register
cPGain_FIX1 : ST_CoEIndSub := (nIndex := 16#21A8, nSubIndex := 0); // Fixed 1 controller P Gain CoE register
cIGain_FIX1 : ST_CoEIndSub := (nIndex := 16#21A9, nSubIndex := 0); // Fixed 1 controller I Gain CoE register
cRampTime_FIX2 : ST_CoEIndSub := (nIndex := 16#21AF, nSubIndex := 0); // Fixed 2 controller Ramp Time CoE register
cRampMode_FIX2 : ST_CoEIndSub := (nIndex := 16#21B0, nSubIndex := 0); // Fixed 2 controller Ramp Mode CoE register
cCtrlDir_FIX2 : ST_CoEIndSub := (nIndex := 16#21B1, nSubIndex := 0); // Fixed 2 controller Control Direction CoE register
cPGain_FIX2 : ST_CoEIndSub := (nIndex := 16#21B2, nSubIndex := 0); // Fixed 2 controller P Gain CoE register
cIGain_FIX2 : ST_CoEIndSub := (nIndex := 16#21B3, nSubIndex := 0); // Fixed 2 controller I Gain CoE register
cRampTime_SP : ST_CoEIndSub := (nIndex := 16#21B9, nSubIndex := 0); // Soft Pump controller Ramp Time CoE register
cRampMode_SP : ST_CoEIndSub := (nIndex := 16#21BA, nSubIndex := 0); // Soft Pump controller Ramp Time CoE register
cPGain_SP : ST_CoEIndSub := (nIndex := 16#21BC, nSubIndex := 0); // Soft Pump controller Ramp Time CoE register
cPosLimitOpen : ST_CoEIndSub := (nIndex := 16#56B3, nSubIndex := 0); // General open limit. It overides the PosLimitOpenCtrl if SET lower
cPosLimitOpenCtrl : ST_CoEIndSub := (nIndex := 16#56B4, nSubIndex := 0); // Open limit active during pressure control only
END_VAR
VAR
fCalcPosSP : REAL := 0;
fCalcPresSP : REAL := 0;
fPressRaw : REAL := 0; // Ranges 0 to 1000000 (or 0.0 to 10V) (1E-12 to 1E-2)
eValveModeSP : E_ControlModeSP; // Control Mode setpoint internal variable
fReqPos : REAL; // Requested position internal variable
fReqPres : REAL; // Requested pressure internal variable
bRemAcsToggle : BOOL; // Remote access control bit
//timer for zero function
tZeroTogg : TON := (PT := T#1S);
//timer for remote function
tRemAcsTogg : TON := (PT := T#500MS);
//used for logging
ePreviousState : E_VCN_VAT590;
(*CoE Interface*)
(*Link this to your VAT590 reference variables under InfoData.*)
//Connect i_stVat590PlcDriveRef.aNetId to the VAT590 Valve InfoData^AdsAddr^netId
//Connect i_stVat590PlcDriveRef.nSlaveAddr to the VAT590 Valve InfoData^AdsAddr^port
i_stVat590PlcDriveRef AT %I*: ST_PlcDriveRef;
stVat590DriveRef : ST_DriveRef;
fbCoeRead : FB_CoERead_ByDriveRef; // CoE Read function block
fbCoeWrite : FB_CoEWrite_ByDriveRef; // CoE Write function block
stCoeParamsInternal : ST_VAT590_COE; // Internal storage of CoE parameters
nCaseReadCoe : UINT := 1; // Iterator for going through all CoE parameters
rtCoeReset : R_TRIG; // Rising edge trigger for reset CoE Interface error
ftCoeReadBusy : F_TRIG; // Falling edge trigger for CoE read new data
ftCoeWriteBusy : F_TRIG; // Falling edge trigger for CoE write data
bFirstRead : BOOL := TRUE; // FALSE after succesfull first readout of all CoE parameters
(*VAT590 Ethercat IO Interface*)
// Ethercat Interface Inputs
i_nRawPosition AT %I* : DINT; // Position readback from the valve
i_nRawPressure AT %I* : DINT; // Pressure readback from the valve
i_eControlMode AT %I* : E_ControlMode; // Valve control mode readback
i_eFatalError AT %I* : E_FatalErrors; // Valve Fatal Errors readback
i_stGenStatus AT %I* : ST_GeneralStatus; // Valve General Status readback
i_stGenWarnings AT %I* : ST_GeneralWarnings; // Valve General Warnings readback
i_stExtnWarnings AT %I* : ST_ExtendedWarnings;// Valve Extended Warnings readback
// Ethercat Interface Outputs
o_nRawPositionSP AT %Q* : DINT; // Position setpoint
o_nRawPressureSP AT %Q* : DINT; // Pressure setpoint
o_nRawPressure AT %Q* : DINT; // Raw pressure output, eliminate need for external connection of AI, ranges 0 to 1000000 (or 0.0 to 10V) (1E-12 Torr to 1E-2 Torr)
o_eCtrlModeSP AT %Q* : E_ControlModeSP; // Valve control mode setpoint
o_stGenCtrlSP AT %Q* : ST_GeneralControlSP;// General Control setpoint
(*Ethercat Interface Status Check*)
(*Link this to your VAT590 WcState variable under WcState*)
i_bValveEthCatStatus AT %I* : BOOL := TRUE;
END_VAR
VAR PERSISTENT
fUpperLimit : REAL; // Max position in % that can be requested
fPresMaxLim : REAL; // Max pressure in Torr that can be requested
fPresRbMaxLim : REAL; // Max pressure in Torr read by chamber gauge, valve will close above the pressure.
END_VAR
(* Needle valve control FB
J. Govednik 2021-11-22
This FB should be used as a low level control block.
It provides:
Valve position ceiling
Interlock
CoE Interface R/W
It could be used for:
Valve position/flow linearization
*)
(*Set Coe Interface, AmsNetID, Slave address etc*)
SetCoeInterface();
(*Read from EPICS and Valve Ethercat interface*)
ReadFromInput();
(*Checking which Control mode is selected and if valve's communication is OK*)
IF stVcnVat590.bValveEthCatStatus THEN //If valve is not connected or something is wrong with EtherCAT communication PLC will set this bit to 1
fReqPos := 0; //set req. position to 0
eValveModeSP := E_ControlModeSP.CloseValve; //set control mode of the valve to CLOSE VALVE
stVcnVat590.eValveControl := E_VCN_VAT590.CloseValve; //EPICS command hold in CloseValve state
stVcnVat590.stValveStatus.eControlMode := E_ControlMode.FatalError; //override the readback from the valve because when valve is disconnected the value remains
stVcnVat590.nRawPosition := 0; //override the readback from the valve because when valve is disconnected the value remains
stVcnVat590.nRawPressure := 0; //override the readback from the valve because when valve is disconnected the value remains
ELSIF bExtIlkOK THEN
CASE stVcnVat590.eValveControl OF
E_VCN_VAT590.OpenValve:
eValveModeSP := E_ControlModeSP.OpenValve;
E_VCN_VAT590.CloseValve:
eValveModeSP := E_ControlModeSP.CloseValve;
E_VCN_VAT590.ManualControl:
IF IG.xPRESS_OK AND IG.rPRESS <= fPresRbMaxLim THEN
eValveModeSP := E_ControlModeSP.ManualControl;
ELSE
eValveModeSP := E_ControlModeSP.CloseValve; // Close valve if something is wrong with the gauge
stVcnVat590.eValveControl := E_VCN_VAT590.CloseValve;
END_IF
E_VCN_VAT590.PressureControl:
IF IG.xPRESS_OK AND IG.rPRESS <= fPresRbMaxLim THEN
eValveModeSP := E_ControlModeSP.PressureControl;
ELSE
eValveModeSP := E_ControlModeSP.CloseValve; // Close valve if something is wrong with the gauge
stVcnVat590.eValveControl := E_VCN_VAT590.CloseValve;
END_IF
E_VCN_VAT590.HoldValve:
IF stVcnVat590.stValveStatus.eControlMode = E_ControlMode.ManualControl OR
stVcnVat590.stValveStatus.eControlMode = E_ControlMode.PressureControl THEN
eValveModeSP := E_ControlModeSP.HoldValve;
END_IF
END_CASE
ELSE
fReqPos := 0;
eValveModeSP := E_ControlModeSP.CloseValve;
stVcnVat590.eValveControl := E_VCN_VAT590.CloseValve;
END_IF
(*Position SP calculation*)
fReqPos := LIMIT(0, fReqPos, fUpperLimit);
fCalcPosSP := fReqPos * (cOpenPosition-cClosePosition)/100 + cClosePosition;
fCalcPosSP := LIMIT(cClosePosition, fCalcPosSP, cOpenPosition); //The requested position SP should remain within this range
(*Pressure SP calculation*)
//Limit the input from EPICS between upper and lower limit defined for seleceted pressure sensor
fReqPres := LIMIT(cMinPress, fReqPres, fPresMaxLim);
//Calcuate pressure setpoint
fCalcPresSP := cPressRawSlope * LOG(fReqPres/cBasePress) + cPressRawBase;
fCalcPresSP := LIMIT(cPresSPLowLim, fCalcPresSP, cPresSPUpLim); //The requested pressure SP should remain within this range
(*Calculated Pressure SP, Pressure readback and PressureRaw output value are 0 if Pressure sensor is not working*)
//Calculate PressureRaw value linked to the Valve's External Digital pressure sensor 1 input
//Pressure information from IG input is linked to stVcnVat590.fPressure
IF IG.xPRESS_OK THEN
stVcnVat590.fPressure := IG.rPRESS;
fPressRaw := cPressRawSlope * LOG(IG.rPRESS/cBasePress) + cPressRawBase;
ELSE
fCalcPresSP := 0;
stVcnVat590.fPressure := 0;
fPressRaw := 0;
END_IF
(*Zero function*)
IF stVcnVat590.stValveStatus.bZeroDisabled OR tZeroTogg.Q THEN
stVcnVat590.bZero := FALSE;
END_IF
tZeroTogg.IN := stVcnVat590.bZero;
tZeroTogg();
(*Set Remote Access*)
IF stVcnVat590.stValveStatus.eRemoteStatus <> stVcnVat590.eRemoteControl THEN
tRemAcsTogg.IN := TRUE;
IF tRemAcsTogg.Q THEN
bRemAcsToggle := NOT bRemAcsToggle;
tRemAcsTogg.IN := FALSE;
END_IF
END_IF
tRemAcsTogg();
(*Reset Coe Interface error*)
rtCoeReset(CLK := stVcnVat590.bResetCoeError);
IF rtCoeReset.Q THEN
stVcnVat590.bResetCoeError := FALSE;
END_IF
(*Write CoE Parameters*)
WriteCoeParameters();
(*Read CoE Parameters*)
ReadCoeParameters();
(*Write to EPICS and Valve Ethercat interface*)
WriteToOutput();
(*Load or save the persistent variables*)
LoadPersistentData();
(*Logging*)
Logger();
END_FUNCTION_BLOCK
ACTION LoadPersistentData:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (bRestorePersistentData) THEN
bRestorePersistentData := FALSE;
IF (fUpperLimit <> 0) THEN
stVcnVat590.fPosSetLimit := fUpperLimit;
END_IF;
IF (fPresMaxLim <> 0) THEN
stVcnVat590.fPresSetLimit := fPresMaxLim;
END_IF;
IF (fPresRbMaxLim <> 0) THEN
stVcnVat590.fPresRdbkLimit := fPresRbMaxLim;
END_IF;
END_IF
END_ACTION
ACTION Logger:
// ILK logger
IF NOT (stVcnVat590.bIlkOK ) AND NOT(ePreviousState = E_VCN_VAT590.CloseValve) THEN
fbLogger(sMsg:='Lost interlock ok bit while valve was open.', eSevr:=TcEventSeverity.Critical);
END_IF
//Control STATE Logger
IF ePreviousState <> stVcnVat590.eValveControl THEN
CASE stVcnVat590.eValveControl OF
E_VCN_VAT590.ManualControl:
fbLogger(sMsg:='Valve in manual control mode.', eSevr:=TcEventSeverity.Warning);
E_VCN_VAT590.HoldValve:
fbLogger(sMsg:='Valve in hold control mode.', eSevr:=TcEventSeverity.Warning);
E_VCN_VAT590.OpenValve:
fbLogger(sMsg:='Valve Open.', eSevr:=TcEventSeverity.Info);
E_VCN_VAT590.PressureControl:
fbLogger(sMsg:='Valve in pressure control mode.', eSevr:=TcEventSeverity.Info);
E_VCN_VAT590.CloseValve:
fbLogger(sMsg:='Valve closed.', eSevr:=TcEventSeverity.Info);
END_CASE
ePreviousState := stVcnVat590.eValveControl;
END_IF
// Log valve open
tAction(CLK:= (stVcnVat590.eValveControl = E_VCN_VAT590.OpenValve) );
IF tAction.Q THEN fbLogger(sMsg:='Valve commanded to fully open', eSevr:=TcEventSeverity.Info); END_IF
END_ACTION
ACTION ReadCoeParameters:
CASE nCaseReadCoe OF
0:
stVcnVat590.bCoeError := TRUE;
IF rtCoeReset.Q THEN
nCaseReadCoe := 1;
stVcnVat590.bCoeError := FALSE;
bFirstRead := TRUE; // Set bFirstRead after Coe interface reset
END_IF
1:
fbCoeRead.nIndex := cPressCtrl.nIndex;
fbCoeRead.nSubIndex := cPressCtrl.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.ePIDCtrl);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.ePIDCtrl);
fbCoeRead.bExecute := TRUE;
2:
fbCoeRead.nIndex := cSensorDelay_AD.nIndex;
fbCoeRead.nSubIndex := cSensorDelay_AD.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fSensorDelay_AD);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fSensorDelay_AD);
fbCoeRead.bExecute := TRUE;
3:
fbCoeRead.nIndex := cRampTime_AD.nIndex;
fbCoeRead.nSubIndex := cRampTime_AD.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_AD);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_AD);
fbCoeRead.bExecute := TRUE;
4:
fbCoeRead.nIndex := cRampMode_AD.nIndex;
fbCoeRead.nSubIndex := cRampMode_AD.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_AD);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_AD);
fbCoeRead.bExecute := TRUE;
5:
fbCoeRead.nIndex := cGainFactor_AD.nIndex;
fbCoeRead.nSubIndex := cGainFactor_AD.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fGainFactor_AD);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fGainFactor_AD);
fbCoeRead.bExecute := TRUE;
6:
fbCoeRead.nIndex := cRampTime_FIX1.nIndex;
fbCoeRead.nSubIndex := cRampTime_FIX1.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_FIX1);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_FIX1);
fbCoeRead.bExecute := TRUE;
7:
fbCoeRead.nIndex := cRampMode_FIX1.nIndex;
fbCoeRead.nSubIndex := cRampMode_FIX1.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_FIX1);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_FIX1);
fbCoeRead.bExecute := TRUE;
8:
fbCoeRead.nIndex := cCtrlDir_FIX1.nIndex;
fbCoeRead.nSubIndex := cCtrlDir_FIX1.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bCtrlDir_FIX1);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bCtrlDir_FIX1);
fbCoeRead.bExecute := TRUE;
9:
fbCoeRead.nIndex := cPGain_FIX1.nIndex;
fbCoeRead.nSubIndex := cPGain_FIX1.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fPGain_FIX1);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_FIX1);
fbCoeRead.bExecute := TRUE;
10:
fbCoeRead.nIndex := cIGain_FIX1.nIndex;
fbCoeRead.nSubIndex := cIGain_FIX1.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fIGain_FIX1);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fIGain_FIX1);
fbCoeRead.bExecute := TRUE;
11:
fbCoeRead.nIndex := cRampTime_FIX2.nIndex;
fbCoeRead.nSubIndex := cRampTime_FIX2.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_FIX2);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_FIX2);
fbCoeRead.bExecute := TRUE;
12:
fbCoeRead.nIndex := cRampMode_FIX2.nIndex;
fbCoeRead.nSubIndex := cRampMode_FIX2.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_FIX2);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_FIX2);
fbCoeRead.bExecute := TRUE;
13:
fbCoeRead.nIndex := cCtrlDir_FIX2.nIndex;
fbCoeRead.nSubIndex := cCtrlDir_FIX2.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bCtrlDir_FIX2);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bCtrlDir_FIX2);
fbCoeRead.bExecute := TRUE;
14:
fbCoeRead.nIndex := cPGain_FIX2.nIndex;
fbCoeRead.nSubIndex := cPGain_FIX2.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fPGain_FIX2);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_FIX2);
fbCoeRead.bExecute := TRUE;
15:
fbCoeRead.nIndex := cIGain_FIX2.nIndex;
fbCoeRead.nSubIndex := cIGain_FIX2.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fIGain_FIX2);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fIGain_FIX2);
fbCoeRead.bExecute := TRUE;
16:
fbCoeRead.nIndex := cRampTime_SP.nIndex;
fbCoeRead.nSubIndex := cRampTime_SP.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_SP);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_SP);
fbCoeRead.bExecute := TRUE;
17:
fbCoeRead.nIndex := cRampMode_SP.nIndex;
fbCoeRead.nSubIndex := cRampMode_SP.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_SP);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_SP);
fbCoeRead.bExecute := TRUE;
18:
fbCoeRead.nIndex := cPGain_SP.nIndex;
fbCoeRead.nSubIndex := cPGain_SP.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fPGain_SP);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_SP);
fbCoeRead.bExecute := TRUE;
19:
fbCoeRead.nIndex := cPosLimitOpen.nIndex;
fbCoeRead.nSubIndex := cPosLimitOpen.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fPositionLimitOpen);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPositionLimitOpen);
fbCoeRead.bExecute := TRUE;
20:
fbCoeRead.nIndex := cPosLimitOpenCtrl.nIndex;
fbCoeRead.nSubIndex := cPosLimitOpenCtrl.nSubIndex;
fbCoeRead.pDstBuf := ADR(stVcnVat590.stCoeParameters.fPositionLimitOpenControl);
fbCoeRead.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPositionLimitOpenControl);
fbCoeRead.bExecute := TRUE;
END_CASE
ftCoeReadBusy(CLK:=fbCoeRead.bBusy);
IF ftCoeReadBusy.Q THEN
fbCoeRead.bExecute := FALSE;
IF NOT fbCoeRead.bError THEN
nCaseReadCoe := nCaseReadCoe + 1;
IF nCaseReadCoe > 20 THEN
nCaseReadCoe := 1;
bFirstRead := FALSE; // Reset bFirstRead after successfull readout of all parameters
END_IF
ELSE
nCaseReadCoe := 0; // Go to Error state
END_IF
END_IF
fbCoeRead();
stCoeParamsInternal := stVcnVat590.stCoeParameters; //Save current values to internal structure
END_ACTION
ACTION ReadFromInput:
(*Read from EPICS*)
fUpperLimit := LIMIT(0, stVcnVat590.fPosSetLimit, 100);
fPresMaxLim := LIMIT(cMinPress, stVcnVat590.fPresSetLimit, cMaxPress);
fPresRbMaxLim := LIMIT(cMinPress, stVcnVat590.fPresRdbkLimit, cMaxPress);
fReqPos := LIMIT(0, stVcnVat590.fReqPosition, 100);
fReqPres := LIMIT(cMinPress, stVcnVat590.fReqPressure, cMaxPress);
(*Read from VAT valve*)
stVcnVat590.stValveStatus.eControlMode := i_eControlMode;
stVcnVat590.stValveStatus.eFatalErrors := i_eFatalError;
stVcnVat590.nRawPosition := i_nRawPosition;
stVcnVat590.nRawPressure := i_nRawPressure;
//Mapping General statuses
stVcnVat590.stValveStatus.bFieldBusDatavalid := i_stGenStatus.Bit0FieldBusDatavalid;
stVcnVat590.stValveStatus.bZeroExecuted := i_stGenStatus.Bit1ZeroExecuted;
stVcnVat590.stValveStatus.bPingPongRxBit := i_stGenStatus.Bit2PingPongRxBit;
stVcnVat590.stValveStatus.bPressureSimulation := i_stGenStatus.Bit3PressureSimulation;
stVcnVat590.stValveStatus.bPressureSPReached := i_stGenStatus.Bit4PressureSPReached;
stVcnVat590.stValveStatus.bWarningsActive := i_stGenStatus.Bit9WarningsActive;
//Read Remote status
stVcnVat590.stValveStatus.eRemoteStatus.0 := i_stGenStatus.Bit7AccessModeBit0;
stVcnVat590.stValveStatus.eRemoteStatus.1 := i_stGenStatus.Bit8AccessModeBit1;
//Mapping General warnings
stVcnVat590.stValveStatus.bServiceRequest := i_stGenWarnings.Bit0ServiceRequest;
stVcnVat590.stValveStatus.bPowerFailureBattery := i_stGenWarnings.Bit3PowerFailureBattery;
stVcnVat590.stValveStatus.bSensMeasUnitFault := i_stGenWarnings.Bit10SensMeasUnitFault;
//Mapping Extended warnings
stVcnVat590.stValveStatus.bRemoteControlNotPossible := i_stExtnWarnings.Bit0RemoteControlNotPossible;
stVcnVat590.stValveStatus.bControlModeSPNotAllowed := i_stExtnWarnings.Bit1ControlModeSPNotAllowed;
stVcnVat590.stValveStatus.bZeroDisabled := i_stExtnWarnings.Bit2ZeroDisabled;
stVcnVat590.stValveStatus.bPFODeactivated := i_stExtnWarnings.Bit3PFODeactivated;
stVcnVat590.stValveStatus.bPressureSPOutOfRange := i_stExtnWarnings.Bit5PressureSPOutOfRange;
stVcnVat590.stValveStatus.bPositionSPOutOfRange := i_stExtnWarnings.Bit6PositionSPOutOfRange;
stVcnVat590.stValveStatus.bCtrlSPOutOfRange := i_stExtnWarnings.Bit10CtrlSPOutOfRange;
stVcnVat590.stValveStatus.bGeneralCtrlSPOutOfRange := i_stExtnWarnings.Bit11GeneralCtrlSPOutOfRange;
stVcnVat590.stValveStatus.bProcDataSettingsNotvalid := i_stExtnWarnings.Bit12ProcDataSettingsNotvalid;
//Mapping WcState - Valve Ethercat Status
stVcnVat590.bValveEthCatStatus := i_bValveEthCatStatus;
END_ACTION
ACTION SetCoeInterface:
stVat590DriveRef.sNetId := F_CreateAmsNetId(i_stVat590PlcDriveRef.aNetId);
stVat590DriveRef.nSlaveAddr := i_stVat590PlcDriveRef.nSlaveAddr;
stVat590DriveRef.nDriveNo := i_stVat590PlcDriveRef.nDriveNo;
stVat590DriveRef.nDriveType := i_stVat590PlcDriveRef.nDriveType;
fbCoeRead.stDriveRef := stVat590DriveRef;
fbCoeWrite.stDriveRef := stVat590DriveRef;
END_ACTION
ACTION WriteCoeParameters:
// User will change one parameter at a time so solution with ELSIF should work OK
IF stVcnVat590.bCoeError THEN
IF rtCoeReset.Q THEN
stVcnVat590.bCoeError := FALSE;
END_IF;
ELSIF bFirstRead THEN
;//Wait until first readout of all CoE parameters is completed
ELSIF stVcnVat590.stCoeParameters.ePIDCtrl <> stCoeParamsInternal.ePIDCtrl THEN
fbCoeWrite.nIndex := cPressCtrl.nIndex;
fbCoeWrite.nSubIndex := cPressCtrl.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.ePIDCtrl);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.ePIDCtrl);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fSensorDelay_AD <> stCoeParamsInternal.fSensorDelay_AD THEN
fbCoeWrite.nIndex := cSensorDelay_AD.nIndex;
fbCoeWrite.nSubIndex := cSensorDelay_AD.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fSensorDelay_AD);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fSensorDelay_AD);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fRampTime_AD <> stCoeParamsInternal.fRampTime_AD THEN
fbCoeWrite.nIndex := cRampTime_AD.nIndex;
fbCoeWrite.nSubIndex := cRampTime_AD.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_AD);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_AD);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bRampMode_AD <> stCoeParamsInternal.bRampMode_AD THEN
fbCoeWrite.nIndex := cRampMode_AD.nIndex;
fbCoeWrite.nSubIndex := cRampMode_AD.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_AD);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_AD);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fGainFactor_AD <> stCoeParamsInternal.fGainFactor_AD THEN
fbCoeWrite.nIndex := cGainFactor_AD.nIndex;
fbCoeWrite.nSubIndex := cGainFactor_AD.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fGainFactor_AD);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fGainFactor_AD);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fRampTime_FIX1 <> stCoeParamsInternal.fRampTime_FIX1 THEN
fbCoeWrite.nIndex := cRampTime_FIX1.nIndex;
fbCoeWrite.nSubIndex := cRampTime_FIX1.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_FIX1);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_FIX1);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bRampMode_FIX1 <> stCoeParamsInternal.bRampMode_FIX1 THEN
fbCoeWrite.nIndex := cRampMode_FIX1.nIndex;
fbCoeWrite.nSubIndex := cRampMode_FIX1.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_FIX1);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_FIX1);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bCtrlDir_FIX1 <> stCoeParamsInternal.bCtrlDir_FIX1 THEN
fbCoeWrite.nIndex := cCtrlDir_FIX1.nIndex;
fbCoeWrite.nSubIndex := cCtrlDir_FIX1.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bCtrlDir_FIX1);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bCtrlDir_FIX1);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fPGain_FIX1 <> stCoeParamsInternal.fPGain_FIX1 THEN
fbCoeWrite.nIndex := cPGain_FIX1.nIndex;
fbCoeWrite.nSubIndex := cPGain_FIX1.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fPGain_FIX1);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_FIX1);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fIGain_FIX1 <> stCoeParamsInternal.fIGain_FIX1 THEN
fbCoeWrite.nIndex := cIGain_FIX1.nIndex;
fbCoeWrite.nSubIndex := cIGain_FIX1.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fIGain_FIX1);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fIGain_FIX1);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fRampTime_FIX2 <> stCoeParamsInternal.fRampTime_FIX2 THEN
fbCoeWrite.nIndex := cRampTime_FIX2.nIndex;
fbCoeWrite.nSubIndex := cRampTime_FIX2.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_FIX2);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_FIX2);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bRampMode_FIX2 <> stCoeParamsInternal.bRampMode_FIX2 THEN
fbCoeWrite.nIndex := cRampMode_FIX2.nIndex;
fbCoeWrite.nSubIndex := cRampMode_FIX2.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_FIX2);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_FIX2);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bCtrlDir_FIX2 <> stCoeParamsInternal.bCtrlDir_FIX2 THEN
fbCoeWrite.nIndex := cCtrlDir_FIX2.nIndex;
fbCoeWrite.nSubIndex := cCtrlDir_FIX2.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bCtrlDir_FIX2);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bCtrlDir_FIX2);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fPGain_FIX2 <> stCoeParamsInternal.fPGain_FIX2 THEN
fbCoeWrite.nIndex := cPGain_FIX2.nIndex;
fbCoeWrite.nSubIndex := cPGain_FIX2.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fPGain_FIX2);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_FIX2);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fIGain_FIX2 <> stCoeParamsInternal.fIGain_FIX2 THEN
fbCoeWrite.nIndex := cIGain_FIX2.nIndex;
fbCoeWrite.nSubIndex := cIGain_FIX2.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fIGain_FIX2);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fIGain_FIX2);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fRampTime_SP <> stCoeParamsInternal.fRampTime_SP THEN
fbCoeWrite.nIndex := cRampTime_SP.nIndex;
fbCoeWrite.nSubIndex := cRampTime_SP.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fRampTime_SP);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fRampTime_SP);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.bRampMode_SP <> stCoeParamsInternal.bRampMode_SP THEN
fbCoeWrite.nIndex := cRampMode_SP.nIndex;
fbCoeWrite.nSubIndex := cRampMode_SP.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.bRampMode_SP);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.bRampMode_SP);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fPGain_SP <> stCoeParamsInternal.fPGain_SP THEN
fbCoeWrite.nIndex := cPGain_SP.nIndex;
fbCoeWrite.nSubIndex := cPGain_SP.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fPGain_SP);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPGain_SP);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fPositionLimitOpen <> stCoeParamsInternal.fPositionLimitOpen THEN
fbCoeWrite.nIndex := cPosLimitOpen.nIndex;
fbCoeWrite.nSubIndex := cPosLimitOpen.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fPositionLimitOpen);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPositionLimitOpen);
fbCoeWrite.bExecute := TRUE;
ELSIF stVcnVat590.stCoeParameters.fPositionLimitOpenControl <> stCoeParamsInternal.fPositionLimitOpenControl THEN
fbCoeWrite.nIndex := cPosLimitOpenCtrl.nIndex;
fbCoeWrite.nSubIndex := cPosLimitOpenCtrl.nSubIndex;
fbCoeWrite.pSrcBuf := ADR(stVcnVat590.stCoeParameters.fPositionLimitOpenControl);
fbCoeWrite.cbBufLen := SIZEOF(stVcnVat590.stCoeParameters.fPositionLimitOpenControl);
fbCoeWrite.bExecute := TRUE;
END_IF
ftCoeWriteBusy(CLK:=fbCoeWrite.bBusy);
IF ftCoeWriteBusy.Q THEN
fbCoeWrite.bExecute := FALSE;
IF fbCoeWrite.bError THEN
stVcnVat590.bCoeError := TRUE;
END_IF
END_IF
fbCoeWrite();
END_ACTION
ACTION WriteToOutput:
(*Write to EPICS*)
stVcnVat590.bIlkOK := bExtIlkOK;
stVcnVat590.nPositionSP := REAL_TO_DINT(fCalcPosSP);
stVcnVat590.nPressureSP := REAL_TO_DINT(fCalcPresSP);
stVcnVat590.fReqPosition := fReqPos;
stVcnVat590.fReqPressure := fReqPres;
stVcnVat590.fPresSetLimit := fPresMaxLim;
stVcnVat590.fPosSetLimit := fUpperLimit;
(*Write to VAT valve*)
o_eCtrlModeSP := eValveModeSP;
o_nRawPositionSP := REAL_TO_DINT(fCalcPosSP);
o_nRawPressureSP := REAL_TO_DINT(fCalcPresSP);
o_nRawPressure := REAL_TO_DINT(fPressRaw);
o_stGenCtrlSP.Bit0ExecuteZero := stVcnVat590.bZero;
o_stGenCtrlSP.Bit4AccessMode := bRemAcsToggle;
END_ACTION
METHOD SetThrottle : BOOL
VAR_INPUT
rUpperLimit : REAL;
END_VAR
stVcnVat590.fPosSetLimit := LIMIT(0, rUpperLimit, 100);
END_METHOD
METHOD ValveControl : BOOL
VAR_INPUT
state : E_VCN_VAT590; // Set Control modes
END_VAR
stVcnVat590.eValveControl := state;
END_METHOD
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
i_xVeto_Enable:BOOL:= FALSE; (*Set to TRUE if there is a Veto Valve to this VFS*)
END_VAR
VAR_IN_OUT
io_fbFFHWO : FB_HardwareFFOutput;
END_VAR
VAR
xOPN_OK :BOOL;
xERR_ExtFault :BOOL;
xVeto: 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;
rTrigOverrideOpen : R_TRIG;
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;
(*VETO Devices*)
i_xVetoValveOpenDO AT%I*: BOOL;
i_xVetoValveClosed 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
(*set Veto bit*)
IF (i_xVeto_Enable) THEN
xVeto := (i_xVetoValveClosed AND NOT i_xVetoValveOpenDO);
ELSE xVeto:=FALSE;
END_IF
//xVeto:= i_xVeto AND (i_xVetoValveClosed AND NOT i_xVetoValveOpenDO);
(* 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)) AND NOT(xVeto) AND NOT (tonOvrd.Q AND i_xOverrideMode) THEN
xClose:= TRUE;
xOpen:= FALSE;
q_xVAC_FAULT_OK:= FALSE;
xERR_ExtFault := TRUE; ;
i_xCLS_SW := TRUE;
q_xOPN_DO := FALSE;
//i_xOPN_SW :=FALSE;
xOpen:=FALSE;
ELSIF (i_xCLS_SW) THEN
xClose:= TRUE;
xOpen:= FALSE;
q_xVAC_FAULT_OK:= FALSE;
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) OR xVeto) 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 OR xVeto) ;
(* 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) OR xVeto) 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
*)
rTrigOverrideOpen(CLK:= i_xOverrideOpen);
tonDelOK(IN:=xOPN_OK, PT:=tDelOK);
rtOK(CLK:=tonDelOK.Q);
IF rtOK.Q AND i_xOverrideOpen THEN
i_xOverrideOpen :=FALSE;
//if (i_xOpnLS) AND (i_xOverrideMode) THEN i_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 AND i_xVAC_FAULT_Reset) OR xVeto THEN
xERR_ExtFault := FALSE;
i_xVAC_FAULT_Reset:=FALSE;
END_IF
IF (i_xTrigger OR NOT i_xPress_OK) AND NOT xVeto THEN
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_2OO3
(* This function block implements basic functionality for Fast Shutter/Valves*)
(* NOTE: All 3 gauge cards with fast TTL output need to be installed in the same gauge controller*)
(* 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_2OO3 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 := 'VFS'; (*Device name for diagnostic*)
i_xVeto_Enable : BOOL := FALSE; (*Set to TRUE if there is a Veto Valve to this VFS*)
END_VAR
VAR_IN_OUT
io_fbFFHWO : FB_HardwareFFOutput;
END_VAR
VAR
xOPN_OK : BOOL; //internal variable OK to Open signal
xERR_ExtFault : BOOL; //internal variable External Fault signal
xVeto : BOOL; //internal variable Veto signal
xTrigger : BOOL; //internal trigger signal, output of 2oo3 logic
xPress_OK : BOOL; //internal signal indicating if at gauges are connected and OK
//Fault counter triggers
rtFault_IG1 : R_TRIG;
rtFault_IG2 : R_TRIG;
rtFault_IG3 : R_TRIG;
//Reset Fault counter trigger
rtResetFaultCounter : R_TRIG;
//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; //Current pressure
xFirstPass : BOOL := TRUE; //Do something on first pass, after that set to FALSE
rtOPN_SW : R_TRIG; //Detect rising endge on i_xOPN_SW input
tonDelOK : TON;
rtOK : R_TRIG;
tonOvrd : TON;
tDelOK : TIME := T#60S;
tOvrd : TIME := T#10s;
rTrigOverrideOpen : R_TRIG;
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 to the valve(s)*)
i_xOpnLS AT%I* : BOOL; //VFS Open limit switch input
i_xClsLS AT%I* : BOOL; //VFS Close limit switch input
q_xClose_A AT%Q* : BOOL; //3x Valve Close output signals (current req.)
q_xClose_B AT%Q* : BOOL; //3x Valve Close output signals (current req.)
q_xClose_C AT%Q* : BOOL; //3x Valve Close output signals (current req.)
q_xOPN_DO AT%Q* : BOOL; //Valve Open output signal
(*3x Fast trigger Inputs from CC Gauge controller.
Make sure all tri CC gauge cards are in the same controller*)
i_xTriggerIG1 AT%I* : BOOL; //Fast trigger input from CC gauge 1
i_xTriggerIG2 AT%I* : BOOL; //Fast trigger input from CC gauge 2
i_xTriggerIG3 AT%I* : BOOL; //Fast trigger input from CC gauge 3
(*VETO Devices*)
i_xVetoValveOpenDO AT%I* : BOOL; (*Link to VFS Interface*)
i_xVetoValveClosed AT%I* : BOOL; (*Link to VFS Interface*)
(*Interface with external system*)
i_e2OO3_MODE AT%I* : E_2OO3_MODE; (*Link to VFS Interface, 2oo3 mode selector*)
i_ResetFaultCounter AT%I* : BOOL; (*Link to VFS Interface, FaultCounter Reset*)
i_xPress_OK_IG1 AT%I* : BOOL; (*Link to VFS Interface, IG.xPRESS_OK, to make sure that the device is connected*)
i_xPress_OK_IG2 AT%I* : BOOL; (*Link to VFS Interface, IG.xPRESS_OK, to make sure that the device is connected*)
i_xPress_OK_IG3 AT%I* : BOOL; (*Link to VFS Interface, IG.xPRESS_OK, to make sure that the device is connected*)
i_xOPN_SW AT%I* : BOOL; (*Link to VFS Interface, external open signal e.g epics*)
i_xCLS_SW AT%I* : BOOL; (*Link to VFS Interface, external close signal e.g epics*)
i_xVAC_FAULT_Reset AT%I* : BOOL; (*Link to VFS Interface Valve Vacuum OK, is set to False when there is Vacuum Fault*)
i_xOverrideMode AT%I* : BOOL; (*Link to VFS Interface, To be linked to global override bit. This Overrides Vacuum logic only, EPS, MPS and PMPS are still enforces*)
i_xOverrideOpen AT%I* : BOOL; (*Link to VFS Interface*)
q_xTrigger AT%Q* : BOOL; (*Link to VFS Interface*)
q_xVFS_Open AT%Q* : BOOL; (*Link to VFS Interface*)
q_xVFS_Closed AT%Q* : BOOL; (*Link to VFS Interface*)
q_xVAC_FAULT_OK AT%Q* : BOOL; (*Link to VFS Interface, Valve Vacuum OK, is set to False when there is Vacuum Fault*)
q_xMPS_OK AT%Q* : BOOL; (*Link to VFS Interface, MPS Fault OK, is set when the Valve is Open and there is no trigger*)
q_eVFS_State AT%Q* : E_VGC; (*Link to VFS Interface*)
q_nFltCount_IG1 AT%Q* : UINT; (*Link to VFS Interface*)
q_nFltCount_IG2 AT%Q* : UINT; (*Link to VFS Interface*)
q_nFltCount_IG3 AT%Q* : UINT; (*Link to VFS Interface*)
END_VAR
VAR PERSISTENT
nFaultCounter_IG1 : UINT; //Fault counter for IG1
nFaultCounter_IG2 : UINT; //Fault counter for IG2
nFaultCounter_IG3 : UINT; //Fault counter for IG3
END_VAR
(*Fast Shutter/Valve Function Block*)
(*M.Ghaly Feb 2019*)
(*J. Govednik Jan 2022 Adding 2OO3 logic*)
(* 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
(*set Veto bit*)
IF (i_xVeto_Enable) THEN
xVeto := (i_xVetoValveClosed AND NOT i_xVetoValveOpenDO);
ELSE xVeto := FALSE;
END_IF
(*User will be able to select gauge channel or 2oo3 function*)
CASE i_e2OO3_MODE OF
E_2OO3_MODE.MODE_2OO3:
(*2oo3 logic*)
//First let's verify that at least 2 gauges are connected
xPress_OK := (i_xPress_OK_IG1 AND i_xPress_OK_IG2) OR
(i_xPress_OK_IG1 AND i_xPress_OK_IG3) OR
(i_xPress_OK_IG2 AND i_xPress_OK_IG3);
//Each trigger input validated with Press OK bit which indicates if the gauge is connected or disconnected
IF xPress_OK THEN
xTrigger := ((i_xTriggerIG1 OR NOT i_xPress_OK_IG1) AND (i_xTriggerIG2 OR NOT i_xPress_OK_IG2)) OR
((i_xTriggerIG1 OR NOT i_xPress_OK_IG1) AND (i_xTriggerIG3 OR NOT i_xPress_OK_IG3)) OR
((i_xTriggerIG2 OR NOT i_xPress_OK_IG2) AND (i_xTriggerIG3 OR NOT i_xPress_OK_IG3));
(*
xTrigger := ((i_xTriggerIG1 AND i_xPress_OK_IG1) AND (i_xTriggerIG2 AND i_xPress_OK_IG2)) OR
((i_xTriggerIG1 AND i_xPress_OK_IG1) AND (i_xTriggerIG3 AND i_xPress_OK_IG3)) OR
((i_xTriggerIG2 AND i_xPress_OK_IG2) AND (i_xTriggerIG3 AND i_xPress_OK_IG3));
*)
ELSE
xTrigger := TRUE;
END_IF
E_2OO3_MODE.IG1:
xPress_OK := i_xPress_OK_IG1;
xTrigger := i_xTriggerIG1 OR NOT i_xPress_OK_IG1;
E_2OO3_MODE.IG2:
xPress_OK := i_xPress_OK_IG2;
xTrigger := i_xTriggerIG2 OR NOT i_xPress_OK_IG2;
E_2OO3_MODE.IG3:
xPress_OK := i_xPress_OK_IG3;
xTrigger := i_xTriggerIG3 OR NOT i_xPress_OK_IG3;
END_CASE;
//xVeto:= i_xVeto AND (i_xVetoValveClosed AND NOT i_xVetoValveOpenDO);
(*Input Trigger to close the Fast Shutter*)
(*Neighboring Valves Should also be triggered to Close*)
//IF ((xTrigger) OR NOT (i_xPress_OK) OR NOT (i_xExt_OK)) AND NOT (xVeto) AND NOT (tonOvrd.Q AND i_xOverrideMode) THEN
IF ((xTrigger) OR NOT (i_xExt_OK)) AND NOT (xVeto) AND NOT (tonOvrd.Q AND i_xOverrideMode) THEN
xClose := TRUE;
xOpen := FALSE;
q_xVAC_FAULT_OK := FALSE;
xERR_ExtFault := TRUE;
i_xCLS_SW := TRUE;
q_xOPN_DO := FALSE;
//i_xOPN_SW :=FALSE;
ELSIF (i_xCLS_SW) THEN
xClose := TRUE;
xOpen := FALSE;
q_xVAC_FAULT_OK:= FALSE;
i_xCLS_SW := TRUE;
q_xOPN_DO := FALSE;
//i_xOPN_SW :=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
//Hold Valve close signals to TRUE until limit swith is hit
IF (xClose) AND (NOT i_xClsLS) THEN
q_xClose_A := TRUE;
q_xClose_B := TRUE;
q_xClose_C := TRUE;
END_IF
//Set Valve close signals to FALSE when close timer expires or close limit switch is closed
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
//Set Valve open signal to FALSE when open timer expires or open limit switch is closed
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 xTrigger) OR xVeto) 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 xTrigger OR xVeto) ;
(*When the valve is open MPS is OK*)
//q_xMPS_OK := i_xOpnLS AND ((NOT xTrigger AND i_xPress_OK) OR xVeto) AND NOT(i_xCLS_SW);
q_xMPS_OK := i_xOpnLS AND (NOT xTrigger OR xVeto) 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
*)
rTrigOverrideOpen (CLK := i_xOverrideOpen); // Not used
tonDelOK (IN := xOPN_OK, PT := tDelOK);
rtOK (CLK := tonDelOK.Q);
IF rtOK.Q AND i_xOverrideOpen THEN
i_xOverrideOpen := FALSE;
//if (i_xOpnLS) AND (i_xOverrideMode) THEN i_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 (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);
OpenTimer (IN := i_xOpnLS, PT := OpenTimDur);
tOPNtimeout (IN := q_xOPN_DO, PT := tOPNTimeOutDuration);
tCLStimeout (IN := q_xClose_A, PT := tCLSTimeOutDuration);
//Detecting rising edge for i_xOPN_SW (epics command)
rtOPN_SW (CLK := i_xOPN_SW);
(*Gauge fault counters*)
rtResetFaultCounter(CLK := i_ResetFaultCounter);
IF rtResetFaultCounter.Q THEN
nFaultCounter_IG1 := 0;
nFaultCounter_IG2 := 0;
nFaultCounter_IG3 := 0;
END_IF
//IG1
rtFault_IG1(CLK := i_xTriggerIG1 AND NOT xTrigger);
IF rtFault_IG1.Q THEN
nFaultCounter_IG1 := nFaultCounter_IG1 + 1;
END_IF
//IG2
rtFault_IG2(CLK := i_xTriggerIG2 AND NOT xTrigger);
IF rtFault_IG2.Q THEN
nFaultCounter_IG2 := nFaultCounter_IG2 + 1;
END_IF
//IG3
rtFault_IG3(CLK := i_xTriggerIG3 AND NOT xTrigger);
IF rtFault_IG3.Q THEN
nFaultCounter_IG3 := nFaultCounter_IG3 + 1;
END_IF
(*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 xTrigger AND xPress_OK AND i_xVAC_FAULT_Reset) OR xVeto THEN
xERR_ExtFault := FALSE;
i_xVAC_FAULT_Reset := FALSE;
END_IF
IF (xTrigger OR NOT xPress_OK) AND NOT xVeto THEN
i_xVAC_FAULT_Reset := FALSE;
END_IF
END_ACTION
ACTION IO:
q_xTrigger := xTrigger;
q_xVFS_Closed := i_xClsLS;
q_xVFS_Open := i_xOpnLS;
q_nFltCount_IG1 := nFaultCounter_IG1;
q_nFltCount_IG2 := nFaultCounter_IG2;
q_nFltCount_IG3 := nFaultCounter_IG3;
END_ACTION
- Related:
FB_VFS_2OO3Interface
(*Used as soft IO mapping to create a psuedo valve to communicate over two task on the same PLC.*)
(*NOTE: All 3 gauge cards with fast TTL output need to be installed in the same gauge controller*)
(*For FAST shutter control*)
FUNCTION_BLOCK FB_VFS_2OO3Interface EXTENDS FB_Valve
VAR_INPUT
(*Make sure all three CC gauge cards are in the same controller*)
IG1 : ST_VG; // The MKS422 Cold Cathode Data Structure for CC gauge 1
IG2 : ST_VG; // The MKS422 Cold Cathode Data Structure for CC gauge 2
IG3 : ST_VG; // The MKS422 Cold Cathode Data Structure for CC gauge 3
Veto_Valve : ST_VGC; // The VGC structure for the Veto Valve
{attribute 'pytmc' := '
pv: VFS_2OO3_MODE
io: io
'}
i_e2OO3_MODE : E_2OO3_MODE; //2oo3 mode selector as EPICS Command or Input for global variable
{attribute 'pytmc' := '
pv: VFS_RESET_FAULT_COUNTER
io: io
'}
i_ResetFaultCounter : BOOL; //Reset Fault counter as EPCIS Command or Input for global reset button
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
rtTriggerVetoed : R_TRIG;
rtTriggered : R_TRIG;
tonOvrd : TON;
tOvrd : TIME := T#10s;
rtOK : R_TRIG;
tonDelOK : TON;
xOPN_OK : BOOL;
tDelOK : TIME := T#60S;
xPress_OK : BOOL; //internal signal indicating if at gauges are connected and OK
//Reset Fault counter trigger
rtResetFaultCounter : R_TRIG;
(*outputs*)
q_e2OO3_MODE AT%Q* : E_2OO3_MODE; //2oo3 mode selector, connect to FB_VFS_2OO3 mode selector input
q_ResetFaultCounter AT%Q* : BOOL; //Reset Fault counter, connect to FB_VFS_2OO3 reset fault counter input
q_xPRESS_OK_IG1 AT%Q* : BOOL;
q_xPRESS_OK_IG2 AT%Q* : BOOL;
q_xPRESS_OK_IG3 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;
(*VETO Devices*)
q_xVetoValveOpenDO AT%Q* : BOOL;
q_xVetoValveClosed 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*)
{attribute 'pytmc' := '
pv: FLT_COUNT_IG1
io: i
'}
i_nFltCount_IG1 AT%I* : UINT; //Fault counter for IG1
{attribute 'pytmc' := '
pv: FLT_COUNT_IG2
io: i
'}
i_nFltCount_IG2 AT%I* : UINT; //Fault counter for IG2
{attribute 'pytmc' := '
pv: FLT_COUNT_IG3
io: i
'}
i_nFltCount_IG3 AT%I* : UINT; //Fault counter for IG3
END_VAR
(*Soft IO Mapping*)
q_xPRESS_OK_IG1 := IG1.xPRESS_OK;
q_xPRESS_OK_IG2 := IG2.xPRESS_OK;
q_xPRESS_OK_IG3 := IG3.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);
tonDelOK(IN:=xOPN_OK, PT:=tDelOK);
rtOK(CLK:=tonDelOK.Q);
IF rtOK.Q AND q_xOverrideOpen THEN
q_xOverrideOpen :=FALSE;
//if (i_xOpnLS) AND (i_xOverrideMode) THEN i_xOPN_SW := TRUE; END_IF //for seamless transition
//Log
fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning);
END_IF
(*OPN OK evaluation bit *)
xOPN_OK := (i_xVAC_FAULT_OK) AND (NOT i_xTrigger OR (q_xVetoValveClosed AND NOT q_xVetoValveOpenDO));
(*Reset Fault counter, q_ResetFaultCounter output is TRUE for one plc cycle *)
rtResetFaultCounter(CLK := i_ResetFaultCounter);
IF rtResetFaultCounter.Q THEN
i_ResetFaultCounter := FALSE;
q_ResetFaultCounter := TRUE;
ELSE
q_ResetFaultCounter := FALSE;
END_IF
(*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
//Log when the trigger is vetoed
rtTriggerVetoed(CLK:= (i_xTrigger AND (NOT q_xVetoValveOpenDO AND q_xVetoValveClosed)));
IF rtTriggerVetoed.Q THEN fbLogger(sMsg:='Valve triggered while veto device is in', eSevr:=TcEventSeverity.Warning); END_IF
rtTriggered(CLK:= (i_xTrigger AND ( q_xVetoValveOpenDO AND NOT q_xVetoValveClosed)));
IF rtTriggered.Q THEN fbLogger(sMsg:='Fast valve triggered!', eSevr:=TcEventSeverity.Critical); 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 := CONCAT(CONCAT(CONCAT(IG1.sPath,','),CONCAT(IG2.sPath,',')),IG3.sPath);
(*update values*)
iq_stValve.xOPN_OK := xOPN_OK;
(*Veto*)
(*VETO Devices*)
q_xVetoValveOpenDO := Veto_Valve.q_xOPN_DO;
q_xVetoValveClosed := Veto_Valve.i_xClsLS;
//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
//Veto device name
iq_stValve.sVetoDeviceName := Veto_Valve.sDevName;
(*2oo3 mode selector*)
q_e2OO3_MODE := i_e2OO3_MODE;
END_ACTION
- Related:
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
Veto_Valve: ST_VGC;// The VGC structure for the Veto Valve
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
rtTriggerVetoed : R_TRIG;
rtTriggered : R_TRIG;
tonOvrd : TON;
tOvrd : TIME := T#10s;
rtOK : R_TRIG;
tonDelOK : TON;
xOPN_OK : BOOL;
tDelOK : TIME := T#60S;
(*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;
(*VETO Devices*)
q_xVetoValveOpenDO AT%Q*: BOOL;
q_xVetoValveClosed 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);
tonDelOK(IN:=xOPN_OK, PT:=tDelOK);
rtOK(CLK:=tonDelOK.Q);
IF rtOK.Q AND q_xOverrideOpen THEN
q_xOverrideOpen :=FALSE;
//if (i_xOpnLS) AND (i_xOverrideMode) THEN i_xOPN_SW := TRUE; END_IF //for seamless transition
//Log
fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning);
END_IF
(*OPN OK evaluation bit *)
xOPN_OK := (i_xVAC_FAULT_OK) AND (NOT i_xTrigger OR (q_xVetoValveClosed AND NOT q_xVetoValveOpenDO)) ;
(* 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
//Log when the trigger is vetoed
rtTriggerVetoed(CLK:= (NOT q_xPRESS_OK OR i_xTrigger) AND (NOT q_xVetoValveOpenDO AND q_xVetoValveClosed));
IF rtTriggerVetoed.Q THEN fbLogger(sMsg:='Valve triggered while veto device is in', eSevr:=TcEventSeverity.Warning); END_IF
rtTriggered(CLK:= (NOT q_xPRESS_OK OR i_xTrigger) AND ( q_xVetoValveOpenDO AND NOT q_xVetoValveClosed));
IF rtTriggered.Q THEN fbLogger(sMsg:='Fast valve triggered!', eSevr:=TcEventSeverity.Critical); 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;
(*update values*)
iq_stValve.xOPN_OK := xOPN_OK;
(*Veto*)
(*VETO Devices*)
q_xVetoValveOpenDO := Veto_Valve.q_xOPN_DO;
q_xVetoValveClosed := Veto_Valve.i_xClsLS;
//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
//Veto device name
iq_stValve.sVetoDeviceName := Veto_Valve.sDevName;
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_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
VAR PERSISTENT
rAT_VAC_SP : REAL;
rHYST_PERC : REAL;
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;
iq_stValve.sDevName := 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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*FAST FAULT*)
FFO(i_xOK := xMPS_OK,
i_xReset := i_xReset,
i_xAutoReset :=TRUE,
i_DevName := i_sDevName,
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_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rAT_VAC_SP <> 0) THEN
iq_stValve.rAT_VAC_SP := rAT_VAC_SP;
END_IF;
IF (rHYST_PERC <> 0) THEN
iq_stValve.rHYST_PERC := rHYST_PERC;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stValve.rAT_VAC_SP = rAT_VAC_SP) THEN
rAT_VAC_SP :=iq_stValve.rAT_VAC_SP;
END_IF;
IF NOT (iq_stValve.rHYST_PERC = rHYST_PERC) THEN
rHYST_PERC:= iq_stValve.rHYST_PERC;
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_sDeviceName:=i_sDevName,
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
- Related:
FB_VGC_2S
(* This function block implements basic functionality for Isolation Gate Valves with 2 pressure setpoints*)
(* 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-setpoints.
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_2S 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_2S; (* 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_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
VAR PERSISTENT
rAT_VAC_SP : REAL;
rHYST_PERC : REAL;
rAT_VAC_SP_DS : REAL;
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;
iq_stValve.rAT_VAC_SP_DS := 0.001;
iq_stValve.rAT_VAC_SP := 0.001;
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_DS <> iq_stValve.rAT_VAC_SP_DS OR xFirstPass THEN
iq_stValve.rAT_VAC_SP_LAST_DS := iq_stValve.rAT_VAC_SP_DS;
iq_stValve.rAT_VAC_HYS_DS := iq_stValve.rHYST_PERC * iq_stValve.rAT_VAC_SP_DS;
END_IF
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
// Setpoint one Upstream gauge
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
//setpoint two Downstream gauge
iq_stValve.rAT_VAC_HYS_DS := LIMIT(0, iq_stValve.rAT_VAC_HYS_DS, iq_stValve.rAT_VAC_SP_DS);
IF iq_stValve.rAT_VAC_SP_DS <> 0 THEN
IF iq_stValve.rHYST_PERC <> (iq_stValve.rAT_VAC_HYS_DS / iq_stValve.rAT_VAC_SP_DS) THEN
iq_stValve.rHYST_PERC := LIMIT(0, (iq_stValve.rAT_VAC_HYS_DS / iq_stValve.rAT_VAC_SP_DS) ,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_DS 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_DS 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();
(*Load or save the persistent variables*)
ACT_Persistent();
(*FAST FAULT*)
FFO(i_xOK := xMPS_OK,
i_xReset := i_xReset,
i_xAutoReset :=TRUE,
i_DevName := i_sDevName,
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_Persistent:
(*On first PLC pass, load the persistent value into the structrue variable*)
IF (SUPER^.bRestorePersistentData) THEN
SUPER^.bRestorePersistentData := FALSE;
IF (rAT_VAC_SP <> 0) THEN
iq_stValve.rAT_VAC_SP := rAT_VAC_SP;
END_IF;
IF (rHYST_PERC <> 0) THEN
iq_stValve.rHYST_PERC := rHYST_PERC;
END_IF;
IF (rAT_VAC_SP_DS <> 0) THEN
iq_stValve.rAT_VAC_SP_DS := rAT_VAC_SP_DS;
END_IF;
END_IF
(*Check if a new value has been written in the structure variable copy it to the persistent variable*)
IF NOT (iq_stValve.rAT_VAC_SP = rAT_VAC_SP) THEN
rAT_VAC_SP := iq_stValve.rAT_VAC_SP;
END_IF;
IF NOT (iq_stValve.rAT_VAC_HYS = rHYST_PERC) THEN
rHYST_PERC := iq_stValve.rAT_VAC_HYS;
END_IF;
IF NOT (iq_stValve.rAT_VAC_SP_DS = rAT_VAC_SP_DS) THEN
rAT_VAC_SP_DS := iq_stValve.rAT_VAC_SP_DS;
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_sDeviceName:=i_sDevName,
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
- Related:
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'}
{attribute 'obsolete' := 'FB_VGC_AP has been deprecated and is not valid!'}
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
- Related:
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'}
{attribute 'obsolete' := 'FB_VGC_EBD has been deprecated and is not valid!'}
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
- Related:
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
METHOD M_Check_State
VAR_INPUT
END_VAR
END_METHOD
METHOD M_INIT : BOOL
VAR//_INPUT
END_VAR
TEST('VGC_Startup');
This^.US_IG.rPRESS := 1E-9;
This^.US_IG.xPRESS_OK := TRUE;
This^.US_IG.xAT_VAC := TRUE;
This^.DS_IG.rPRESS := 1E-9;
This^.DS_IG.xPRESS_OK := TRUE;
(*This^.VGC(
i_stUSG:= This^.US_IG,
i_stDSG:= This^.DS_IG ,
i_xDis_DPIlk:= FALSE,
i_xEPS_OK:= TRUE ,
//i_xPMPS_OK:= TRUE,
i_xExt_OK:= TRUE,
i_xOverrideMode:= ,
i_xReset:= ,
iq_stValve=> ,
xMPS_OK=> ,
io_fbFFHWO:= This^.io_fbFFHWO,
fbArbiter:=g_fbArbiter);
*)
AssertFalse(This^.VGC.iq_stValve.pv_xOPN_SW,'VGC pv_xOPN_SW is set to true');
AssertFalse(VGC.iq_stValve.q_xOPN_DO,'VGC q_xOPN_DO is set to true');
TEST_FINISHED_NAMED('VGC_Startup');
END_METHOD
METHOD M_Interlock : BOOL
VAR//_INPUT
END_VAR
// Test interlock is active and attempting to open valve
TEST('VGC_Interlock_Active');
This^.US_IG.xPRESS_OK := FALSE;
This^.DS_IG.xPRESS_OK := FALSE;
VGC.M_Set_OPN_SW(TRUE);
// run the function block one more time
(*VGC(
i_stUSG:= US_IG,
i_stDSG:= DS_IG ,
i_xDis_DPIlk:= FALSE,
i_xEPS_OK:= TRUE ,
i_xPMPS_OK:= TRUE ,
i_xExt_OK:= TRUE,
i_xOverrideMode:= ,
i_xReset:= ,
iq_stValve=> ,
xMPS_OK=> ,
io_fbFFHWO:= io_fbFFHWO);*)
AssertFalse(VGC.iq_stValve.pv_xOPN_SW,'VGC pv_xOPN_SW is set to true');
AssertFalse(VGC.iq_stValve.q_xOPN_DO,'VGC q_xOPN_DO is set to true');
TEST_FINISHED_NAMED('VGC_Interlock_Active');
// Test interlock is not active and attempting to open valve
TEST('VGC_Interlock_OK');
This^.US_IG.xPRESS_OK := TRUE;
This^.DS_IG.xPRESS_OK := TRUE;
VGC.M_Set_OPN_SW(TRUE);
// run the function block one more time
(*VGC(
i_stUSG:= US_IG,
i_stDSG:= DS_IG ,
i_xDis_DPIlk:= FALSE,
i_xEPS_OK:= ,
i_xPMPS_OK:= ,
i_xExt_OK:= ,
i_xOverrideMode:= ,
i_xReset:= ,
iq_stValve=> ,
xMPS_OK=> ,
io_fbFFHWO:= io_fbFFHWO);*)
AssertTrue(VGC.iq_stValve.pv_xOPN_SW,'VGC pv_xOPN_SW is set to false');
AssertTrue(VGC.iq_stValve.q_xOPN_DO,'VGC q_xOPN_DO is set to false');
TEST_FINISHED_NAMED('VGC_Interlock_OK');
// Test interlock ok lost while valve is open
TEST('VGC_Interlock_Lost');
This^.US_IG.xPRESS_OK := False;
This^.DS_IG.xPRESS_OK := TRUE;
// run the function block one more time
(*VGC(
i_stUSG:= US_IG,
i_stDSG:= DS_IG ,
i_xDis_DPIlk:= FALSE,
i_xEPS_OK:= ,
i_xPMPS_OK:= ,
i_xExt_OK:= ,
i_xOverrideMode:= ,
i_xReset:= ,
iq_stValve=> ,
xMPS_OK=> ,
io_fbFFHWO:= io_fbFFHWO);*)
AssertFalse(VGC.iq_stValve.pv_xOPN_SW,'VGC pv_xOPN_SW is set to true');
AssertFalse(VGC.iq_stValve.q_xOPN_DO,'VGC q_xOPN_DO is set to true');
TEST_FINISHED_NAMED('VGC_Interlock_Lost');
END_METHOD
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
FB_VRC_DA
(* This Function Block Implements Basic Functionality for dual acting valves. EPs is implemented so that there is interlock Open and Interlock close,
This function block is interloked by an input . 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_DA EXTENDS FB_Valve
VAR_IN_OUT
END_VAR
VAR_INPUT
i_OPN_OK : BOOL; //Connect to EPS Interlock logic condition, Set to True if the valve is not interlocked
i_CLS_OK : BOOL; //Connect to EPS Interlock logic condition, Set to True if the valve is not interlocked
ibCntrlHold:BOOL; (* Control Signal must retain its value, False in case of pulse control*)
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_NO;
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;
(*Limit switch latch timer*)
tLimitSwitchLatchDuration: TIME:=T#1S;
tOPNLimitSwitch:TON;
tCLSLimitSwitch:TON;
tAction2 : R_TRIG;
(*IO*)
i_xOpnLS AT%I*: BOOL;
i_xClsLS AT%I*: BOOL;
q_xOPN_DO AT%Q*: BOOL;
q_xCLS_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;
iq_stValve.pv_xCLS_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 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
(* Can't have both open and close True at the same time. Reset both*)
If (iq_stValve.pv_xOPN_SW) AND (iq_stValve.pv_xCLS_SW) THEN
iq_stValve.pv_xOPN_SW := FALSE;
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
*)
// Release the Force Open bit when the system override is false
IF NOT(i_xOverrideMode) THEN
iq_stValve.pv_xOvrdOpn :=FALSE;
iq_stValve.pv_xOvrdCls := FALSE;
END_IF
//Override timer
tonOvrd(IN:=(iq_stValve.pv_xOvrdOpn OR iq_stValve.pv_xOvrdCls), PT:=tOvrd);
(* Here's where the valve Actuates *)
(*Evaluate EPS interlocks and the override*)
iq_stValve.q_xCLS_DO := (i_CLS_OK AND iq_stValve.pv_xCLS_SW AND NOT iq_stValve.pv_xOPN_SW) OR (tonOvrd.Q AND iq_stValve.pv_xOvrdCls AND i_xOverrideMode);
iq_stValve.q_xOPN_DO := (i_OPN_OK AND iq_stValve.pv_xOPN_SW AND NOT iq_stValve.pv_xCLS_SW) OR (tonOvrd.Q AND iq_stValve.pv_xOvrdOpn AND i_xOverrideMode) ;
(*evaluate Valve open external interlock*)
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
IF NOT iq_stValve.xCLS_OK and NOT tonOvrd.Q THEN
iq_stValve.pv_xCLS_SW := FALSE;
iq_stValve.eVGC_State := ERR_ExtFault;
END_IF
IF (iq_stValve.q_xCLS_DO) AND (iq_stValve.xCLS_OK) THEN
iq_stValve.eVGC_State := AtVacuum;
END_IF
(*Reset the Control signal when command has been executed and give time to ensure the actuator is fully seated in either direction*)
(* for pulse control*)
IF (NOT ibCntrlHold) THEN
IF (iq_stValve.pv_xOPN_SW AND iq_stValve.i_xOpnLS AND tOPNLimitSwitch.Q ) THEN iq_stValve.pv_xOPN_SW := FALSE; END_IF
IF (iq_stValve.pv_xCLS_SW AND iq_stValve.i_xClsLS AND tCLSLimitSwitch.Q ) THEN iq_stValve.pv_xCLS_SW := FALSE; END_IF
END_IF
///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:= iq_stValve.q_xCLS_DO, PT := tTimeOutDuration);
tCLSLimitSwitch(IN:= iq_stValve.i_xClsLS, PT := tLimitSwitchLatchDuration);
tOPNLimitSwitch(IN:= iq_stValve.i_xOpnLS, PT := tLimitSwitchLatchDuration);
(*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;
iq_stValve.xCLS_OK := i_CLS_OK;
iq_stValve.xOPN_OK := i_OPN_OK;
(*outputs*)
q_xOPN_DO:= iq_stValve.q_xOPN_DO;
q_xCLS_DO:= iq_stValve.q_xCLS_DO;
END_ACTION
ACTION ACT_Logger:
//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
tAction2(CLK:= iq_stValve.q_xCLS_DO);
IF tAction.Q THEN fbLogger(sMsg:='Valve commanded closed', 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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
METHOD M_Set_CLS_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := NOT (value);
This^.iq_stValve.pv_xCLS_SW := value;
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
This^.iq_stValve.pv_xCLS_SW := NOT (value);
END_METHOD
- Related:
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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
FB_VRC_NC_ClsLS
(* This Function Block Implements Basic Functionality for ChemRIX NC valve, which has one NC LS mounting at Open position.
Valve considers CLOSED when the LS is closed.
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_NC_ClsLS 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
IF NOT iq_stValve.i_xClsLS AND iq_stValve.q_xOPN_DO THEN
iq_stValve.eState:=OPEN;
ELSIF NOT iq_stValve.i_xClsLS AND NOT iq_stValve.q_xOPN_DO THEN
iq_stValve.eState:=INVALID;
ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.q_xOPN_DO THEN
iq_stValve.eState:=CLOSED;
ELSIF iq_stValve.i_xClsLS AND iq_stValve.q_xOPN_DO THEN
iq_stValve.eState:=INVALID;
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 iq_stValve.i_xClsLS 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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
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
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_NO;
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;
(*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
iq_stValve.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 NOT iq_stValve.q_xCLS_DO THEN
iq_stValve.eState:=OPEN;
ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND iq_stValve.q_xCLS_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
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 AND iq_stValve.pv_xOvrdCls THEN
iq_stValve.pv_xOvrdCls :=FALSE;
if (iq_stValve.eState=CLOSED) AND (i_xOverrideMode) THEN iq_stValve.pv_xCLS_SW := TRUE; END_IF
//Log
fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning);
END_IF
//Override timer
tonOvrd(IN:=iq_stValve.pv_xOvrdCls, PT:=tOvrd);
(* Here's where the valve Actuates *)
q_xCLS_DO := ((iq_stValve.xCLS_OK AND iq_stValve.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:= NOT iq_stValve.q_xCLS_DO, PT := tTimeOutDuration );
tCLStimeout(IN:= iq_stValve.q_xCLS_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*)
iq_stValve.q_xCLS_DO := q_xCLS_DO;
END_ACTION
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_CLS_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xCLS_SW := value;
END_METHOD
FB_VRC_NO_FFO
(* This function block is different from the regular VRC in that CLOSING must be permitted. *)
FUNCTION_BLOCK FB_VRC_NO_FFO EXTENDS FB_VRC_NO
VAR_IN_OUT
io_fbFFHWO : FB_HardwareFFOutput;
fbArbiter: FB_Arbiter();
END_VAR
VAR_INPUT
i_xPMPS_OK: BOOL ; (*Set to True To switch off the bptm and PMPS Arbiter*)
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_NO'; // Device name for diagnostic
i_nTransitionRootID: UDINT; //A unique transition Root ID that is equal to or greater than 1000i_xIsAperture:BOOL :=FALSE; // Set tp True if this is an Aperture Valve, the MPS Fault will trip only when moving.
END_VAR
VAR_OUTPUT
END_VAR
VAR
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 safe state',
i_TypeCode := 16#1010);
xMPS_OK: BOOL; (*MPS Fast OK, is set when the Valve is Open*)
END_VAR
(*VRC_NO*)
(* 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
iq_stValve.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 NOT iq_stValve.q_xCLS_DO THEN
iq_stValve.eState:=OPEN;
ELSIF iq_stValve.i_xClsLS AND NOT iq_stValve.i_xOpnLS AND iq_stValve.q_xCLS_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
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 AND iq_stValve.pv_xOvrdCls THEN
iq_stValve.pv_xOvrdCls :=FALSE;
if (iq_stValve.eState=CLOSED) AND (i_xOverrideMode) THEN iq_stValve.pv_xCLS_SW := TRUE; END_IF
//Log
fbLogger(sMsg:='Override expired', eSevr:=TcEventSeverity.Warning);
END_IF
//Override timer
tonOvrd(IN:=iq_stValve.pv_xOvrdCls, PT:=tOvrd);
(* Here's where the valve Actuates *)
q_xCLS_DO := ((iq_stValve.xCLS_OK AND iq_stValve.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:= NOT iq_stValve.q_xCLS_DO, PT := tTimeOutDuration );
tCLStimeout(IN:= iq_stValve.q_xCLS_DO, PT := tTimeOutDuration);
(*Soft IO Mapping*)
ACT_IO();
// Log States and triggers
ACT_Logger();
(*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.xCLS_OK) THEN
iq_stValve.pv_xCLS_SW:= FALSE; // Reset switch after inlk is lost
iq_stValve.q_xCLS_DO := (iq_stValve.pv_xCLS_SW AND iq_stValve.xCLS_OK) OR (tonOvrd.Q AND i_xOverrideMode);
ELSIF xPMPS_OK THEN
iq_stValve.q_xCLS_DO := (iq_stValve.pv_xCLS_SW AND iq_stValve.xCLS_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 NOT q_xCLS_DO) XOR (i_xClsLS AND q_xCLS_DO);
ELSE
(* When the valve is open MPS is OK*)
xMPS_OK := i_xOpnLS AND NOT i_xClsLS AND q_xCLS_DO;
END_IF
(*FAST FAULT*)
FFO(i_xOK := xMPS_OK,
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. := i_xEXT_OK;
iq_stValve.xOverrideMode := i_xOverrideMode;
(*outputs*)
//q_xOPN_DO:= iq_stValve.q_xOPN_DO;
iq_stValve.q_xCLS_DO:= q_xCLS_DO;
END_ACTION
ACTION ACT_PMPS:
bMoving := (iq_stValve.pv_xCLS_SW AND NOT iq_stValve.i_xClsLS) XOR (NOT iq_stValve.pv_xCLS_SW AND NOT i_xOpnLS);
bDone := (NOT iq_stValve.pv_xCLS_SW AND iq_stValve.i_xOpnLS) XOR ( iq_stValve.pv_xCLS_SW AND i_xClsLS);
If (i_xIsAperture ) OR NOT (iq_stValve.pv_xCLS_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(NOT iq_stValve.pv_xCLS_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
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := (This^.iq_stValve.eState = E_ValvePositionState.CLOSED);
END_METHOD
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := (This^.iq_stValve.eState = E_ValvePositionState.OPEN);
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_CLS_SW : BOOL
VAR_INPUT
value:BOOL;
END_VAR
This^.iq_stValve.pv_xCLS_SW := value;
END_METHOD
- Related:
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
- Related:
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
METHOD M_Check_State
VAR_INPUT
END_VAR
END_METHOD
METHOD M_INIT : BOOL
VAR//_INPUT
Interlock_OK :BOOL := TRUE;
ex: INT;
END_VAR
TEST('VRC_Startup');
//__TRY
VRC(i_xExtILK_OK:= Interlock_OK, i_xOverrideMode:= , iq_stValve=> );
AssertFalse(VRC.iq_stValve.q_xOPN_DO,'VRC q_xOPN_DO is set to true');
AssertFalse(VRC.iq_stValve.pv_xOPN_SW,'VRC pv_xOPN_SW is set to true');
//__CATCH(ex)
//__FINALLY
TEST_FINISHED_NAMED('VRC_Startup');
//__ENDTRY
END_METHOD
METHOD M_Interlock
VAR
US_IG : ST_VG;
DS_IG : ST_VG;
VRC: FB_VRC;
END_VAR
TEST('VRC_Interlock_Active');
VRC.M_Set_OPN_SW (TRUE);
VRC(i_xExtILK_OK:= FALSE, i_xOverrideMode:= , iq_stValve=> );
AssertFalse(VRC.iq_stValve.pv_xOPN_SW,'VRC open_SW is set to true');
AssertFalse(VRC.iq_stValve.q_xOPN_DO,'VRC open DO is set to true');
TEST_FINISHED_NAMED('VRC_Interlock_Active');
TEST('VRC_Interlock_OK');
VRC.M_Set_OPN_SW (TRUE);
VRC(i_xExtILK_OK:= TRUE, i_xOverrideMode:= , iq_stValve=> );
AssertTrue(VRC.iq_stValve.pv_xOPN_SW,'VRC open_SW is set to FALSE');
AssertTrue(VRC.iq_stValve.q_xOPN_DO,'VRC open DO is set to FALSE');
TEST_FINISHED_NAMED('VRC_Interlock_OK');
TEST('VRC_Interlock_Lost');
VRC(i_xExtILK_OK:= FALSE, i_xOverrideMode:= , iq_stValve=> );
AssertFalse(VRC.iq_stValve.pv_xOPN_SW,'VRC open_SW is set to true');
AssertFalse(VRC.iq_stValve.q_xOPN_DO,'VRC open DO is set to true');
TEST_FINISHED_NAMED('VRC_Interlock_Lost');
END_METHOD
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
// Method Returns True if the Valve is closed, i.e. the open digital output is not active
METHOD PUBLIC M_IsClosed : BOOL
VAR_INPUT
END_VAR
M_IsClosed := NOT This^.q_xOPN_DO;
END_METHOD
// Method Returns True if the Valve is open, i.e. the open digital output is active
METHOD PUBLIC M_IsOpen : BOOL
VAR_INPUT
END_VAR
M_IsOpen := This^.q_xOPN_DO;
END_METHOD
METHOD PUBLIC M_Open : BOOL
VAR_INPUT
open: bool; //Set to true to open the valvue, false to close
END_VAR
This^.iq_stValve.pv_xOPN_SW := open;
END_METHOD
// Use this Methode to Open or close the valve by setting the OPN_SW to the input value.
METHOD PUBLIC M_Set_OPN_SW : BOOL
VAR_INPUT
value:BOOL; //True to SET, False to RESET
END_VAR
This^.iq_stValve.pv_xOPN_SW := value;
END_METHOD
- Related:
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