DUTs
E_AxisMotionState
TYPE E_AxisMotionState :
(
Init := 10,
MoveEnabled := 3000,
Error := 9000
);
END_TYPE
- Related:
E_AxisMoveMode
TYPE E_AxisMoveMode :
(
Hold := 0,
MoveAbs := 1,
MoveRel := 2
) WORD;
END_TYPE
E_BHElFlowUnit
TYPE E_BHElFlowUnit :
(
enum_member := 0
);
END_TYPE
E_CTRL_MODE
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_CTRL_MODE :
(
Init := 0,
Run := 10,
Hold := 20,
Error := 9000
);
END_TYPE
E_PropValveFBState
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_PropValveFBState :
(
Init := 0,
Hold := 10,
Manual := 100,
Pressure := 200,
Error := 9000
);
END_TYPE
HMI_PropValveCtrlState
{attribute 'qualified_only'}
(* Maps to an EPICS MBBI/O *)
TYPE HMI_PropValveCtrlState :
(
Manual := 0,
Pressure := 1,
Hold := 2
) WORD;
END_TYPE
ST_ALI
TYPE ST_ALI EXTENDS ST_ALIIO:
STRUCT
(* Aerodynamic Lens Injector *)
(* Manipulator *)
axisX : ST_ManipAxis;
axisY : ST_ManipAxis;
axisZ : ST_ManipAxis;
(* Vacuum gauges *)
//925
stNozzleBoxVG : ST_VG;
//925
stSkimmerVG : ST_VG;
//722
stRelaxVG : ST_VG := (rFULL_SCALE:=10);
(* Butterfly valve pressure controller *)
stPropVlv : ST_PropValveMKS253;
(* PropValve Pressure Controller *)
//stPressCtrl : ST_PressureController;
END_STRUCT
END_TYPE
ST_ALIIO
TYPE ST_ALIIO EXTENDS ST_M3DeviceBaseIO :
STRUCT
//EP7041
//EP7041
//EP7041
//EP5101
//EP3174 Vacuum Gauges, misc analog
i_EP3174_Ch1 AT %I* : INT;
i_EP3174_Ch2 AT %I* : INT;
i_EP3174_Ch3 AT %I* : INT;
i_EP3174_Ch4 AT %I* : INT;
//EP7041 - Prop valve
//EP2338 Misc DIO
q_EP2338_Ch1 AT %Q* : BOOL;
q_EP2338_Ch2 AT %Q* : BOOL;
q_EP2338_Ch3 AT %Q* : BOOL;
q_EP2338_Ch4 AT %Q* : BOOL;
i_EP2338_Ch5 AT %I* : BOOL;
i_EP2338_Ch6 AT %I* : BOOL;
i_EP2338_Ch7 AT %I* : BOOL;
i_EP2338_Ch8 AT %I* : BOOL;
//EP4374 Pressure Control
i_EP4374_Ch1 AT %I* : INT;
i_EP4374_Ch2 AT %I* : INT;
q_EP4374_Ch3 AT %Q* : INT;
q_EP4374_Ch4 AT %Q* : INT;
END_STRUCT
END_TYPE
- Related:
ST_BhElFlow
TYPE ST_BhElFlow :
STRUCT
eFlowUnit : E_BHElFlowUnit;
{attribute 'pytmc' := '
pv: Unit
io: i
'}
i_sUnit AT %I* : STRING;
i_udiUnit : UDINT;
{attribute 'pytmc' := '
pv: Flow
io: i
'}
i_rFlow AT %I* : REAL;
{attribute 'pytmc' := '
pv: Setpoint
io: o
'}
q_rSetpoint AT %Q* : REAL;
END_STRUCT
END_TYPE
- Related:
ST_M3DeviceBaseIO
TYPE ST_M3DeviceBaseIO :
STRUCT
(* This structure should be used as a basis for M3 arch. IO clusters.
Will stash stuff like ethercat diagnostics and other general purpose stuff *)
//WC bit tells you if the sync unit is OK, which tells you if cluster is alive
{attribute 'pytmc' := '
pv: SyncUnitOK
io: i
'}
i_SyncUnitWC AT %I* : BOOL := TRUE;
END_STRUCT
END_TYPE
ST_ManifoldValve
TYPE ST_ManifoldValve :
STRUCT
(* Controls *)
{attribute 'pytmc' := '
pv: Open
io: o
'}
xSW : BOOL; //epics control
{attribute 'pytmc' := '
pv: CloseDO
io: i
'}
qxDO AT %Q* : BOOL; //actual valve output
(* Readbacks *)
{attribute 'pytmc' := '
pv: OpenSW
io: i
'}
ixOPN AT %I*: BOOL;
(* Soft variables *)
{attribute 'pytmc' := '
pv: Ilk
io: i
'}
xILK : BOOL; // permissive bit
{attribute 'pytmc' := '
pv: Name
io: i
'}
sName: STRING;
END_STRUCT
END_TYPE
ST_ManipAxis
TYPE ST_ManipAxis :
STRUCT
(* Controls *)
xEnable : BOOL := TRUE;
rReqAbsPos : REAL;
rReqRelPos : REAL;
xStart : BOOL;
xStop : BOOL;
wMode : E_AxisMoveMode;
//EPICS motor status
uiMsta : UINT;
//EPICS Position readback
rActPos : REAL;
//Axis enabled readback
xEnabled : BOOL;
//EPICS High Limit Switch (NO)
xHLS : BOOL;
//EPICS Low Limit Switch (NO)
xLLS : BOOL;
//EPICS MOVN
xMovn : BOOL;
//EPICS DMOV
xDmov : BOOL;
//EPICS Reset
xReset : BOOL;
(* Axis motor *)
stAxis : AXIS_REF;
lrVelocity : LREAL :=1; //mm/s
eState : E_AxisMotionState;
xHiLS AT %I* : BOOL;
xLoLS AT %I* : BOOL;
END_STRUCT
END_TYPE
- Related:
ST_PIDParams
TYPE ST_PIDParams :
STRUCT
fCtrlCycleTime : LREAL; (* controller cycle time in seconds [s] *)
fKp : LREAL; (* proportional gain Kp (P) *)
fTn : LREAL; (* integral gain Tn (I) [s] *)
fTv : LREAL; (* derivative gain Tv (D-T1) [s] *)
fTd : LREAL; (* derivative damping time Td (D-T1) [s] *)
fUpperLim : LREAL; (* upper controller output range limit of PID block *)
fLowerLim : LREAL; (* lower controller output range limit of PID block *)
END_STRUCT
END_TYPE
ST_PressureController
TYPE ST_PressureController :
STRUCT
(* Intent of this structure is to provide generic variables
for a pressure controller. It is meant to be extended by
the hardware IO variables into a specific structure. *)
//Controls
//Pressure setpoint
i_rPressSP : REAL;
//Pressure readback
i_rPressRB : REAL;
//Controller deadband
rDeadband : REAL;
//Actuator percentage open setpoint 0-100%
i_iPercOpenSP : INT;
//Acuator percentage open readback
q_iPercOpen : INT;
//Controller Mode
i_eCntlMode : E_CTRL_MODE;
//PID Parameters
stPIDParams : ST_PIDParams := (
fKp := 1,
fTn := 1,
fUpperLim := 1,
fLowerLim := -1,
fCtrlCycleTime := 0.01
);
END_STRUCT
END_TYPE
- Related:
ST_Proportionair
TYPE ST_Proportionair :
STRUCT
(* Control *)
{attribute 'pytmc' := '
pv: Enable
io: io
'}
xEnable : BOOL;
{attribute 'pytmc' := '
pv: Setpoint
io: io
'}
iSetpoint : INT;
{attribute 'pytmc' := '
pv: HighLimit
io: io
'}
iHiLimit : INT;
{attribute 'pytmc' := '
pv: LowLimit
io: io
'}
iLoLimit : INT;
(* Readback *)
xStable : BOOL;//TTL signal from the proportionair controller that the pressure is stable
{attribute 'pytmc' := '
pv: Pressure
io: i
'}
iPressure : INT;
{attribute 'TcDisplayScale' := '0-10'}
i_iPressRB AT %I* : INT;
{attribute 'TcDisplayScale' := '0-10'}
q_iPressCt AT %Q* : INT;
END_STRUCT
END_TYPE
ST_PropValveMKS253
TYPE ST_PropValveMKS253 EXTENDS ST_PressureController:
STRUCT
(* Prop valve structure, open is considered to be the higher axis position *)
(* Note: The wiring (if using the dongle from the ALI schematic is such that the
stepper driver must be inverted to make the increasing counter value correspond to a more open valve. *)
//EPICS Enable Switch
xEnable : BOOL;
//EPICS Enable Control;
xEnabled : BOOL;
//Valve interlock
xInterlock : BOOL;
//PropValve Control State Control
ePVCtrlStateReq : HMI_PropValveCtrlState;
//PropValve Control State Readback
ePVCtrlState : HMI_PropValveCtrlState;
//NC Axis Ref
stAxis : AXIS_REF;
//Physical limit switches
i_HLS AT %I* : BOOL; //Open
i_LLS AT %I* : BOOL; // Closed
//Open EPICS LS
xHLS : BOOL;
//Close EPICS LS
xLLS : BOOL;
//Top speed for the valve
lrMaxSpeed : LREAL := 100; //deg/sec
END_STRUCT
END_TYPE
ST_RegDeadbandControls
TYPE ST_RegDeadbandControls :
STRUCT
(* Deadband controls *)
iDeadband : INT;
iMaxSpeed : INT; // degrees per second
END_STRUCT
END_TYPE
ST_Regulator
TYPE ST_Regulator :
STRUCT
(* Regulator enable *)
xRegEnab : BOOL;
(* Regulator motor *)
stRegulatorMotor : AXIS_REF;
wPercentageOpen : WORD;
lrVelocity : LREAL :=360 ;
lrUpperLimPos : LREAL := 2887; //init to 2887, legacy hardcoded value
(* Deadband controls *)
stDeadband : ST_RegDeadbandControls;
(* Manual control *)
xManual : BOOL;
xPlus : BOOL;
xMinus : BOOL;
i_iMovePerc : INT;
(* Knob controls *)
i_iPercSP : UINT;
(* Pressure transducer *)
i_iRawInPress : INT;
wInPress : WORD;
i_iRawOutPress : INT;
wOutPress : WORD;
(* Pressure setpoint *)
wPressSP : WORD;
wPressSpHi : WORD := 2000;
xPressSpWarn : BOOL;
xSeeking : BOOL;
xRegHiLS AT %I* : BOOL;
xRegLoLS AT %I* : BOOL;
END_STRUCT
END_TYPE
- Related:
ST_SelectorM3
TYPE ST_SelectorM3 :
STRUCT
(* Selector EPICS controls *)
{attribute 'pytmc' := '
pv: ValvesLockRequest
io: io
'}
xResLock : BOOL; // request to lock the valves
{attribute 'pytmc' := '
pv: ValvesUnlockRequest
io: io
'}
xResUnlock : BOOL; // request to unlock the valves
{attribute 'pytmc' := '
pv: ValveSyncReqPos
io: io
'}
iSyncReqPos : INT; // requested position when synced
{attribute 'pytmc' := '
pv: Valve:01:ReqPos
io: io
'}
iVici1ReqPos : INT; // requested position for Vici 1
{attribute 'pytmc' := '
pv: Valve:02:ReqPos
io: io
'}
iVici2ReqPos : INT; // requested position for Vici 2
//Hardware control
astViciVlvCtrl : ARRAY[1..2] OF ST_ViciControl;
(* Selector EPICS Readbacks *)
{attribute 'pytmc' := '
pv: ValvesLocked
io: i
'}
xResLocked : BOOL; // valve movement is locked
{attribute 'pytmc' := '
pv: ValvesSynced
io: i
'}
xResSyncd : BOOL; // valves are on the same position
{attribute 'pytmc' := '
pv: ValveSyncCurrentPos
io: i
'}
iSyncResPos : INT;
//Hardware readbacks
{attribute 'pytmc' := '
pv: Valve
'}
astViciVlvStatus : ARRAY[1..2] OF ST_ViciStatus;
// M3 flow rates
{attribute 'pytmc' := '
pv: SampleFlow
io: i
'}
rLowFlow : REAL; //Sensirion Sample Flowrate
{attribute 'pytmc' := '
pv: LiquidSheathFlow
io: i
'}
rHighFlow : REAL; //Sensirion Sheath liquid flowrate
// Sample Names
{attribute 'pytmc' := '
pv: SampleNames
io: i
'}
stSampleNames : ST_Strings;
END_STRUCT
END_TYPE
- Related:
ST_SensirionFM
TYPE ST_SensirionFM :
STRUCT
stCtrl : ST_SensirionFMControl;
stStat : ST_SensirionFMStatus;
{attribute 'pytmc' := '
pv: Reset
io: o
'}
xFMReset : BOOL; // Reset on rising edge
{attribute 'pytmc' := '
pv: Flow
io: i
'}
rFlow : REAL; // uL/min
{attribute 'pytmc' := '
pv: OoR
io: i
'}
xFMOoR : BOOL; // Out of range
{attribute 'pytmc' := '
pv: FlowValid
io: i
'}
xFlowValid : BOOL; // driver completes successfully
{attribute 'pytmc' := '
pv: State
io: i
'}
eFMState : E_SensirionFMMode;
{attribute 'pytmc' := '
pv: Mode
io: o
'}
xFMModeReq : BOOL; //for sensirion calibration range (request)
{attribute 'pytmc' := '
pv: ModeRb
io: i
'}
xFMModeRb : BOOL; //for sensirion calibration range (readback)
END_STRUCT
END_TYPE
ST_SensirionFMControl
TYPE ST_SensirionFMControl :
STRUCT
xCalMode : BOOL; // calib mode request 0 = prec, 1 = extended
xReset : BOOL; // reset request
bAdr : BYTE; // device address
END_STRUCT
END_TYPE
ST_SensirionFMStatus
TYPE ST_SensirionFMStatus :
STRUCT
iFlowTicks : INT;
uiSensorOutput : UINT;
iState : INT;
rFlow : REAL; // units of uL/min
xOoR : BOOL; // Out of range
xFMMode : BOOL; // calib mode readback 0 = prec, 1 = extended
xFlowValid : BOOL; // driver completes successfully
abReply : ARRAY[1..500] OF BYTE;
bStatus : BYTE; //Sensor status
(* Bit 0: 0: Sensor idle
1: Sensor Busy
Bit 1: 0: Continuous Measurement disabled
1: Continuous Measurement enabled
Bit 2: (for Firmware . 1.3)
0: Auto detection Measurement disabled
1: Auto detection Measurement enabled
Bit 3: (for Firmware . 1.3)
0: No Auto Measurement since last read out Status
1: Auto Measurement finished since last read out Status, is set back to 0 after read out *)
END_STRUCT
END_TYPE
ST_SerialComm
// Generic serial comm necessities
TYPE ST_SerialComm :
STRUCT
RxBuffer : ComBuffer;
TxBuffer : ComBuffer;
stComIn AT %I* : EL6inData22B (*KL6inData22B*);
stComOut AT %Q* : EL6outData22B (*KL6outData22B*);
END_STRUCT
END_TYPE
ST_Shaker
TYPE ST_Shaker :
STRUCT
{attribute 'pytmc' := '
pv: PowerOn
io: i
'}
q_xPwrDO AT %Q* : BOOL;
{attribute 'pytmc' := '
pv: Ilk
io: i
'}
xIlk : BOOL;
i_xSwitch : BOOL;
{attribute 'pytmc' := '
pv: Ctrl
io: o
'}
i_xEpics : BOOL;
END_STRUCT
END_TYPE
ST_ShakerControls
TYPE ST_ShakerControls :
STRUCT
xShaker1On : BOOL;
xShaker2On : BOOL;
xShaker3On : BOOL;
xShaker4On : BOOL;
xShaker5On : BOOL;
xShaker6On : BOOL;
xShaker7On : BOOL;
xShaker8On : BOOL;
END_STRUCT
END_TYPE
ST_Strings
TYPE ST_Strings :
STRUCT
(* Twelve strings used to store sample names *)
{attribute 'pytmc' := '
pv: 01
io: i
'}
s1 : STRING;
{attribute 'pytmc' := '
pv: 02
io: i
'}
s2 : STRING;
{attribute 'pytmc' := '
pv: 03
io: i
'}
s3 : STRING;
{attribute 'pytmc' := '
pv: 04
io: i
'}
s4 : STRING;
{attribute 'pytmc' := '
pv: 05
io: i
'}
s5 : STRING;
{attribute 'pytmc' := '
pv: 06
io: i
'}
s6 : STRING;
{attribute 'pytmc' := '
pv: 07
io: i
'}
s7 : STRING;
{attribute 'pytmc' := '
pv: 08
io: i
'}
s8 : STRING;
{attribute 'pytmc' := '
pv: 09
io: i
'}
s9 : STRING;
{attribute 'pytmc' := '
pv: 10
io: i
'}
s10 : STRING;
{attribute 'pytmc' := '
pv: 11
io: i
'}
s11 : STRING;
{attribute 'pytmc' := '
pv: 12
io: i
'}
s12 : STRING;
END_STRUCT
END_TYPE
ST_TECControl
TYPE ST_TECControl :
STRUCT
sAddress : STRING(2);
diTempSetpoint : DINT;
xOutputOn : BOOL;
END_STRUCT
END_TYPE
ST_TECStatus
TYPE ST_TECStatus :
STRUCT
//Parameters
diTemp1 : DINT;
diSetPoint : DINT;
diPercentOut : DINT;
diCurrent : DINT;
xOutputOn : BOOL;
//Alarms
xHiTemp : BOOL;
xLoTemp : BOOL;
xCompCtrlAlrm : BOOL;
xOverCurrent : BOOL;
xOpenInput1 : BOOL;
xOpenInput2 : BOOL;
xDriverLowVoltage : BOOL;
//Interface Diagnosis
xStatusValid : BOOL;
sReply : STRING;
END_STRUCT
END_TYPE
ST_ViciControl
TYPE ST_ViciControl :
STRUCT
sViciReply : STRING;
iReqPos : INT;
xDirection : BOOL;
iAddress : INT;
END_STRUCT
END_TYPE
ST_ViciStatus
TYPE ST_ViciStatus :
STRUCT
sViciReq : STRING;
sViciReply :STRING;
{attribute 'pytmc' := '
pv: CurrentPos
io: i
'}
iCurrPos : INT;
iReqPos : INT;
xPosValid : BOOL;
(*iAddress :INT;*)
(* Do not use this address. If you do, the driver
will overwrite this address with zero each time
it runs, as the status is an input, not in_out *)
END_STRUCT
END_TYPE
GVLs
GVL
VAR_GLOBAL
g_xFirstPass: BOOL := TRUE;
g_xIOState : BOOL := FALSE;
g_aEcatMaster1 AT %I* : AMSNETID;
g_aEcatMaster2 AT %I* : AMSNETID;
END_VAR
GVL_Autosave
VAR_GLOBAL PERSISTENT
gp_stWaterRegDeadband : ST_RegDeadbandControls;
gp_stSheathRegDeadband : ST_RegDeadbandControls;
gp_stSelector : ST_Selector;
gp_stSelector2 : ST_Selector;
gp_stRegProp1 : ST_Proportionair;
gp_stRegProp2 : ST_Proportionair;
gp_stRegProp3 : ST_Proportionair;
gp_stRegProp4 : ST_Proportionair;
END_VAR
- Related:
GVL_ComBuffers
VAR_GLOBAL
SerialRXBuffer_SelVici : ComBuffer;
SerialTXBuffer_SelVici : ComBuffer;
SerialRXBuffer_SelFlwMtr : ComBuffer;
SerialTXBuffer_SelFlwMtr : ComBuffer;
SerialRXBuffer_Sel2FlwMtr : ComBuffer;
SerialTXBuffer_Sel2FlwMtr : ComBuffer;
SerialRXBuffer_Sel2Vici : ComBuffer;
SerialTXBuffer_Sel2Vici : ComBuffer;
SerialRXBuffer_CoolerShakerTEC : ComBuffer;
SerialTXBuffer_CoolerShakerTEC : ComBuffer;
END_VAR
GVL_Devices
VAR_GLOBAL
(* ALI - rarely used, stages need to be redone in new format *)
// stALI : ST_ALI;
//{attribute 'pytmc' := '
// pv: @(P):PCM:A
// '}
//ALI : FB_ALI;
{attribute 'pytmc' := '
pv: @(P):SEL:A
'}
{attribute 'TcLinkTo' := '.stSensSerial.stComIn.Status := TIIB[M3 Selector A Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 1^Status;
.stViciSerial.stComIn.Status := TIIB[M3 Selector A Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 2^Status;
.stSensSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector A Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stViciSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector A Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stShaker01.q_xPwrDO := TIIB[M3 Selector A Digital IO (EP2338-1001)]^Channel 9^Output;
.stShaker02.q_xPwrDO := TIIB[M3 Selector A Digital IO (EP2338-1001)]^Channel 10^Output;
.stShaker03.q_xPwrDO := TIIB[M3 Selector A Digital IO (EP2338-1001)]^Channel 11^Output;
.stShaker04.q_xPwrDO := TIIB[M3 Selector A Digital IO (EP2338-1001)]^Channel 12^Output;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^M3 Selector A (EP1111)^WcState^WcState;
'}
M3SelectorA : FB_SelectorM3;
{attribute 'pytmc' := '
pv: @(P):SEL:B
'}
{attribute 'TcLinkTo' := '.stSensSerial.stComIn.Status := TIIB[M3 Selector B Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 1^Status;
.stViciSerial.stComIn.Status := TIIB[M3 Selector B Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 2^Status;
.stSensSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector B Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stViciSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector B Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stShaker01.q_xPwrDO := TIIB[M3 Selector B Digital IO (EP2338-1001)]^Channel 9^Output;
.stShaker02.q_xPwrDO := TIIB[M3 Selector B Digital IO (EP2338-1001)]^Channel 10^Output;
.stShaker03.q_xPwrDO := TIIB[M3 Selector B Digital IO (EP2338-1001)]^Channel 11^Output;
.stShaker04.q_xPwrDO := TIIB[M3 Selector B Digital IO (EP2338-1001)]^Channel 12^Output;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^M3 Selector B (EP1111)^WcState^WcState
'}
M3SelectorB : FB_SelectorM3;
{attribute 'pytmc' := '
pv: @(P):SEL:C
'}
{attribute 'TcLinkTo' := '.stSensSerial.stComIn.Status := TIIB[M3 Selector C Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 1^Status;
.stViciSerial.stComIn.Status := TIIB[M3 Selector C Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 2^Status;
.stSensSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector C Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stViciSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector C Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stShaker01.q_xPwrDO := TIIB[M3 Selector C Digital IO (EP2338-1001)]^Channel 9^Output;
.stShaker02.q_xPwrDO := TIIB[M3 Selector C Digital IO (EP2338-1001)]^Channel 10^Output;
.stShaker03.q_xPwrDO := TIIB[M3 Selector C Digital IO (EP2338-1001)]^Channel 11^Output;
.stShaker04.q_xPwrDO := TIIB[M3 Selector C Digital IO (EP2338-1001)]^Channel 12^Output;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^M3 Selector C (EP1111)^WcState^WcState
'}
M3SelectorC : FB_SelectorM3;
{attribute 'pytmc' := '
pv: @(P):SEL:D
'}
{attribute 'TcLinkTo' := '.stSensSerial.stComIn.Status := TIIB[M3 Selector D Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 1^Status;
.stViciSerial.stComIn.Status := TIIB[M3 Selector D Serial IO (EP6002-0002)]^COM TxPDO-Map Inputs Channel 2^Status;
.stSensSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector D Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stViciSerial.stComOut.Ctrl := TIID^MAIN (EtherCAT)^M3 Selector D Serial IO (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stShaker01.q_xPwrDO := TIIB[M3 Selector D Digital IO (EP2338-1001)]^Channel 9^Output;
.stShaker02.q_xPwrDO := TIIB[M3 Selector D Digital IO (EP2338-1001)]^Channel 10^Output;
.stShaker03.q_xPwrDO := TIIB[M3 Selector D Digital IO (EP2338-1001)]^Channel 11^Output;
.stShaker04.q_xPwrDO := TIIB[M3 Selector D Digital IO (EP2338-1001)]^Channel 12^Output;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^M3 Selector D (EP1111)^WcState^WcState
'}
M3SelectorD : FB_SelectorM3;
{attribute 'pytmc' := '
pv: @(P):PCM:A
'}
{attribute 'TcLinkTo' := '.stPropAir1.i_iPressRB := TIID^MAIN (EtherCAT)^PCM A (EP4374-0002)^AI Inputs Channel 1^Value;
.stPropAir2.i_iPressRB := TIID^MAIN (EtherCAT)^PCM A (EP4374-0002)^AI Inputs Channel 2^Value;
.stPropAir1.q_iPressCt := TIID^MAIN (EtherCAT)^PCM A (EP4374-0002)^AO Outputs Channel 3^Analog output;
.stPropAir2.q_iPressCt := TIID^MAIN (EtherCAT)^PCM A (EP4374-0002)^AO Outputs Channel 4^Analog output;
.stSerial1.stComIn.Status := TIID^MAIN (EtherCAT)^PCM A (EP6002-0002)^COM TxPDO-Map Inputs Channel 1^Status;
.stSerial2.stComIn.Status := TIID^MAIN (EtherCAT)^PCM A (EP6002-0002)^COM TxPDO-Map Inputs Channel 2^Status;
.stSerial1.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM A (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stSerial2.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM A (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^PCM A (EP1111)^WcState^WcState;
'}
PCMA : FB_PressureControlModule;
{attribute 'pytmc' := '
pv: @(P):PCM:B
'}
{attribute 'TcLinkTo' := '.stPropAir1.i_iPressRB := TIID^MAIN (EtherCAT)^PCM B (EP4374-0002)^AI Inputs Channel 1^Value;
.stPropAir2.i_iPressRB := TIID^MAIN (EtherCAT)^PCM B (EP4374-0002)^AI Inputs Channel 2^Value;
.stPropAir1.q_iPressCt := TIID^MAIN (EtherCAT)^PCM B (EP4374-0002)^AO Outputs Channel 3^Analog output;
.stPropAir2.q_iPressCt := TIID^MAIN (EtherCAT)^PCM B (EP4374-0002)^AO Outputs Channel 4^Analog output;
.stSerial1.stComIn.Status := TIID^MAIN (EtherCAT)^PCM B (EP6002-0002)^COM TxPDO-Map Inputs Channel 1^Status;
.stSerial2.stComIn.Status := TIID^MAIN (EtherCAT)^PCM B (EP6002-0002)^COM TxPDO-Map Inputs Channel 2^Status;
.stSerial1.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM B (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stSerial2.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM B (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^PCM B (EP1111)^WcState^WcState;
'}
PCMB : FB_PressureControlModule;
{attribute 'pytmc' := '
pv: @(P):PCM:C
'}
{attribute 'TcLinkTo' := '.stPropAir1.i_iPressRB := TIID^MAIN (EtherCAT)^PCM C (EP4374-0002)^AI Inputs Channel 1^Value;
.stPropAir2.i_iPressRB := TIID^MAIN (EtherCAT)^PCM C (EP4374-0002)^AI Inputs Channel 2^Value;
.stPropAir1.q_iPressCt := TIID^MAIN (EtherCAT)^PCM C (EP4374-0002)^AO Outputs Channel 3^Analog output;
.stPropAir2.q_iPressCt := TIID^MAIN (EtherCAT)^PCM C (EP4374-0002)^AO Outputs Channel 4^Analog output;
.stSerial1.stComIn.Status := TIID^MAIN (EtherCAT)^PCM C (EP6002-0002)^COM TxPDO-Map Inputs Channel 1^Status;
.stSerial2.stComIn.Status := TIID^MAIN (EtherCAT)^PCM C (EP6002-0002)^COM TxPDO-Map Inputs Channel 2^Status;
.stSerial1.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM C (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stSerial2.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM C (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^PCM C (EP1111)^WcState^WcState;
'}
PCMC : FB_PressureControlModule;
{attribute 'pytmc' := '
pv: @(P):PCM:D
'}
{attribute 'TcLinkTo' := '.stPropAir1.i_iPressRB := TIID^MAIN (EtherCAT)^PCM D (EP4374-0002)^AI Inputs Channel 1^Value;
.stPropAir2.i_iPressRB := TIID^MAIN (EtherCAT)^PCM D (EP4374-0002)^AI Inputs Channel 2^Value;
.stPropAir1.q_iPressCt := TIID^MAIN (EtherCAT)^PCM D (EP4374-0002)^AO Outputs Channel 3^Analog output;
.stPropAir2.q_iPressCt := TIID^MAIN (EtherCAT)^PCM D (EP4374-0002)^AO Outputs Channel 4^Analog output;
.stSerial1.stComIn.Status := TIID^MAIN (EtherCAT)^PCM D (EP6002-0002)^COM TxPDO-Map Inputs Channel 1^Status;
.stSerial2.stComIn.Status := TIID^MAIN (EtherCAT)^PCM D (EP6002-0002)^COM TxPDO-Map Inputs Channel 2^Status;
.stSerial1.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM D (EP6002-0002)^COM RxPDO-Map Outputs Channel 1^Ctrl;
.stSerial2.stComOut.Ctrl := TIID^MAIN (EtherCAT)^PCM D (EP6002-0002)^COM RxPDO-Map Outputs Channel 2^Ctrl;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^PCM D (EP1111)^WcState^WcState;
'}
PCMD : FB_PressureControlModule;
{attribute 'pytmc' := '
pv: @(P):MAN
'}
{attribute 'TcLinkTo' := '.stValve[1].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 9^Output;
.stValve[1].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 1^Input;
.stValve[2].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 10^Output;
.stValve[2].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 2^Input;
.stValve[3].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 11^Output;
.stValve[3].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 3^Input;
.stValve[4].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 12^Output;
.stValve[4].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 4^Input;
.stValve[5].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 13^Output;
.stValve[5].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 5^Input;
.stValve[6].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 14^Output;
.stValve[6].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 6^Input;
.stValve[7].qxDO := TIIB[Gas_Mani_ValveCtrl (EP2338-0001)]^Channel 15^Output;
.stValve[7].ixOPN := TIIB[Gas_Mani_ValveRbk (EP2338-0001)]^Channel 7^Input;
.stBaseIO.i_SyncUnitWC := TIID^MAIN (EtherCAT)^Gas_Mani (EP1111)^WcState^WcState;
'}
GasMan : FB_GasManifold;
{attribute 'pytmc' := '
pv: @(P):MFM:A
'}
{attribute 'TcLinkTo' := '.stMFM.i_rFlow := TIID^MAIN (EtherCAT)^Bronkhorst MFM A^TxPDO Map^fMeasure;
.stMFM.q_rSetpoint := TIID^MAIN (EtherCAT)^Bronkhorst MFM A^RxPDO Map^fSetpoint;
'}
BronkhorstA : FB_BronkhorstMFM;
{attribute 'pytmc' := '
pv: @(P):MFM:B
'}
{attribute 'TcLinkTo' := '.stMFM.i_rFlow := TIID^MAIN (EtherCAT)^Bronkhorst MFM B^TxPDO Map^fMeasure;
.stMFM.q_rSetpoint := TIID^MAIN (EtherCAT)^Bronkhorst MFM B^RxPDO Map^fSetpoint;
'}
BronkhorstB : FB_BronkhorstMFM;
{attribute 'pytmc' := '
pv: @(P):MFM:C
'}
{attribute 'TcLinkTo' := '.stMFM.i_rFlow := TIID^MAIN (EtherCAT)^Bronkhorst MFM C^TxPDO Map^fMeasure;
.stMFM.q_rSetpoint := TIID^MAIN (EtherCAT)^Bronkhorst MFM C^RxPDO Map^fSetpoint;
'}
BronkhorstC : FB_BronkhorstMFM;
{attribute 'pytmc' := '
pv: @(P):MFM:D
'}
{attribute 'TcLinkTo' := '.stMFM.i_rFlow := TIID^MAIN (EtherCAT)^Bronkhorst MFM D^TxPDO Map^fMeasure;
.stMFM.q_rSetpoint := TIID^MAIN (EtherCAT)^Bronkhorst MFM D^RxPDO Map^fSetpoint;
'}
BronkhorstD : FB_BronkhorstMFM;
{attribute 'pytmc' := '
pv: @(P):SPR:A
'}
{attribute 'TcLinkTo' := '.q_xDO1 := TIIB[Solenoid Pair A Digital IO (EP2338-0001)]^Channel 9^Output;
.q_xDO2 := TIIB[Solenoid Pair A Digital IO (EP2338-0001)]^Channel 10^Output;
'}
SolenoidPairA : FB_SolenoidPair;
END_VAR
GVL_IO
{attribute 'qualified_only'}
VAR_GLOBAL
iq_stM2SelectorA : ST_M2SelectorIO;
iq_stM2SelectorB : ST_M2SelectorIO;
iq_stM3SelectorA : ST_M3SelectorIO;
iq_stM3SelectorB : ST_M3SelectorIO;
iq_stPressCtrlA : ST_M3PressCtrlIO;
iq_stPressCtrlB : ST_M3PressCtrlIO;
i_iFscEB1Ch0AI AT %I* : INT;
i_iFscEB1Ch1AI AT %I* : INT;
i_iFscEB1Ch2AI AT %I* : INT;
i_iFscEB1Ch3AI AT %I* : INT;
iq_stM3GasManifold : ST_M3GasManifoldIO;
END_VAR
GVL_misc
VAR_GLOBAL
(* Overall system controls *)
xN2Purge : BOOL;
g_xEstop : BOOL;
(* Shakers *)
st_Shakers : ST_ShakerControls;
xEnableRemoteControl : BOOL := TRUE;
xSystemPressurized : BOOL := TRUE;
gxRedLight : BOOL;
gxYellowLight : BOOL;
gxGreenLight : BOOL;
END_VAR
- Related:
POUs
F_AxisRef_To_MSTA
FUNCTION F_AxisRef_To_MSTA : UINT
VAR_INPUT
stManipAxis : ST_ManipAxis;
END_VAR
VAR_OUTPUT
MSTA : UINT;
END_VAR
VAR
END_VAR
(* This block translates the axis ref status to an EPICS psuedo MSTA value *)
(* A. Wallace, 17/8/17 *)
MSTA.1 := stManipAxis.stAxis.Status.Error;
MSTA.0 := stManipAxis.xEnabled;
END_FUNCTION
- Related:
F_rPressure
FUNCTION F_rPressure : WORD
VAR_INPUT
iRawVoltage : INT;
END_VAR
VAR
END_VAR
(* Transducer voltage to pressure *)
(* Add formula in here *)
F_rPressure := INT_TO_WORD((iRawVoltage*3000)/32767);
END_FUNCTION
FB_ALI
FUNCTION_BLOCK FB_ALI
VAR_IN_OUT
iq_Injector : ST_ALI;
END_VAR
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
fb925ALINB1 : FB_9XX;
fb925ALINB2 : FB_9XX;
fb722ALINB3 : FB_GCM;
fbManipX : FB_ManipAxis;
fbManipY : FB_ManipAxis;
fbManipZ : FB_ManipAxis;
fbPropValve : FB_PropValve;
ftHotPlug : F_TRIG;
END_VAR
//When the system is plugged in, WC goes to zero, so reset motion control and other stuff
ftHotPlug(CLK:=iq_Injector.i_SyncUnitWC);
(* Soft IO *)
iq_Injector.stNozzleBoxVG.iPRESS_R :=iq_Injector.i_EP3174_Ch1;
iq_Injector.stSkimmerVG.iPRESS_R := iq_Injector.i_EP3174_Ch2;
iq_Injector.stRelaxVG.iPRESS_R := iq_Injector.i_EP3174_Ch3;
(* Vacuum Gauge Supervisor *)
fb925ALINB1(VG:=iq_Injector.stNozzleBoxVG);
fb925ALINB2(VG:=iq_Injector.stSkimmerVG);
fb722ALINB3(VG:=iq_Injector.stRelaxVG);
(* Butterfly valve *)
iq_Injector.stPropVlv.xInterlock := TRUE;
iq_Injector.stPropVlv.i_rPressRB := iq_Injector.stSkimmerVG.rPRESS;
fbPropValve(stPropValve:=iq_Injector.stPropVlv, i_xExtIlk := TRUE);
(* Manipulator *)
fbManipX(iq_ManipAxis:=iq_Injector.axisX, i_xHotPlug:=ftHotPlug.Q);
fbManipY(iq_ManipAxis:=iq_Injector.axisY, i_xHotPlug:=ftHotPlug.Q);
fbManipZ(iq_ManipAxis:=iq_Injector.axisZ, i_xHotPlug:=ftHotPlug.Q);
F_AxisRef_To_MSTA(stManipAxis:=iq_Injector.axisX, MSTA=>iq_Injector.axisX.uiMsta);
F_AxisRef_To_MSTA(stManipAxis:=iq_Injector.axisY, MSTA=>iq_Injector.axisY.uiMsta);
F_AxisRef_To_MSTA(stManipAxis:=iq_Injector.axisZ, MSTA=>iq_Injector.axisZ.uiMsta);
END_FUNCTION_BLOCK
- Related:
FB_BronkhorstMFM
FUNCTION_BLOCK FB_BronkhorstMFM
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := '
pv:
'}
stMFM : ST_BhElFlow;
END_VAR
END_FUNCTION_BLOCK
- Related:
FB_EcatDiag
FUNCTION_BLOCK FB_EcatDiag
VAR_INPUT
i_AMSNetId : AMSNETID; //Connect this to an AMSNETID structure as a PLC input. Then link the input to the AMSNETID name in the master info.
i_xFirstPass : BOOL;
END_VAR
VAR_OUTPUT
q_xAllStatesGood : BOOL;
END_VAR
VAR
sNetId : T_AmsNetId;
astTermStates : ARRAY[1..256] OF ST_EcSlaveState;
fbGetAllSlaveStates : FB_EcGetAllSlaveStates;
ftReset : F_TRIG;
iterator: INT;
END_VAR
//Create the net ID string
sNetID := F_CreateAmsNetId(i_AMSNetId);
//Query the state of all terminals, collect in astTermStates
ftReset(CLK:=fbGetAllSlaveStates.bBusy OR i_xFirstPass);
fbGetAllSlaveStates.bExecute := ftReset.Q;
fbGetAllSlaveStates(sNetId:=sNetId, pStateBuf := ADR(astTermStates), cbBufLen:=SIZEOF(astTermStates));
//Keep checking...
//Cycle through each entry in the array and check if we have anyone not in OP and that the link state is good.
// If so, then set our global IO bad boolean.
IF fbGetAllSlaveStates.nSlaves > 0 THEN
q_xAllStatesGood := TRUE;
FOR iterator := 1 TO (fbGetAllSlaveStates.nSlaves -1) BY 1
DO
IF NOT( (astTermStates[iterator].deviceState = EC_DEVICE_STATE_OP) AND (astTermStates[iterator].linkState = EC_LINK_STATE_OK)) THEN
q_xAllStatesGood := FALSE;
END_IF
END_FOR
END_IF
END_FUNCTION_BLOCK
FB_GasManifold
FUNCTION_BLOCK FB_GasManifold
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: IO
'}
stBaseIO : ST_M3DeviceBaseIO;
{attribute 'pytmc' := '
pv: Valve
'}
stValve : ARRAY [1..MANIFOLD_VALVES] OF ST_ManifoldValve;
fbValve : ARRAY [1..MANIFOLD_VALVES] OF FB_ManiValve;
idx : int;
END_VAR
VAR CONSTANT
MANIFOLD_VALVES : INT := 7;
END_VAR
FOR idx := 1 to MANIFOLD_VALVES DO
stValve[idx].xILK := TRUE; // these valves have no interlock
fbValve[idx](iq_valve := stValve[idx]);
END_FOR
END_FUNCTION_BLOCK
FB_ManipAxis
FUNCTION_BLOCK FB_ManipAxis
VAR_INPUT
i_xHotPlug : BOOL;
END_VAR
VAR_OUTPUT
q_asResult: ARRAY [1..20] OF STRING;
q_xError : BOOL;
END_VAR
VAR_IN_OUT
iq_ManipAxis : ST_ManipAxis;
END_VAR
VAR
mcPower : MC_Power;
mcReset : MC_Reset;
mcMoveAbsolute : ARRAY[1..2] OF MC_MoveAbsolute;
mcHalt : MC_Halt;
mcSmoothMover : MC_SmoothMover;
//Error Stuff
indexResult: INT := 1;
fbFormatString : FB_FormatString;
xInitComplete : BOOL := FALSE;
imcBlockIndex: INT;
rtEnable : R_TRIG;
ftEnable : F_TRIG;
rtStop : R_TRIG;
rtStart : R_TRIG;
rReqAbsPosOld: REAL := 0;
rReqAbsPosPrevious: REAL;
iI: INT;
END_VAR
(* Manipulator Axis Function Block
Alex Wallace, 2016-8-8
This block works by jumping between two absolute move blocks and buffering
the motion between them.
*)
rtEnable(CLK:=iq_ManipAxis.xEnable);
ftEnable(CLK:=iq_ManipAxis.xEnable);
//Update status
iq_ManipAxis.stAxis();
IF rtEnable.Q OR ftEnable.Q THEN
xInitComplete := FALSE;
iq_ManipAxis.eState := Init;
ELSIF xInitComplete THEN
iq_ManipAxis.eState := MoveEnabled;
END_IF
//For EPICS, because the modbus map was made for 32 bit..
iq_ManipAxis.rActPos := LREAL_TO_REAL(iq_ManipAxis.stAxis.NcToPlc.ActPos);
CASE iq_ManipAxis.eState OF
0: iq_ManipAxis.eState := Init;
Init:
(* Start by initializing the motor axis *)
mcPower.Enable := iq_ManipAxis.xEnable;
iq_ManipAxis.rReqAbsPos := iq_ManipAxis.rActPos; //do this when the PLC comes up so we don't just lose the position if we tweak
IF mcPower.Status THEN //success
xInitComplete := TRUE;
q_asResult[indexResult] := 'Motor init success';
indexResult := indexResult + 1;
iq_ManipAxis.eState := MoveEnabled;
ELSIF mcPower.Error THEN
mcPower.Enable := FALSE;
fbFormatString(sFormat:='Init mcPower Error: %s', arg1:=F_UDINT(mcPower.ErrorID));
ActError();
END_IF
MoveEnabled:
mcSmoothMover.Enable := iq_ManipAxis.xEnable;
mcSmoothMover.ReqAbsPos := LIMIT(-100, REAL_TO_LREAL(iq_ManipAxis.rReqAbsPos), 100);
mcSmoothMover.Execute := iq_ManipAxis.xStart;
(*We've already captured the rising edge of start (if there was one, so
go ahead and reset it.*)
iq_ManipAxis.xStart := FALSE;
//Halt
mcHalt.Execute S= iq_ManipAxis.xStop;
mcHalt.Execute R= mcHalt.Busy;
iq_ManipAxis.xStop R= mcHalt.Busy;
IF mcPower.Error OR mcSmoothMover.Error THEN
iq_ManipAxis.eState:=Error;
END_IF
END_CASE
mcReset.Execute := rtEnable.Q OR i_xHotPlug OR iq_ManipAxis.xReset;
//Reset the reset controls for the next reset
IF mcReset.Done THEN
mcReset.Execute := FALSE;
iq_ManipAxis.xReset := FALSE;
iq_ManipAxis.eState:=Init;
END_IF
//Set function block state once axis is happy again
IF NOT iq_ManipAxis.stAxis.Status.Error THEN
q_xError := FALSE;
END_IF
(* Run the mc blocks *)
ActPower();
mcReset(Axis:=iq_ManipAxis.stAxis);
mcSmoothMover(Axis:=iq_ManipAxis.stAxis,
Velocity:=iq_ManipAxis.lrVelocity);
mcHalt(Axis:=iq_ManipAxis.stAxis);
iq_ManipAxis.xDmov := mcHalt.Done OR mcSmoothMover.Done;
iq_ManipAxis.xMovn := mcHalt.Busy OR mcSmoothMover.Busy;
//For display, this logic is not for safety
iq_ManipAxis.xHLS := NOT iq_ManipAxis.xHiLS;
iq_ManipAxis.xLLS := NOT iq_ManipAxis.xLoLS;
END_FUNCTION_BLOCK
ACTION ActPower:
END_ACTION
ACTION ActError:
(* Error action
*)
//Reset the debugging index
IF indexResult > 20 THEN indexResult := 1; END_IF
iq_ManipAxis.eState := Error;
q_xError := TRUE;
q_asResult[indexResult] := fbFormatString.sOut;
indexResult := indexResult +1;
END_ACTION
- Related:
FB_ManiValve
FUNCTION_BLOCK FB_ManiValve
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
iq_valve : ST_ManifoldValve;
END_VAR
VAR
END_VAR
(* Basic valve control block *)
IF NOT iq_valve.xILK THEN
iq_valve.xSW := FALSE;
END_IF
iq_valve.qxDO := NOT (iq_valve.xILK and iq_valve.xSW); // These are normally open valves, qxDO closes the valve
END_FUNCTION_BLOCK
- Related:
FB_PID_EXT
FUNCTION_BLOCK FB_PID_EXT
VAR_INPUT
i_lrSetpoint : LREAL;
i_lrActVal : LREAL;
i_stPIDParams : ST_PIDParams;
//Hold the output and pause the block
i_Hold : BOOL;
i_Reset : BOOL;
END_VAR
VAR_OUTPUT
q_lrCtrlOutput : LREAL;
q_Error : BOOL;
q_Limited : BOOL;
END_VAR
VAR
fbPID : FB_BasicPID;
eState : E_CTRL_MODE;
xInitialized : BOOL := FALSE;
lrSetpoint : LREAL;
lrActVal : LREAL;
bReset: BOOL;
lrCtrlOutput: LREAL;
xFirstPass: BOOL;
stDiag : ST_fbDiagnostics;
END_VAR
(*
NOTE: If the PID block is having issues check the cycle time, it might be wrong.
*)
IF fbPID.nErrorStatus <> 0 THEN
eState := E_CTRL_MODE.Error;
ELSIF fbPID.nErrorStatus = 0 AND xInitialized THEN
q_Error := FALSE;
IF i_Hold THEN
eState := E_CTRL_MODE.Hold;
ELSE
eState := E_CTRL_MODE.Run;
END_IF
END_IF
CASE eState OF
E_CTRL_MODE.Init:
//Complete 1 pass through this FB to check PID block for errors
IF xFirstPass THEN
xInitialized := FALSE;//pass
ELSE
xInitialized := TRUE;
END_IF
E_CTRL_MODE.Run:
lrSetpoint := i_lrSetpoint;
lrCtrlOutput := fbPID.fCtrlOutput;
lrActVal := i_lrActVal;
bReset := FALSE;
E_CTRL_MODE.Hold:
(* While holding, the setpoint value is set to the actual
value and the execution of the PID block is paused *)
lrSetpoint := lrActVal;
lrCtrlOutput := lrCtrlOutput;
bReset := TRUE;
E_CTRL_MODE.Error:
q_Error := TRUE;
lrSetpoint := 0;
lrCtrlOutput := 0;
END_CASE
fbPID(fSetpointValue := lrSetpoint,
fActualValue := lrActVal,
bReset := bReset,
fCtrlCycleTime :=i_stPIDParams.fCtrlCycleTime,
fKp := i_stPIDParams.fKp,
fTn := i_stPIDParams.fTn,
fTv := i_stPIDParams.fTv,
fTd := i_stPIDParams.fTd
);
//Hold everything until fb is initialized
IF xInitialized AND NOT q_Error THEN
//Indicate that the output is being limited
q_Limited := lrCtrlOutput > i_stPIDParams.fUpperLim OR lrCtrlOutput < i_stPIDParams.fLowerLim;
q_lrCtrlOutput := LIMIT(i_stPIDParams.fLowerLim, lrCtrlOutput, i_stPIDParams.fUpperLim);
ELSE
q_lrCtrlOutput := 0;
q_Limited := FALSE;
END_IF
xFirstPass := False;
END_FUNCTION_BLOCK
- Related:
FB_PressureControlModule
// Encapsulation of a pressure control module.
// A. Wallace, 2019-10-22
// Support for 2 proportionairs, 4 generic analog inputs (usually a pressure gauge
// and two shimadzu ports.
FUNCTION_BLOCK FB_PressureControlModule
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: IO
'}
stBaseIO : ST_M3DeviceBaseIO;
stSerial1 : ST_SerialComm;
stSerial2 : ST_SerialComm;
fbSerial1 : FB_SerialCommWrapper;
fbSerial2 : FB_SerialCommWrapper;
{attribute 'pytmc' := '
pv: PropAir:01
'}
stPropAir1 : ST_Proportionair;
{attribute 'pytmc' := '
pv: PropAir:02
'}
stPropAir2 : ST_Proportionair;
PropAir1 : FB_ProportionairRegulator;
PropAir2 : FB_ProportionairRegulator;
END_VAR
PropAir1(iq_stRegProp:=stPropAir1);
PropAir2(iq_stRegProp:=stPropAir2);
END_FUNCTION_BLOCK
ACTION SerialComm:
fbSerial1(stSerialComm := stSerial1);
fbSerial2(stSerialComm := stSerial2);
END_ACTION
FB_ProportionairRegulator
FUNCTION_BLOCK FB_ProportionairRegulator
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
iq_stRegProp : ST_Proportionair;
END_VAR
VAR
END_VAR
(* A. Wallace 2015-10-7
Proportionair regulator control *)
(* Pressure signal conversion, max pressure out 1000 (I think...) *)
iq_stRegProp.iPressure := REAL_TO_INT(1000 * (MAX(0,INT_TO_REAL(iq_stRegProp.i_iPressRB)) / 32767));
iq_stRegProp.iSetpoint := LIMIT(iq_stRegProp.iLoLimit, iq_stRegProp.iSetpoint, iq_stRegProp.iHiLimit);
iq_stRegProp.q_iPressCt := REAL_TO_INT( (INT_TO_REAL(iq_stRegProp.iSetpoint)/1000)*32767);
END_FUNCTION_BLOCK
- Related:
FB_PropValve
FUNCTION_BLOCK FB_PropValve
VAR_INPUT
i_xExtIlk : BOOL;
END_VAR
VAR_OUTPUT
q_xError : BOOL;
END_VAR
VAR_IN_OUT
stPropValve : ST_PropValveMKS253;
END_VAR
VAR
xInterlock : BOOL;
eState : E_PropValveFBState := E_PropValveFBState.Init;
stDiag : ST_fbDiagnostics;
mcPower : MC_Power;
mcJog : MC_Jog;
mcReset : MC_Reset;
mcSmoothMover : MC_SmoothMover;
tofLLSFilter : TOF := (PT:=T#100MS);
tofHLSFilter : TOF := (PT:=T#100MS);
fbPID : FB_PID_EXT;
AnyError: BOOL;
xReady : BOOL;
rtReset: R_TRIG;
UpperLimit : LREAL := 2000;
END_VAR
(* Provide the motor enable and limit switch functions for the MKS 253 valve *)
xInterlock := stPropValve.xInterlock AND i_xExtIlk;
stPropValve.xEnable R= NOT xInterlock;
rtReset(CLK:=stPropValve.xEnable);
(* The low limit switch was flakey, I added the filters *)
tofLLSFilter(IN:=stPropValve.i_LLS);
tofHLSFilter(IN:=stPropValve.i_HLS);
mcPower.Enable_Positive := tofHLSFilter.Q;
mcPower.Enable_Negative := tofLLSFilter.Q;
mcPower.Enable := xInterlock AND stPropValve.xEnable;
stPropValve.xEnabled := mcPower.Enable;
(* Move the valve *)
IF AnyError THEN
eState := E_PropValveFBState.Error;
stPropValve.ePVCtrlStateReq := HMI_PropValveCtrlState.Hold;
ELSIF xReady THEN
CASE stPropValve.ePVCtrlStateReq OF
HMI_PropValveCtrlState.Hold:
eState := E_PropValveFBState.Hold;
HMI_PropValveCtrlState.Manual:
eState := E_PropValveFBState.Manual;
HMI_PropValveCtrlState.Pressure:
eState := E_PropValveFBState.Pressure;
END_CASE
ELSE
eState := E_PropValveFBState.Init;
END_IF
CASE eState OF
E_PropValveFBState.Init:
//This state is reached at startup and after a reset
//It seeks the closed limit switch and moves to hold
stPropValve.ePVCtrlState := HMI_PropValveCtrlState.Hold;
fbPID.i_Reset := TRUE;
IF NOT tofLLSFilter.Q AND mcPower.Status THEN
stPropValve.ePVCtrlStateReq := HMI_PropValveCtrlState.Hold;
fbPID.i_Reset := TRUE;
xReady := TRUE;
END_IF
E_PropValveFBState.Hold:
stPropValve.ePVCtrlState := HMI_PropValveCtrlState.Hold;
mcJog.JogForward := FALSE;
mcJog.JogBackwards := FALSE;
fbPID.i_Hold := TRUE;
E_PropValveFBState.Pressure:
stPropValve.ePVCtrlState := HMI_PropValveCtrlState.Pressure;
fbPID.i_Hold := FALSE;
fbPID.i_Reset := FALSE;
//If the PID output is positive, jog forward, velocity scaled by the PID output
//likewise for a negative PID output
mcJog.JogForward := fbPID.q_lrCtrlOutput > 0;
mcJog.JogBackwards := fbPID.q_lrCtrlOutput < 0;
mcJog.Velocity := ABS(fbPID.q_lrCtrlOutput) * stPropValve.lrMaxSpeed;
E_PropValveFBState.Manual:
stPropValve.ePVCtrlState := HMI_PropValveCtrlState.Manual;
fbPID.i_Hold := TRUE;
mcSmoothMover.Enable := TRUE;
mcSmoothMover.ReqAbsPos := LIMIT(0, INT_TO_LREAL(stPropValve.i_iPercOpenSP), 100)/100*UpperLimit;
E_PropValveFBState.Error:
//Purgatory
mcJog.JogBackwards := FALSE;
mcJog.JogForward := FALSE;
fbPID.i_Hold := TRUE;
xReady := FALSE;
END_CASE
//Valve movement based on PID output
fbPID(i_lrSetpoint := REAL_TO_LREAL(stPropValve.i_rPressSP),
i_lrActVal := REAL_TO_LREAL(stPropValve.i_rPressRB),
i_stPIDParams := stPropValve.stPIDParams);
//MC blocks
mcPower(Axis:=stPropValve.stAxis);
mcReset(Axis:=stPropValve.stAxis);
mcJog(Axis:=stPropValve.stAxis, Mode:=E_JogMode.MC_JOGMODE_CONTINOUS);
mcSmoothMover(Axis:=stPropValve.stAxis,
Velocity:=stPropValve.lrMaxSpeed);
AnyError := mcJog.Error OR mcPower.Error OR fbPID.q_Error OR mcSmoothMover.Error;
(* EPICS display of perc open *)
stPropValve.q_iPercOpen := LREAL_TO_INT(100*stPropValve.stAxis.NcToPlc.ActPos / UpperLimit);
(* EPICS display of limits *)
stPropValve.xHLS := NOT tofHLSFilter.Q;
stPropValve.xLLS := NOT tofLLSFilter.Q;
END_FUNCTION_BLOCK
FB_Regulator
FUNCTION_BLOCK FB_Regulator
VAR_INPUT
iI: INT;
lrVelocity : LREAL := 360;
END_VAR
VAR_OUTPUT
q_asResult: ARRAY [1..20] OF STRING;
q_xError : BOOL;
END_VAR
VAR_IN_OUT
iq_stRegulator : ST_Regulator;
END_VAR
VAR
mcPower : MC_Power;
mcSetHome : MC_SetPosition;
mcReset : MC_Reset;
mcMoveAbsolute : ARRAY[1..2] OF MC_MoveAbsolute;
mcStatus : MC_ReadStatus;
mcHalt : MC_Halt;
iPressDiff: INT;
iTolerance: INT := 50;
xMoveOK: BOOL;
iStep: INT;
indexResult: INT := 1;
xInitComplete : BOOL := FALSE;
ftBackToAuto : F_TRIG;
iPercSPOld: UINT;
imcBlockIndex: INT;
imcBlockIndexPrev : INT;
rtEnable : R_TRIG;
END_VAR
(* Regulator Function Block *)
(* Alex Wallace, 2014-10-16 *)
(* Modified in 2.5.3 to remove all pressure SP based code *)
rtEnable(CLK:=iq_stRegulator.xRegEnab);
(* Regulator Pressure Calculation *)
iq_stRegulator.wInPress := LIMIT(0, F_rPressure(iRawVoltage := iq_stRegulator.i_iRawInPress), 10000);
iq_stRegulator.wOutPress := LIMIT(0, F_rPressure(iRawVoltage := iq_stRegulator.i_iRawOutPress), 10000);
(* Basic regulator interlocking *)
(* Check for regulator enable *)
xMoveOK := iq_stRegulator.xRegEnab;
IF NOT xMoveOK THEN
(*iq_stRegulator.wPressSP := 0; not preferred *)
iq_stRegulator.xRegEnab := FALSE;
(* add motor error here *)
END_IF
IF q_xError THEN
iStep := 9000;
ELSIF rtEnable.Q THEN
xInitComplete := FALSE;
iStep := 10;
ELSIF xInitComplete THEN
iStep := 3000;
END_IF
CASE iStep OF
0: iStep := 10;
10:
(* Start by initializing the motor axis *)
mcPower.Enable := TRUE;
iq_stRegulator.lrUpperLimPos := 2887;
IF mcPower.Status THEN
xInitComplete := TRUE;
q_asResult[indexResult] := 'Motor init success';
indexResult := indexResult + 1;
iStep := 3000;
ELSIF mcPower.Error THEN
mcPower.Enable := FALSE;
iStep := 9000;
q_xError := TRUE;
q_asResult[indexResult] := CONCAT('Initilizing regulator motor in step 10 failed with error code: ', UDINT_TO_STRING(mcPower.ErrorID));
indexResult := indexResult +1;
END_IF
//Knob controls
3000:
IF iq_stRegulator.i_iPercSP <> iPercSPOld AND xMoveOK THEN
mcMoveAbsolute[imcBlockIndex].Execute := FALSE;
imcBlockIndex := imcBlockIndex + 1;
IF imcBlockIndex >2 THEN imcBlockIndex := 1; END_IF
mcMoveAbsolute[imcBlockIndex].Position := LIMIT(0, UINT_TO_LREAL(iq_stRegulator.i_iPercSP) * iq_stRegulator.lrUpperLimPos/65535, MAX(iq_stRegulator.lrUpperLimPos,1));
mcMoveAbsolute[imcBlockIndex].Execute := TRUE;
iPercSPOld := iq_stRegulator.i_iPercSP;
ELSIF mcMoveAbsolute[imcBlockIndex].Done or mcMoveAbsolute[imcBlockIndex].CommandAborted or mcMoveAbsolute[imcBlockIndex].Busy or mcMoveAbsolute[imcBlockIndex].Error THEN
mcMoveAbsolute[imcBlockIndex].Execute := FALSE;
END_IF
9000:
(* Error *)
mcReset.Execute := iq_stRegulator.xRegEnab;
IF mcReset.Done THEN
q_xError := FALSE;
mcReset.Execute := FALSE;
iStep:=10;
END_IF
END_CASE
//Reset the debugging index
IF indexResult > 20 THEN indexResult := 1; END_IF
(* Regulator Percentage Open *)
(* Calculate the regulator percentage open, based on 6.5 turns to fully open *)
iq_stRegulator.wPercentageOpen := LREAL_TO_WORD( (iq_stRegulator.stRegulatorMotor.NcToPlc.ActPos / MAX(iq_stRegulator.lrUpperLimPos,1))*100);
(* Run the mc blocks *)
iq_stRegulator.stRegulatorMotor();
ActPower();
mcSetHome(Execute:=NOT(iq_stRegulator.xRegLoLS) AND iq_stRegulator.stRegulatorMotor.Status.StandStill, Axis:=iq_stRegulator.stRegulatorMotor, Position:=0, Mode:=FALSE);
IF NOT iq_stRegulator.xRegHiLS THEN iq_stRegulator.lrUpperLimPos := iq_stRegulator.stRegulatorMotor.NcToPlc.ActPos; END_IF
mcReset(Axis:=iq_stRegulator.stRegulatorMotor);
FOR iI := 1 TO 2 DO
mcMoveAbsolute[iI](Axis := iq_stRegulator.stRegulatorMotor, Velocity:=iq_stRegulator.lrVelocity, BufferMode:=MC_Aborting);
END_FOR
IF mcMoveAbsolute[1].Error OR mcMoveAbsolute[2].Error THEN
q_xError := TRUE;
mcMoveAbsolute[1].Execute := FALSE;
mcMoveAbsolute[2].Execute := FALSE;
q_asResult[indexResult] := 'Error occured with motion blocks';
indexResult := indexResult +1;
ELSIF mcPower.Error THEN
q_xError := TRUE;
q_asResult[indexResult] := concat('mcPower block error: ', UDINT_TO_STRING(mcPower.ErrorID));
END_IF
END_FUNCTION_BLOCK
ACTION ActPower:
END_ACTION
- Related:
FB_SelectorM3
FUNCTION_BLOCK FB_SelectorM3
VAR CONSTANT
END_VAR
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: IO
'}
stBaseIO : ST_M3DeviceBaseIO;
{attribute 'pytmc' := '
pv:
'}
stSelector : ST_SelectorM3;
// Shakers
{attribute 'pytmc' := '
pv: Shaker:01
'}
stShaker01 : ST_Shaker;
{attribute 'pytmc' := '
pv: Shaker:02
'}
stShaker02 : ST_Shaker;
{attribute 'pytmc' := '
pv: Shaker:03
'}
stShaker03 : ST_Shaker;
{attribute 'pytmc' := '
pv: Shaker:04
'}
stShaker04 : ST_Shaker;
// Vici valves
fbSelectorSync : FB_SelectorSync;
afbViciDriver : ARRAY [1..2] OF FB_ViciDriver;
fbViciSerial : FB_SerialCommWrapper;
stViciSerial : ST_SerialComm;
{attribute 'pytmc' := '
pv: Sensirion
'}
astSelFM : ARRAY[1..2] OF ST_SensirionFM; //Address 1 is low flow, address 2 is high flow, low flow is mapped to M2 flow
afbSensirionDriver : ARRAY [1..2] OF FB_SensirionDriver;
fbSensSerial : FB_SerialCommWrapper;
stSensSerial : ST_SerialComm;
stStatus: STRING;
udCount: UDINT;
tofReset: TOF;
iIdOld : INT;
viciIndex: INT := 1;
fmIndex: INT := 1;
//stComIn_EP6002P1 AT %I* : EL6inData22B (*KL6inData22B*);
//stComOut_EP6002P1 AT %Q* : EL6outData22B (*KL6outData22B*);
END_VAR
//Flowmeters
//////////////////////////////////////
Sensirion();
//Bronkhorst();
//Valve control
///////////////////////////////////
fbSelectorSync(iq_stSelector:=stSelector);
stSelector.astViciVlvCtrl[1].iReqPos := LIMIT(1, stSelector.iVici1ReqPos, 12);
stSelector.astViciVlvCtrl[2].iReqPos := LIMIT(1, stSelector.iVici2ReqPos, 12);
ViciDrivers();
//for epics readback to be consistent
IF stSelector.xResSyncd THEN stSelector.iSyncResPos := stSelector.astViciVlvStatus[1].iCurrPos; END_IF
(* Shakers *)
stShaker01.q_xPwrDO := stShaker01.i_xEpics;
stShaker02.q_xPwrDO := stShaker02.i_xEpics;
stShaker03.q_xPwrDO := stShaker03.i_xEpics;
stShaker04.q_xPwrDO := stShaker04.i_xEpics;
END_FUNCTION_BLOCK
ACTION Sensirion:
(* Sensirion Flowmeter driver encapsulation*)
(* M3 selector has two flow meters. Sample and sheath. Sample is "low flow."*)
astSelFM[fmIndex].stCtrl.xReset := astSelFM[fmIndex].xFMReset;
astSelFM[fmIndex].stCtrl.xCalMode := astSelFM[fmIndex].xFMModeReq;
astSelFM[fmIndex].stCtrl.bAdr := INT_TO_BYTE(fmIndex-1);
afbSensirionDriver[fmIndex](
i_xExecute:= TRUE,
i_tTimeOut:= t#1s,
iq_stSerialRXBuffer:= stSensSerial.RxBuffer,
iq_stSerialTXBuffer:= stSensSerial.TxBuffer,
q_stStatus=>astSelFM[fmIndex].stStat,
i_stControl:=astSelFM[fmIndex].stCtrl);
IF afbSensirionDriver[fmIndex].q_xDone OR
afbSensirionDriver[fmIndex].q_xError OR
afbSensirionDriver[fmIndex].q_xTimeout THEN
astSelFM[fmIndex].rFlow := astSelFM[fmIndex].stStat.rFlow;
astSelFM[fmIndex].xFMOoR := astSelFM[fmIndex].stStat.xOoR;
astSelFM[fmIndex].xFlowValid := astSelFM[fmIndex].stStat.xFlowValid;
astSelFM[fmIndex].eFMState := astSelFM[fmIndex].stStat.iState;
astSelFM[fmIndex].xFMModeRb := astSelFM[fmIndex].stStat.xFMMode;
//M3 flows
IF fmIndex = 1 THEN
stSelector.rLowFlow := astSelFM[fmIndex].stStat.rFlow;
ELSIF fmIndex = 2 THEN
stSelector.rHighFlow := astSelFM[fmIndex].stStat.rFlow;
END_IF
(* reset function for next time *)
afbSensirionDriver[fmIndex].Reset();
fmIndex := fmIndex + 1;
IF fmIndex > 1 THEN fmIndex := 1; END_IF
END_IF
END_ACTION
ACTION SerialComm:
fbSensSerial(stSerialComm := stSensSerial);
fbViciSerial(stSerialComm := stViciSerial);
END_ACTION
ACTION ViciDrivers:
stSelector.astViciVlvCtrl[viciIndex].iAddress := viciIndex;
afbViciDriver[viciIndex](
i_xExecute:= TRUE,
i_tTimeOut:= t#10s,
i_stControl:= stSelector.astViciVlvCtrl[viciIndex],
iq_stSerialRXBuffer:= stViciSerial.RxBuffer,
iq_stSerialTXBuffer:= stViciSerial.TxBuffer,
q_stStatus=>stSelector.astViciVlvStatus[viciIndex]);
IF afbViciDriver[viciIndex].q_xDone OR
afbViciDriver[viciIndex].q_xError OR
afbViciDriver[viciIndex].q_xTimeout THEN
(* reset function for next time *)
afbViciDriver[viciIndex](i_xExecute:=FALSE,
iq_stSerialRXBuffer:= stViciSerial.RxBuffer,
iq_stSerialTXBuffer:= stViciSerial.TxBuffer);
viciIndex := viciIndex + 1;
IF viciIndex > 2 THEN viciIndex := 1; END_IF
END_IF
END_ACTION
FB_SelectorSync
FUNCTION_BLOCK FB_SelectorSync
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
iq_stSelector : ST_SelectorM3;
END_VAR
VAR
tofResSyncd : TOF;
rtResLocked : R_TRIG;
rsResLocked : RS;
rtResLock : R_TRIG;
rtResUnlock : R_TRIG;
END_VAR
(* Selector valve sync FB for vici valves *)
(* Check if both valves are in the same position *)
(* Should probably add something to the driver to check if we are in motion*)
tofResSyncd.IN := (iq_stSelector.astViciVlvStatus[1].iCurrPos = iq_stSelector.astViciVlvStatus[2].iCurrPos ) AND
(iq_stSelector.astViciVlvStatus[1].xPosValid ) AND (iq_stSelector.astViciVlvStatus[1].xPosValid); (* Check for valid position rb *)
(* Delay the loss of ResSyncd so the valves can both be queried before overriding *)
(* Using a tof, but you can also check that both valves have been queried *)
tofResSyncd(PT:=T#10S);
iq_stSelector.xResSyncd := tofResSyncd.Q;
//Set, reset of valve lock
rtResLock(CLK:=iq_stSelector.xResLock);
rtResUnlock(CLK:=iq_stSelector.xResUnlock);
rsResLocked(set:=rtResLock.Q OR g_xFirstPass, reset1:=rtResUnlock.Q OR NOT iq_stSelector.xResSyncd);
iq_stSelector.xResLocked := rsResLocked.Q1;
(* If valve lock just became active, set the SyncReqPos to current locked position *)
rtResLocked(CLK:=iq_stSelector.xResLocked);
(*
IF rtResLocked.Q THEN
iq_stSelector.iSyncReqPos := iq_stSelector.iVici1ReqPos;
END_IF
*)
(* If valve lock active, match valve positions from the valve req that changes req number *)
(* This logic permits synchronized requests from EITHER valve in EPICS, ie. once sync and locked,
commanding either valve will move both. *)
IF iq_stSelector.xResLocked AND iq_stSelector.xResSyncd THEN
IF iq_stSelector.iVici1ReqPos <> iq_stSelector.iSyncReqPos THEN
iq_stSelector.iSyncReqPos := iq_stSelector.iVici1ReqPos;
iq_stSelector.iVici2ReqPos := iq_stSelector.iVici1ReqPos;
ELSIF iq_stSelector.iVici2ReqPos <> iq_stselector.iSyncReqPos THEN
iq_stSelector.iSyncReqPos := iq_stSelector.iVici2ReqPos;
iq_stSelector.iVici1ReqPos := iq_stSelector.iVici2ReqPos;
END_IF
END_IF
iq_stSelector.xResLock := FALSE;
iq_stSelector.xResUnlock := False;
END_FUNCTION_BLOCK
- Related:
FB_SensirionDriver
FUNCTION_BLOCK FB_SensirionDriver
VAR_INPUT
i_xExecute : BOOL := FALSE; (* rising edge execute *)
i_tTimeOut : TIME := T#10S; (* Maximum wait time for reply *)
i_stControl : ST_SensirionFMControl;
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_xError : BOOL;
q_xWarning : BOOL; (* set in the event of an unexpected reply *)
q_xTimeout : BOOL;
q_asResult : ARRAY[1..60] OF STRING(255);
q_stStatus : ST_SensirionFMStatus;
q_xInitComplete : BOOL;
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
//Usual stuff
rtExecute : R_TRIG;
rtReInit : R_TRIG;
iStep : INT;
iResultIndex : INT := 1;
aiSteps : ARRAY[1..256] OF INT;
iStepIndex : INT;
fbSensirionTransaction : FB_SensirionTransaction;
//Device Specific Working Variables
tonDelay : TON;
bLen : BYTE;
abTxData : ARRAY[1..256] OF BYTE;
xInitComplete : BOOL;
abRxData : ARRAY[1..256] OF BYTE;
iScale : INT;
iUnit : INT;
xType : BOOL;
xMode : BYTE;
xLinear : BOOL;
uiOffset : UINT;
iIdleAttempt : INT;
//Command delays
tonCommandDelay : TON;
xcatch : BOOL;
END_VAR
(* This function block performs serial communication with a Sensirion meter *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
rtReInit(CLK:= i_stControl.xReset);
IF rtReInit.Q THEN xInitComplete := FALSE; END_IF
IF rtExecute.Q THEN
q_xDone := FALSE;
q_xError := FALSE;
q_xWarning := FALSE;
q_xTimeout := FALSE;
q_asResult[iResultIndex]:= '';
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
IF xInitComplete THEN
iStep := 30;
(* Branch to reset device if reset bit is set here *)
IF rtReInit.Q OR (i_stControl.xCalMode <> q_stStatus.xFMMode) THEN
iStep := 400;
xInitComplete := FALSE;
q_stStatus.xFlowValid := FALSE;
q_asResult[iResultIndex] := 'Reinitializing';
iResultIndex := iResultIndex +1;
END_IF
ELSIF i_stControl.xReset THEN
iStep := 400;
q_stStatus.xFlowValid := FALSE;
q_asResult[iResultIndex] := 'Reinitializing';
iResultIndex := iResultIndex +1;
ELSE
iStep := 9;
END_IF
END_IF
CASE iStep OF
0: (* idle *)
;
9: //Get cal mode
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#43,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 9 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
//abRxdata := fbSensirionTransaction.q_baRxData;
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(xMode), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=1)) THEN
q_stStatus.xFMMode := BYTE_TO_BOOL(xMode);
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
q_asResult[iResultIndex] := CONCAT('Step 9 complete. Cal mode is set to: ', BYTE_TO_STRING(xMode));
iResultIndex := iResultIndex +1;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 9 failed';
iResultIndex := iResultIndex +1;
END_IF
iStep := 10;
END_IF
10: // Set calibration
//calibration of 0 for precision mode, 250 nL/min - 5000 nL/min
// calibration of 1 for extended mode, 2000 nL/min - 20000 nL/min
IF q_stStatus.xFMMode <> i_stControl.xCalMode THEN
IF i_stControl.xCalMode THEN abTxData[1]:=1; ELSE abTxData[1]:=0; END_IF
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 1,
i_bCmd:= 16#43,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 10 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#1S);
IF tonCommandDelay.Q THEN
(* No response, completed transaction confirmed by a response*)
q_asResult[iResultIndex] := 'Step 10 complete';
iResultIndex := iResultIndex +1;
iStep := 11;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
END_IF
ELSE
q_asResult[iResultIndex] := 'Calmode already set';
iResultIndex := iResultIndex +1;
iStep := 11;
END_IF
11: //Get cal mode
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#43,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 11 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 11 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(xMode), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=1)) THEN
q_stStatus.xFMMode := BYTE_TO_BOOL(xMode);
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
q_asResult[iResultIndex] := 'Step 11 complete';
iResultIndex := iResultIndex +1;
iStep := 12;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 9 failed';
iResultIndex := iResultIndex +1;
END_IF
END_IF
END_IF
12: //Get scale factor
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#53,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 12 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 12 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(iScale), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=2)) THEN //always 2 for the 16 bit wide integer
iScale := WORD_TO_INT(HOST_TO_BE16(INT_TO_WORD(iScale)));
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
q_asResult[iResultIndex] := 'Step 12 complete';
iResultIndex := iResultIndex +1;
iStep := 13;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 12 failed';
iResultIndex := iResultIndex +1;
END_IF
END_IF
END_IF
13: //Get flow unit
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#52,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 13 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 13 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(iUnit), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=2)) THEN //always 2 for the 16 bit wide integer
iUnit := WORD_TO_INT(HOST_TO_BE16(INT_TO_WORD(iUnit)));
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
q_asResult[iResultIndex] := 'Step 13 complete';
iResultIndex := iResultIndex +1;
iStep := 14;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 13 failed';
iResultIndex := iResultIndex +1;
END_IF
END_IF
END_IF
14: //Get measurement type
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#55,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 14 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 14 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
xType := BYTE_TO_BOOL(fbSensirionTransaction.q_baRxData[1]);
q_asResult[iResultIndex] := 'Step 14 complete';
iResultIndex := iResultIndex +1;
iStep := 16;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
END_IF
15: //Get offset
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#56,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 15 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 15 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(uiOffset), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=2)) THEN //always 2 for the 16 bit wide integer
uiOffset := WORD_TO_UINT(HOST_TO_BE16(UINT_TO_WORD(uiOffset)));
A_ClearTransaction(); (* reset *)
q_asResult[iResultIndex] := CONCAT('Step 15 complete, offset is: ', UINT_TO_STRING(uiOffset));
iResultIndex := iResultIndex +1;
iStep := 16;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 15 failed';
iResultIndex := iResultIndex +1;
END_IF
A_ClearTransaction(); (* reset *)
END_IF
END_IF
16: //Get linear mode
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#45,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 16 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_bState <> 0 THEN
q_asResult[iResultIndex] := CONCAT('in Step 16 serial transaction failed with error state: ', BYTE_TO_STRING(fbSensirionTransaction.q_bState));
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#100MS);
IF tonCommandDelay.Q THEN
xLinear := BYTE_TO_BOOL(fbSensirionTransaction.q_baRxData[1]);
A_ClearTransaction(); (* reset *)
q_asResult[iResultIndex] := CONCAT('Step 16 complete, linear is: ', BOOL_TO_STRING(xLinear));
iResultIndex := iResultIndex +1;
iStep := 20;
END_IF
END_IF
//////////////////////////////////////////////////////////////////
// Reset sequence
//////////////////////////////////////////////////////////////////
(*
1. Issue a stop command to halt continuous measurement
2. Reset command
3. Reset initcomplete bit, and xReset
4. Move to done state
*)
400: //Stop measurement
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#34,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 400 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#250MS);
IF tonCommandDelay.Q THEN
q_asResult[iResultIndex] := 'Measurement stop command sent';
iResultIndex := iResultIndex +1;
iStep:=405;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
END_IF
405: //Check sensor status
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#30,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 405 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
(* The status measurement is available at the rx data here *)
IF fbSensirionTransaction.q_iRxLen > 0 THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(q_stStatus.bStatus), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=1)) THEN
IF q_stStatus.bStatus.0 = 0 THEN //lsb of bStatus is idle
iStep :=410;
q_asResult[iResultIndex] := 'Sensor is idle.';
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
ELSE
iStep := 400;
iIdleAttempt := iIdleAttempt + 1;
q_asResult[iResultIndex +1] := CONCAT('Failed to move to idle state in step 405, attempt ' , CONCAT(INT_TO_STRING(iIdleAttempt) , ' of 3'));
IF iIdleAttempt = 3 THEN iStep := 9000; iIdleAttempt := 0; END_IF
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 405 failed';
iResultIndex := iResultIndex +1;
END_IF
ELSE
q_asResult[iResultIndex] := 'No status data returned';
iResultIndex := iResultIndex +1;
iStep := 9000;
END_IF
END_IF
410: //Reset device
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#65,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 400 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#200MS);
IF tonCommandDelay.Q THEN
q_asResult[iResultIndex] := 'Device reset command sent';
iResultIndex := iResultIndex +1;
iStep:=8000;
xInitComplete := FALSE;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
END_IF
20: //Start continuous measurement
abTxData[1]:=0;
abTxData[2]:=0;
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 2,
i_bCmd:= 16#33,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 20 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
tonCommandDelay(IN:=fbSensirionTransaction.q_xDone, PT:=T#250MS);
IF tonCommandDelay.Q THEN
(* No response, completed transaction confirmed by a response*)
iStep := 21;
q_asResult[iResultIndex] := CONCAT('Sent start measurement command', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_IF
END_IF
21: //Check sensor status
fbSensirionTransaction(
i_xExecute:= TRUE,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 0,
i_bCmd:= 16#30,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 21 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
(* The status measurement is available at the rx data here *)
IF fbSensirionTransaction.q_iRxLen > 0 THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(q_stStatus.bStatus), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=1)) THEN
IF q_stStatus.bStatus.1 = 1 THEN
iStep :=30;
xInitComplete := TRUE;
q_asResult[iResultIndex] := 'Init success, sensor is measuring.';
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
ELSIF q_stStatus.bStatus.0 = 1 THEN
iStep :=21; // if the sensor is busy, and not yet measuring, perhaps we try again
q_asResult[iResultIndex] := 'Sensor is busy...';
iResultIndex := iResultIndex +1;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
ELSE
iStep := 9000;
q_asResult[iResultIndex +1] := 'Failed to start cont. measurement in step 21';
END_IF
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 21 failed';
iResultIndex := iResultIndex +1;
END_IF
ELSE
q_asResult[iResultIndex] := 'No status data returned in step 21';
iResultIndex := iResultIndex +1;
iStep := 9000;
END_IF
END_IF
30: //Get last measurement
tonDelay.IN:=TRUE;
abTxData[1]:=0;
fbSensirionTransaction(
i_xExecute:= tonDelay.Q,
i_bAdr:= i_stControl.bAdr,
i_bLen:= 1,
i_bCmd:= 16#35,
i_abTxData := abTxData,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer
);
IF fbSensirionTransaction.q_xError THEN
q_asResult[iResultIndex] := CONCAT('in Step 30 serial transaction failed with message: ', fbSensirionTransaction.q_sResult);
iResultIndex := iResultIndex +1;
iStep := 9000;
ELSIF fbSensirionTransaction.q_xDone THEN
(* The flow measurement is available at the rx data here *)
IF fbSensirionTransaction.q_iRxLen > 0 THEN
IF UDINT_TO_BOOL(MEMCPY(destAddr:=ADR(q_stStatus.uiSensorOutput), srcAddr:=ADR(fbSensirionTransaction.q_baRxData), n:=2)) THEN //always 2 for the 16 bit wide integer
q_stStatus.uiSensorOutput := WORD_TO_UINT(HOST_TO_BE16(UINT_TO_WORD(q_stStatus.uiSensorOutput))); //memcpy effs up an produces a little endian number, sensirion transmits be
IF (q_stStatus.uiSensorOutput AND 32768) = 32768 THEN
q_stStatus.iFlowTicks := UINT_TO_INT(-1*((q_stStatus.uiSensorOutput XOR 65535) + 1));
ELSE
q_stStatus.iFlowTicks := UINT_TO_INT(q_stStatus.uiSensorOutput);
END_IF
//Conversion from int to real value using the unit
IF ((INT_TO_WORD(iUnit) AND 16#000F)=16#0003) OR ((INT_TO_WORD(iUnit) AND 16#000F)=16#0004) THEN //lsb of iUnit is magnitude, should only be one thing
q_stStatus.rFlow := (INT_TO_REAL(q_stStatus.iFlowTicks)/INT_TO_REAL(iScale))*EXPT(10,-3); //converting from nL to uL
(* Check flow readback against calibration range for OoR *)
//calibration of 0 for precision mode, 250 nL/min - 5000 nL/min
// calibration of 1 for extended mode, 2000 nL/min - 20000 nL/min
IF (q_stStatus.xFMMode =0 ) THEN
IF (ABS(q_stStatus.rFlow) < (250E-3)) THEN
q_stStatus.iState := 2; //<OoR
ELSIF (ABS(q_stStatus.rFlow) > (5000E-3)) THEN
q_stStatus.iState := 1; //OoR>
ELSE
q_stStatus.iState := 0; //OK
END_IF
ELSIF (q_stStatus.xFMMode =1 ) THEN
IF (ABS(q_stStatus.rFlow) < (2000E-3)) THEN
q_stStatus.iState := 2; //<OoR Add to xOoR variable
ELSIF (ABS(q_stStatus.rFlow) > (20000E-3)) THEN
q_stStatus.iState := 1; //OoR>
ELSE
q_stStatus.iState := 0; // OK
END_IF
END_IF
q_stStatus.xFlowValid := (q_stStatus.iState = 0); // Flow valid if OK
// Limit the measured flowrate based on the calmode
IF q_stStatus.xFMMode THEN
q_stStatus.rFlow := LIMIT(-20000E-3, q_stStatus.rFlow, 20000E-3);
ELSE
q_stStatus.rFlow := LIMIT(-5000E-3, q_stStatus.rFlow, 5000E-3);
END_IF
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'iUnit not recognized in step 30';
iResultIndex := iResultIndex +1;
END_IF
(* Stuff worked, go to 8000 *)
iStep := 8000;
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
tonDelay.IN := FALSE;
ELSE
iStep := 9000;
q_asResult[iResultIndex] := 'Memcpy in step 30 failed';
iResultIndex := iResultIndex +1;
END_IF
ELSE
q_asResult[iResultIndex] := 'No flow data returned';
iResultIndex := iResultIndex +1;
iStep := 9000;
END_IF
END_IF
8000: (* done *)
q_xDone := TRUE;
IF q_asResult[iResultIndex] = '' THEN
q_asResult[iResultIndex] := 'Success';
iResultIndex := iResultIndex +1;
END_IF
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
iStep := 0;
END_IF
9000:
q_xTimeout := fbSensirionTransaction.q_xTimeout;
q_xError := TRUE;
// If we're having issues communicating we don't want to be left thinking there's a flow... even if we know it's not valid.
q_stStatus.xFlowValid := FALSE;
q_stStatus.iState := 3; // Flow invalid
q_stStatus.rFlow := 0;
END_CASE
tonDelay(PT:=T#500MS);
//q_stStatus.rFlow := INT_TO_REAL(iFlow);
iStepIndex := iStepIndex +1;
aiSteps[iStepIndex]:=iStep;
q_xInitComplete := xInitComplete;
IF iStepIndex >256 THEN iStepIndex:=1; END_IF
IF iResultIndex >60 THEN iResultIndex:=1; END_IF
END_FUNCTION_BLOCK
ACTION A_ClearTransaction:
fbSensirionTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
END_ACTION
ACTION Get:
END_ACTION
METHOD Reset
VAR_INPUT
END_VAR
// If this works....
THIS^(i_xExecute := False,
iq_stSerialRXBuffer := THIS^.iq_stSerialRXBuffer,
iq_stSerialTXBuffer := THIS^.iq_stSerialTXBuffer,);
END_METHOD
FB_SensirionTransaction
FUNCTION_BLOCK FB_SensirionTransaction
VAR CONSTANT
c_ArraySize : INT := 600;
c_DebugArraySize : INT:=40;
END_VAR
VAR_INPUT
i_xExecute : BOOL; (* Rising edge execute *)
i_bCmd : BYTE; (* Command parameter *)
i_bAdr : BYTE; //Address parameter
i_bLen : BYTE; //Length of transmission
i_abTxData : ARRAY[1..256] OF BYTE; //Transmission
i_tTimeOut : TIME := T#10S; (* Maximum wait time for reply *)
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_baRxData : ARRAY[1..c_ArraySize] OF BYTE; //Rx data from the frame, parse this in the driver
q_iRxLen : INT;
q_xError : BOOL;
q_xTimeout : BOOL;
q_sResult : STRING(255);
q_abLastTxData : ARRAY[1..c_DebugArraySize,1..c_ArraySize] OF BYTE; (* Last byte stream sent to serial device - for debugging *)
q_abLastRxData : ARRAY[1..c_DebugArraySize,1..c_ArraySize] OF BYTE; (* Last byte stream received from serial device - for debugging *)
q_bState : BYTE;
q_baRxUnit : ARRAY[1..c_ArraySize] OF BYTE;
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
//Usual stuff
rtExecute : R_TRIG;
iStep : INT;
fbClearComBuffer : ClearComBuffer;
fbSendData : SendData;
udiTxLen : UDINT;
udiRxLen : UDINT;
fbReceiveData : ReceiveData;
baRxData : ARRAY[1..c_ArraySize] OF BYTE;
baTxData : ARRAY[1..c_ArraySize] OF BYTE;
tonTimeout : TON;
iDataTx: INT := 1;
iDataRx: INT := 1;
//Device specific
iSeek : INT :=1 ;
bDelimiter : BYTE:=16#7E;
bRxLen : BYTE;
fbShiftByteArray : FB_ShiftByteArray;
iFrameWidth : INT;
bCatch : BYTE;
xCatch : BOOL;
END_VAR
(* This function block performs serial transactions for the SHDCLC (Sensirion) Interface *)
(* Note the SHDLC transaction uses bytes and not strings *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
IF rtExecute.Q THEN
q_xDone := FALSE;
//Clear the Tx and Rx buffer
MEMSET(destAddr:=ADR(baRxData), fillByte:=16#00, n:=SIZEOF(baRxData));
MEMSET(destAddr:=ADR(baTxData), fillByte:=16#00, n:=SIZEOF(baTxData));
q_xError := FALSE;
q_xTimeout := FALSE;
q_sResult:= '';
//MEMSET(destAddr:=ADR(q_abLastTxData[iDataTx,1]), fillByte:=16#00, n:=c_ArraySize);
//MEMSET(destAddr:=ADR(q_abLastTxData[iDataRx,1]), fillByte:=16#00, n:=c_ArraySize);
(* clear com buffers *)
fbClearComBuffer(Buffer:= iq_stSerialRXBuffer);
fbClearComBuffer(Buffer:= iq_stSerialTXBuffer);
(* Reset receive *)
fbReceiveData(
Reset:= TRUE,
RXbuffer:= iq_stSerialRXBuffer );
iStep := 10;
END_IF
CASE iStep OF
0:
; (* idle *)
10:
(* Build the byte array *)
//Every message starts with 0x7e, but we don't add that here.
baTxData[1]:=16#00;
//Address, command and message length
baTxData[2]:=i_bAdr;
baTxData[3]:=i_bCmd;
baTxData[4]:=i_bLen;
//Add the message
IF i_bLen > 0 THEN
MEMCPY(destAddr:=ADR(baTxData[5]), srcAddr:=ADR(i_abTxData), n:=i_bLen);
END_IF
//Add the checksum
baTxData[5+i_bLen]:=fSHDLCChecksum(i_bAdr:=i_bAdr, i_bCmd:=i_bCmd, i_bLen:=i_bLen, i_pbaTxData:=ADR(i_abTxData));
//Calc the framewidth, a useful number
// start + adr + cmd + len + tx data (len) + chk + stop
udiTxLen:=(4+BYTE_TO_UDINT(i_bLen)+1);
(* Byte stuffing
If any special bytes are found in the message, they must be replaced with a special pair of bytes *)
(* Seek through the byte array for any of the special bytes
0x7e
0x7d
0x11
0x13 *)
FOR iSeek:=2 TO UDINT_TO_INT(udiTxLen) DO
IF (baTxData[iSeek]=16#7E) OR (baTxData[iSeek]=16#7D) OR (baTxData[iSeek]=16#11) OR (baTxData[iSeek]=16#13) THEN
(* When a special byte is found, move the remaining message to the right by one byte *)
fbShiftByteArray(ptArray:=ADR(baTxData), iSize:=256, iCurrIndex:=iSeek, iMessageLen:=(5+BYTE_TO_INT(i_bLen)+1), iShift:=1);
IF fbShiftByteArray.q_xError THEN
q_sResult:='Transaction failed in step 10 while making room for the stuffing';
iStep := 9000;
RETURN; //this way we catch what happened.
ELSE
//Stuff in the byte
baTxData[iSeek]:=16#7D;
//Change the original byte, which now resides at iSeek+1, to invert the fifth bit
baTxData[iSeek+1]:=baTxData[iSeek+1] XOR 16#10;
//Update the framewidth to be +1
udiTxLen := udiTxLen + 1;
END_IF
END_IF
END_FOR
//If we made it this far, we're done with building the message, and stuffing it, frame it
baTxData[1]:=bDelimiter;
//Add one to the frame width
udiTxLen := udiTxLen +1;
baTxData[udiTxLen]:=bDelimiter;
fbSendData(TXbuffer:= iq_stSerialTXBuffer,pSendData:=ADR(baTxData), Length:=udiTxLen);
//For debugging:
MEMCPY(destAddr:=ADR(q_abLastTxData[iDataTx,1]), srcAddr:=ADR(baTxData), n:=INT_TO_UDINT(c_ArraySize));
IF iDataTx = c_DebugArraySize THEN iDataTx := 1; ELSE iDataTx := iDataTx +1; END_IF
if i_bCmd = bCatch THEN xCatch := TRUE; END_IF
iStep := iStep + 10;
20: (* Finish sending the data *)
IF fbSendData.Busy THEN
fbSendData(TXbuffer:= iq_stSerialTXBuffer,pSendData:=ADR(baTxData), Length:=udiTxLen);
ELSIF fbSendData.Error <> 0 THEN
q_sResult := CONCAT('In step 20 fbSendData resulted in error: ', INT_TO_STRING(fbSendData.Error));
iStep := 9000;
ELSIF NOT fbSendData.Busy THEN
iStep:=iStep + 10;
END_IF
tonTimeout(IN:= FALSE);
30: (* Get reply *)
fbReceiveData(
pPrefix:=ADR(bDelimiter),
LenPrefix:=1,
pSuffix:=ADR(bDelimiter),
LenSuffix:=1,
pReceiveData:=ADR(baRxData),
SizeReceiveData:=SIZEOF(baRxData),
Timeout:= i_tTimeOut,
Reset:= FALSE,
RXbuffer:= iq_stSerialRXBuffer );
tonTimeout(IN:= TRUE, PT:= i_tTimeOut);
IF fbReceiveData.Error <> 0 THEN
q_sResult := CONCAT('In step 30 fbReceiveData resulted in error: ', INT_TO_STRING(fbReceiveData.Error));
iStep := 9000;
ELSIF fbReceiveData.RxTimeout OR tonTimeout.Q THEN
q_sResult := 'Serial device failed to reply within timeout period';
q_xTimeout := TRUE;
iStep := 9000;
ELSIF fbReceiveData.DataReceived THEN
udiRxLen := fbReceiveData.LenReceiveData;
// For troubleshooting, use this to capture a specific command message reply
IF udiRxLen > 0 THEN
//Remove the stuffing
FOR iSeek:=1 TO UDINT_TO_INT(udiRxLen) DO
IF baRxData[iSeek]=16#7D THEN
fbShiftByteArray(ptArray:=ADR(baRxData), iSize:=256, iCurrIndex:=iSeek, iMessageLen:=UDINT_TO_INT(udiRxLen), iShift:=-1);
IF fbShiftByteArray.q_xError THEN
q_sResult:='Transaction failed in step 30, while removing the stuffing Hans dropped some on the floor, oh Hans!';
iStep:=9000;
RETURN; // this way we catch the problem
ELSE
//iSeek now points to the escaped byte, once again invert the fifth bit to recover
baRxData[iSeek]:=baRxData[iSeek] XOR 16#10;
//Reduce the message length by one byte
udiRxLen := udiRxLen - 1;
END_IF
END_IF
END_FOR
//Byte stuffing removed and cleared at this point
//Could check the checksum here...
//naaa
//Determine the Rx data length
bRxLen := baRxData[5];
IF i_bCmd = bCatch and bRxLen > 0 THEN
MEMCPY(destAddr:=ADR(q_baRxUnit), srcAddr:=ADR(baRxData), n:=udiRxLen);
END_IF
//Snag the state byte while we're here
q_bState := baRxData[4];
q_iRxLen := BYTE_TO_INT(bRxLen);
//Memcpy ze' data, check if memcpy is successful (returns other than zero) or bRxLen is zero (which causes memcpy to return zero)
IF UDINT_TO_BOOL(MEMCPY(srcAddr:=(ADR(baRxData)+5), destAddr:=ADR(q_baRxData), n:=BYTE_TO_UINT(bRxLen))) OR (bRxLen=0) THEN
q_sResult := 'Success';
iStep:=100;
//For debugging
//MEMCPY(destAddr:=ADR(q_abLastRxData[iDataRx,1]), srcAddr:=ADR(baRxData), n:=c_ArraySize);
//IF iDataRx = c_DebugArraySize THEN iDataRx := 1; ELSE iDataRx := iDataRx +1; END_IF
ELSE
q_sResult := 'Memcpy step failed, expected some bytes but was NOT able to export them via memcpy, investigate transaction fb';
iStep := 9000;
END_IF
END_IF
END_IF
100: (* done *)
q_xDone:=TRUE;
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
q_bState:=16#00;
iStep := 0;
END_IF
9000:
q_xError := TRUE;
END_CASE
END_FUNCTION_BLOCK
- Related:
FB_SerialCommWrapper
FUNCTION_BLOCK FB_SerialCommWrapper
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_Out
stSerialComm : ST_SerialComm;
END_VAR
VAR
fbSerialLineControl : SerialLineControl;
END_VAR
fbSerialLineControl(
Mode:= SERIALLINEMODE_EL6_22B (*SERIALLINEMODE_KL6_22B_STANDARD*),
pComIn:= ADR(stSerialComm.stComIn),
pComOut:=ADR(stSerialComm.stComOut) ,
SizeComIn:= UINT_TO_INT(SIZEOF(stSerialComm.stComOut)),
TxBuffer:= stSerialComm.TxBuffer,
RxBuffer:= stSerialComm.RxBuffer,
Error=> ,
ErrorID=> );
END_FUNCTION_BLOCK
- Related:
FB_ShiftByteArray
FUNCTION_BLOCK FB_ShiftByteArray
VAR_INPUT
ptArray : POINTER TO BYTE;
iSize : INT;
iCurrIndex : INT;
iMessageLen : INT;
iShift : INT;
END_VAR
VAR_OUTPUT
q_xError : BOOL;
END_VAR
VAR
howbig : UINT;
END_VAR
howbig := sizeof(ptArray^);
// Clear error bit
q_xError := FALSE;
IF iMessageLen = iCurrIndex THEN
//Dont do a damn thing
;
// Check the remaining length and shift amount summed is not greater than the total array width.
ELSIF iSize < (iMessageLen - iCurrIndex) + iShift THEN
q_xError := TRUE;
ELSE// MEMMOVE from 1+ the current index to the remaining length by the shift amount
IF MEMMOVE(srcAddr:=(ptArray + iCurrIndex + 1), destAddr:=(ptArray + iCurrIndex + 1 + iShift), n:=INT_TO_UDINT(iMessageLen + 1 - iCurrIndex)) =0 THEN
q_xError := TRUE;
END_IF
END_IF
END_FUNCTION_BLOCK
FB_SolenoidPair
FUNCTION_BLOCK FB_SolenoidPair
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: Valve:01:Open
io: io
'}
q_xDO1 AT %Q* : BOOL;
{attribute 'pytmc' := '
pv: Valve:02:Open
io: io
'}
q_xDO2 AT %Q* : BOOL;
{attribute 'pytmc' := '
pv: Switch
io: o
'}
i_xEpics AT %I* : BOOL; // open A when False, open B when True
{attribute 'pytmc' := '
pv: Debug
io: o
'}
i_xDebug AT %I* : BOOL; // if True, do not set valves according to i_xEpics
END_VAR
(* If debug, set independently. Else, only open one valve according to i_xEpics *)
IF NOT i_xDebug THEN
q_xDO1 := NOT i_xEpics;
q_xDO2 := i_xEpics;
END_IF
END_FUNCTION_BLOCK
FB_TECDriver
FUNCTION_BLOCK FB_TECDriver
VAR_INPUT
i_xExecute : BOOL; (* rising edge execute *)
i_tTimeOut : TIME := t#10s; (* Maximum wait time for reply *)
i_stControl : ST_TECControl;
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_xError : BOOL;
q_xWarning : BOOL; (* set in the event of an unexpected reply *)
q_xTimeout : BOOL;
q_sResult : STRING(255);
q_sLastSentString : ARRAY[1..40] OF STRING; (* Last String Sent to Serial Device - for debugging *)
q_sLastReceivedString : ARRAY[1..40] OF STRING; (* Last String Received from Serial Device - for debugging *)
q_stStatus : ST_TECStatus;
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
rtExecute : R_TRIG;
iStep : INT;
sSendData : STRING;
fbTECTransaction : FB_TECTransaction;
(*fbFormatString : FB_FormatString;*)
(*fbSplit : FB_Split255;*)
asReplyFields : ARRAY[1..20] OF T_MaxString;
wChecksum: WORD;
rtStartIntegration: R_TRIG;
rtEndReset: R_TRIG;
sWorking: STRING;
strResponseExpected: STRING;
diWorking : DINT;
END_VAR
(* This function block performs serial communication with a TC-36-25 TEC Driver *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
IF rtExecute.Q THEN
q_xDone := FALSE;
q_xError := FALSE;
q_xWarning := FALSE;
q_xTimeout := FALSE;
q_sResult:= '';
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 10;
END_IF
CASE iStep OF
0: (* idle *)
;
10: (* Get Current Temperature *)
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '01',
i_diParameter := 0,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 10 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
q_stStatus.sReply := fbTECTransaction.q_sResponseData;
sWorking :=LEFT(q_stStatus.sReply, 8);
sWorking := CONCAT('16#', sWorking);
q_stStatus.diTemp1 := STRING_TO_DINT(sWorking);
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
iStep := 20;
END_IF
20: (* Read setpoint *)
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '03', // command
i_diParameter := 0, // always zero for query
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 20 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
q_stStatus.sReply := fbTECTransaction.q_sResponseData;
sWorking :=LEFT(q_stStatus.sReply, 8);
sWorking := CONCAT('16#', sWorking);
q_stStatus.diSetPoint := STRING_TO_DINT(sWorking);
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
iStep := 25;
END_IF
25: (* Read output ON/OFF *)
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '46', // command
i_diParameter := 0, // always zero for query
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 25 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
q_stStatus.sReply := fbTECTransaction.q_sResponseData;
sWorking :=LEFT(q_stStatus.sReply, 8);
sWorking := CONCAT('16#', sWorking);
diWorking := STRING_TO_DINT(sWorking);
q_stStatus.xOutputOn := DINT_TO_BOOL(diWorking);
A_ResetTransaction();
iStep := 30;
END_IF
30: (* Read the alarm status *)
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '05', // command
i_diParameter := 0, // always zero for query
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 10 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
q_stStatus.sReply := fbTECTransaction.q_sResponseData;
sWorking :=LEFT(q_stStatus.sReply, 8);
sWorking := CONCAT('16#', sWorking);
diWorking := STRING_TO_DINT(sWorking);
//Alarm assignment
q_stStatus.xHiTemp := diWorking.0;
q_stStatus.xLoTemp := diWorking.1;
q_stStatus.xLoTemp := diWorking.2;
q_stStatus.xOverCurrent := diWorking.3;
q_stStatus.xOpenInput1 := diWorking.4;
q_stStatus.xOpenInput2 := diWorking.5;
q_stStatus.xDriverLowVoltage := diWorking.6;
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
iStep := 40;
END_IF
40: (* Output Power *)
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '02', // command
i_diParameter := 0, // always zero for query
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 10 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
q_stStatus.sReply := fbTECTransaction.q_sResponseData;
sWorking :=LEFT(q_stStatus.sReply, 8);
sWorking := CONCAT('16#', sWorking);
diWorking := STRING_TO_DINT(sWorking);
q_stStatus.diPercentOut := (diWorking/511)*100; //in percentage
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
iStep := 5000;
END_IF
5000: (* Change Setpoint *)
IF q_stStatus.diSetpoint <> i_stControl.diTempSetpoint THEN
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '1c', // command
i_diParameter := i_stControl.diTempSetpoint,
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=TRUE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 5000 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
fbTECTransaction(
i_xExecute:= FALSE,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag:=FALSE ); (* reset *)
iStep := 5010;
END_IF
ELSE
iStep := 5010;
END_IF
5010: (* Output ON/OFF *)
IF q_stStatus.xOutputOn <> i_stControl.xOutputOn THEN
fbTECTransaction(
i_xExecute:= TRUE,
i_sAddress:=i_stControl.sAddress,
i_sCmd:= '2d', // command
i_diParameter := BOOL_TO_DINT(i_stControl.xOutputOn),
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=TRUE);
IF fbTECTransaction.q_xError THEN
q_sResult := CONCAT('in Step 5010 serial transaction failed with message: ', fbTECTransaction.q_sResult);
iStep := 9000;
ELSIF fbTECTransaction.q_xDone THEN
fbTECTransaction(
i_xExecute:= FALSE,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag:=FALSE ); (* reset *)
iStep := 8000;
END_IF
ELSE
iStep := 8000;
END_IF
8000: (* done *)
q_xDone := TRUE;
IF q_sResult = '' THEN
q_sResult := 'Success';
q_stStatus.xStatusValid := TRUE;
END_IF
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
iStep := 0;
END_IF
9000:
_A_ClearStatus();
q_xTimeout := fbTECTransaction.q_xTimeout;
q_xError := TRUE;
q_stStatus.xStatusValid := FALSE;
END_CASE
q_sLastSentString := fbTECTransaction.q_sLastSentString;
q_sLastReceivedString := fbTECTransaction.q_sLastReceivedString;
END_FUNCTION_BLOCK
ACTION A_ResetTransaction:
fbTECTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
END_ACTION
ACTION _A_ClearStatus:
q_stStatus.sReply := '';
END_ACTION
- Related:
FB_TECTransaction
FUNCTION_BLOCK FB_TECTransaction
VAR_INPUT
i_xExecute : BOOL; (* Rising edge execute *)
i_sCmd : STRING(2); (* Send Data *)
i_sAddress : STRING(2);
i_diParameter : DINT;
i_tTimeOut : TIME := T#10S; (* Maximum wait time for reply *)
i_CmdFlag: BOOL := FALSE;
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_sResponseData : STRING;
q_xError : BOOL;
q_xTimeout : BOOL;
q_sResult : STRING;
q_sLastSentString : ARRAY[1..40] OF STRING; (* Last String Sent to Serial Device - for debugging *)
q_sLastReceivedString : ARRAY[1..40] OF STRING; (* Last String Received from Serial Device - for debugging *)
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
rtExecute : R_TRIG;
iStep : INT;
fbClearComBuffer : ClearComBuffer;
fbSendString : SendString;
fbReceiveString : ReceiveString;
sReceivedString : STRING;
tonTimeout : TON;
sSendString: STRING;
iStringTx: INT := 1;
iStringRx: INT := 1;
iStringTxRx: INT := 1;
sTxRxString: ARRAY[1..40] OF STRING;
fbFormatParamString : FB_FormatString;
fbFormatParamZPadString : FB_FormatString;
fbFormatCSString : FB_FormatString;
sParameter: STRING;
iIndexChkSum: INT;
wCsSum: WORD;
udiParameter: UDINT;
END_VAR
(* This function block performs serial transactions with a TC-36-25 TEC Driver *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
IF rtExecute.Q THEN
q_xDone := FALSE;
q_sResponseData := '';
q_xError := FALSE;
q_xTimeout := FALSE;
q_sResult:= '';
q_sLastSentString[iStringTx] := '';
q_sLastReceivedString[iStringRx]:= '';
iStep := 10;
END_IF
CASE iStep OF
0:
; (* idle *)
10: (* clear com buffers *)
fbClearComBuffer(Buffer:= iq_stSerialRXBuffer);
fbClearComBuffer(Buffer:= iq_stSerialTXBuffer);
(* Reset receive *)
fbReceiveString(
Reset:= TRUE,
ReceivedString:= sReceivedString,
RXbuffer:= iq_stSerialRXBuffer );
(* build the string *)
sSendString := CONCAT(i_sAddress, i_sCmd); (* adding the address and command/query *)
IF i_CmdFlag THEN
(* Convert the parameter into an 8-wide hex string twos complemented*)
udiParameter := DINT_TO_UDINT(i_diParameter);
fbFormatParamString(sFormat:= '%x', arg1:=F_UDINT(udiParameter)); // first convert to a string
fbFormatParamZPadString(sFormat:='%08s', arg1:=F_STRING(fbFormatParamString.sOut));// then zero pad, because FB_Format string does not zero-pad hex strings naturally
sParameter := fbFormatParamZPadString.sOut;
ELSE
sParameter := '00000000'; (* send zeros if we're querying *)
END_IF
(* Add parameter *)
sSendString := CONCAT(sSendString, sParameter);
(* Compute checksum *)
A_Checksum();
(* add stx at start *)
sSendString := CONCAT('*', sSendString);
(* append checksum *)
fbFormatCSString(sFormat := '%02x', arg1:=F_WORD(wCsSum));
sSendString := CONCAT(sSendString, fbFormatCSString.sOut);
(*adding tail *)
sSendString := CONCAT(sSendString,'$R'); (* add <cr> *)
IF fbFormatParamString.bError OR fbFormatCSString.bError THEN
q_sResult := CONCAT('In step 10 fbFormatString resulted in error: ', UDINT_TO_STRING(fbFormatParamString.nErrId));
iStep := 9000;
ELSE
fbSendString( SendString:= sSendString, TXbuffer:= iq_stSerialTXBuffer );
END_IF
q_sLastSentString[iStringTx] := sSendString;
IF iStringTx = 40 THEN iStringTx := 1; ELSE iStringTx := iStringTx +1; END_IF
iStep := iStep + 10;
20: (* Finish sending the String *)
IF fbSendString.Busy THEN
fbSendString( SendString:= sSendString, TXbuffer:= iq_stSerialTXBuffer );
ELSIF fbSendString.Error <> 0 THEN
q_sResult := CONCAT('In step 20 fbSendString resulted in error: ', INT_TO_STRING(fbSendString.Error));
iStep := 9000;
ELSIF NOT fbSendString.Busy THEN
iStep:=iStep + 10;
END_IF
tonTimeout(IN:= FALSE);
30: (* Get reply *)
fbReceiveString(
Prefix:= '*',
Suffix:= '^',
Timeout:= i_tTimeOut,
Reset:= FALSE,
ReceivedString:= sReceivedString,
RXbuffer:= iq_stSerialRXBuffer );
tonTimeout(IN:= TRUE, PT:= i_tTimeOut);
IF fbReceiveString.Error <> 0 THEN
q_sResult := CONCAT('In step 30 fbReceiveString resulted in error: ', INT_TO_STRING(fbReceiveString.Error));
iStep := 9000;
ELSIF fbReceiveString.RxTimeout OR tonTimeout.Q THEN
q_sResult := 'Serial device failed to reply within timeout period';
q_xTimeout := TRUE;
iStep := 9000;
ELSIF fbReceiveString.StringReceived THEN
q_sLastReceivedString[iStringRx] := sReceivedString;
IF iStringRx = 40 THEN iStringRx := 1; ELSE iStringRx := iStringRx +1; END_IF
IF LEN(sReceivedString) < 2 THEN
q_sResult := 'Reply too short.';
iStep := 9000;
ELSE
(* Removing stx and ack *)
q_sResponseData := LEFT(sReceivedString,LEN(sReceivedString)-1);
q_sResponseData := RIGHT(q_sResponseData,LEN(q_sResponseData)-1);
(* Checking for error messages *)
IF q_sResponseData = 'XXXXXXXXc0' THEN
q_sResult := 'Checksum error reported by driver';
iStep := 9000;
END_IF
q_sResult := 'Success.';
q_xDone:= TRUE;
iStep := 100;
END_IF
END_IF
100: (* done *)
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
iStep := 0;
END_IF
IF NOT i_CmdFlag THEN
sTxRxString[iStringTxRx]:=CONCAT(CONCAT(q_sLastSentString[iStringTx], ' received '), q_sLastReceivedString[iStringRx]);
END_IF
IF iStringTxRx = 40 THEN iStringTxRx := 1; ELSE iStringTxRx := iStringTxRx +1; END_IF
9000:
q_xError := TRUE;;
END_CASE
END_FUNCTION_BLOCK
ACTION A_Checksum:
wCsSum := 0;
FOR iIndexChkSum := 0 TO (LEN(sSendString) -1) DO
wCsSum:= wCsSum + BYTE_TO_WORD(sSendString[iIndexChkSum]);
END_FOR
wCsSum := wCsSum MOD 256; (* modulo 256 *)
END_ACTION
FB_ViciDriver
FUNCTION_BLOCK FB_ViciDriver
VAR_INPUT
i_xExecute : BOOL; (* rising edge execute *)
i_tTimeOut : TIME := t#10s; (* Maximum wait time for reply *)
i_stControl : ST_ViciControl;
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_xError : BOOL;
q_xWarning : BOOL; (* set in the event of an unexpected reply *)
q_xTimeout : BOOL;
q_sResult : STRING(255);
q_sLastSentString : ARRAY[1..40] OF STRING; (* Last String Sent to Serial Device - for debugging *)
q_sLastReceivedString : ARRAY[1..40] OF STRING; (* Last String Received from Serial Device - for debugging *)
q_stStatus : ST_ViciStatus;
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
iReqPosReverse : INT;
rtExecute : R_TRIG;
iStep : INT;
sSendData : STRING;
fbViciTransaction : FB_ViciTransaction;
(*fbFormatString : FB_FormatString;*)
(*fbSplit : FB_Split255;*)
asReplyFields : ARRAY[1..20] OF T_MaxString;
wChecksum: WORD;
rtStartIntegration: R_TRIG;
rtEndReset: R_TRIG;
sWorking: STRING;
strResponseExpected: STRING;
END_VAR
(* This function block performs serial transactions with a Vici *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
IF rtExecute.Q THEN
q_xDone := FALSE;
q_xError := FALSE;
q_xWarning := FALSE;
q_xTimeout := FALSE;
q_sResult:= '';
FBViciTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer ); (* reset *)
iStep := 10;
END_IF
CASE iStep OF
0: (* idle *)
;
10: (* Get Position *)
FBViciTransaction(
i_xExecute:= TRUE,
i_iAddress:=i_stControl.iAddress,
i_sSendData:= 'CP', (*declared postion readback Vici command*)
i_tTimeOut:= i_tTimeOut,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag :=FALSE);
IF FBViciTransaction.q_xError THEN
q_sResult := CONCAT('in Step 10 serial transaction failed with message: ', FBViciTransaction.q_sResult);
q_stStatus.xPosValid := FALSE; (* used to signal an invalid readback or control exchange *)
iStep := 9000;
ELSIF FBViciTransaction.q_xDone THEN
q_stStatus.sViciReply := FBViciTransaction.q_sResponseData;
(* grab the left two characters, they should equal CP *)
strResponseExpected := CONCAT(INT_TO_STRING( i_stControl.iAddress), 'CP');
IF LEFT(q_stStatus.sViciReply, 3) <> strResponseExpected (*'1CP'*) THEN
(*use q-s Result to specify error type*)
q_sResult := CONCAT(CONCAT('in step 10 unexpected response recieved: ', LEFT(q_stStatus.sViciReply, 3)),CONCAT(' instead of: ', CONCAT(INT_TO_STRING( i_stControl.iAddress), 'CP')));
q_xWarning := TRUE;
(* If they don't equal CP, then error *)
q_stStatus.xPosValid := FALSE; (* used to signal an invalid readback or control exchange *)
iStep := 9000;
(* else then grab the right two characters and convert to INT *)
ELSE
sWorking := RIGHT(q_stStatus.sViciReply, 2);
q_stStatus.iCurrPos := 13 - STRING_TO_INT(sWorking); (* pass that INT to the q_stStatus.iCurrPos, mirroring positions *)
q_stStatus.xPosValid := TRUE;
FBViciTransaction( i_xExecute:= FALSE, iq_stSerialRXBuffer:= iq_stSerialRXBuffer, iq_stSerialTXBuffer:= iq_stSerialTXBuffer, i_CmdFlag :=FALSE); (* reset *)
iStep := 5000;
END_IF
END_IF
5000: (* Set position*)
(* Limit the requested position to the valid 12 possible positions *)
IF i_stControl.iReqPos > 12 OR i_stControl.iReqPos < 1 THEN
q_sResult := CONCAT('in step 5000 port range exceeded: ',FBViciTransaction.q_sResponseData);
i_stControl.iReqPos := LIMIT(1, i_stControl.iReqPos,12);
END_IF
(* Mirror the "clock" value across the vertical axis of symmetry *)
iReqPosReverse := 13 - i_stControl.iReqPos;
IF q_stStatus.iReqPos <> i_stControl.iReqPos THEN
IF q_stStatus.iCurrPos > i_stControl.iReqPos THEN
IF ABS(q_stStatus.iCurrPos - i_stControl.iReqPos) >= 6 THEN
sSendData := CONCAT('CW', INT_TO_STRING(iReqPosReverse));
ELSE
sSendData := CONCAT('CC', INT_TO_STRING(iReqPosReverse));
END_IF
END_IF
IF q_stStatus.iCurrPos < i_stControl.iReqPos THEN
IF ABS(q_stStatus.iCurrPos - i_stControl.iReqPos) >= 6 THEN
sSendData := CONCAT('CC', INT_TO_STRING(iReqPosReverse));
ELSE
sSendData := CONCAT('CW', INT_TO_STRING(iReqPosReverse));
END_IF
END_IF
FBViciTransaction(
i_xExecute:= TRUE,
i_sSendData:= sSendData,
i_tTimeOut:= i_tTimeOut,
i_iAddress:=i_stControl.iAddress,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag:=TRUE);
IF FBViciTransaction.q_xError THEN
q_sResult := CONCAT('in Step 5000 serial transaction failed with message: ', FBViciTransaction.q_sResult);
iStep := 9000;
ELSIF FBViciTransaction.q_xDone THEN
FBViciTransaction(
i_xExecute:= FALSE,
iq_stSerialRXBuffer:= iq_stSerialRXBuffer,
iq_stSerialTXBuffer:= iq_stSerialTXBuffer,
i_CmdFlag:=FALSE ); (* reset *)
q_stStatus.iReqPos := i_stControl.iReqPos;
iStep := 8000;
END_IF
ELSE
iStep := 8000;
END_IF
8000: (* done *)
q_xDone := TRUE;
IF q_sResult = '' THEN
q_sResult := 'Success';
END_IF
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
iStep := 0;
END_IF
9000:
_A_ClearStatus();
q_xTimeout := FBViciTransaction.q_xTimeout;
q_xError := TRUE;
END_CASE
q_sLastSentString := FBViciTransaction.q_sLastSentString;
q_sLastReceivedString := FBViciTransaction.q_sLastReceivedString;
END_FUNCTION_BLOCK
ACTION _A_ClearStatus:
q_stStatus.sViciReply := '';
END_ACTION
FB_ViciTransaction
FUNCTION_BLOCK FB_ViciTransaction
VAR_INPUT
i_xExecute : BOOL; (* Rising edge execute *)
i_sSendData : STRING; (* Send Data *)
i_iAddress :INT;
i_tTimeOut : TIME := t#10s; (* Maximum wait time for reply *)
i_CmdFlag: BOOL := FALSE;
END_VAR
VAR_OUTPUT
q_xDone : BOOL;
q_sResponseData : STRING;
q_xError : BOOL;
q_xTimeout : BOOL;
q_sResult : STRING;
q_sLastSentString : ARRAY[1..40] OF STRING; (* Last String Sent to Serial Device - for debugging *)
q_sLastReceivedString : ARRAY[1..40] OF STRING; (* Last String Received from Serial Device - for debugging *)
END_VAR
VAR_IN_OUT
iq_stSerialRXBuffer : ComBuffer;
iq_stSerialTXBuffer : ComBuffer;
END_VAR
VAR
rtExecute : R_TRIG;
iStep : INT;
fbClearComBuffer : ClearComBuffer;
fbSendString : SendString;
fbReceiveString : ReceiveString;
sReceivedString : STRING;
tonTimeout : TON;
sSendString: STRING;
iStringTx: INT := 1;
iStringRx: INT := 1;
iStringTxRx: INT := 1;
sTxRxString: ARRAY[1..40] OF STRING;
END_VAR
(* This function block performs serial transactions with a VICI *)
(* rising edge trigger *)
rtExecute(CLK:= i_xExecute);
IF rtExecute.Q THEN
q_xDone := FALSE;
q_sResponseData := '';
q_xError := FALSE;
q_xTimeout := FALSE;
q_sResult:= '';
q_sLastSentString[iStringTx] := '';
q_sLastReceivedString[iStringRx]:= '';
iStep := 10;
END_IF
CASE iStep OF
0:
; (* idle *)
10: (* clear com buffers *)
fbClearComBuffer(Buffer:= iq_stSerialRXBuffer);
fbClearComBuffer(Buffer:= iq_stSerialTXBuffer);
(* Reset receive *)
fbReceiveString(
Reset:= TRUE,
ReceivedString:= sReceivedString,
RXbuffer:= iq_stSerialRXBuffer );
(* send the string *)
sSendString := INT_TO_STRING(i_iAddress); (*building address header *)
sSendString := CONCAT(sSendString, i_sSendData); (* adding the header *)
sSendString := CONCAT(sSendString,'$R$L'); (* add <cr><lf> *) (*adding tail *)
fbSendString( SendString:= sSendString, TXbuffer:= iq_stSerialTXBuffer );
q_sLastSentString[iStringTx] := sSendString;
IF iStringTx = 40 THEN iStringTx := 1; ELSE iStringTx := iStringTx +1; END_IF
iStep := iStep + 10;
20: (* Finish sending the String *)
IF fbSendString.Busy THEN
fbSendString( SendString:= sSendString, TXbuffer:= iq_stSerialTXBuffer );
ELSIF fbSendString.Error <> 0 THEN
q_sResult := CONCAT('In step 20 fbSendString resulted in error: ', INT_TO_STRING(fbSendString.Error));
iStep := 9000;
ELSIF NOT fbSendString.Busy THEN
(* if we are sending a position change request then we expect no reply *)
IF i_CmdFlag THEN
q_sResult := 'String sent....';
q_xDone:= TRUE;
iStep := 100;
ELSE
iStep:=iStep + 10;
END_IF
END_IF
tonTimeout(IN:= FALSE);
30: (* Get reply *)
fbReceiveString(
Prefix:= ,
Suffix:= '$R',
Timeout:= i_tTimeOut,
Reset:= FALSE,
ReceivedString:= sReceivedString,
RXbuffer:= iq_stSerialRXBuffer );
tonTimeout(IN:= TRUE, PT:= i_tTimeOut);
IF fbReceiveString.Error <> 0 THEN
q_sResult := CONCAT('In step 30 fbReceiveString resulted in error: ', INT_TO_STRING(fbReceiveString.Error));
iStep := 9000;
ELSIF fbReceiveString.RxTimeout OR tonTimeout.Q THEN
q_sResult := 'Serial device failed to reply within timeout period';
q_xTimeout := TRUE;
iStep := 9000;
ELSIF fbReceiveString.StringReceived THEN
q_sLastReceivedString[iStringRx] := sReceivedString;
IF iStringRx = 40 THEN iStringRx := 1; ELSE iStringRx := iStringRx +1; END_IF
IF LEN(sReceivedString) < 2 THEN
q_sResult := 'Reply too short.';
iStep := 9000;
ELSE
q_sResponseData := LEFT(sReceivedString,LEN(sReceivedString)-1);
q_sResult := 'Success.';
q_xDone:= TRUE;
iStep := 100;
END_IF
END_IF
100: (* done *)
IF i_xExecute = FALSE THEN
q_xDone:= FALSE;
iStep := 0;
END_IF
IF NOT i_CmdFlag THEN
sTxRxString[iStringTxRx]:=CONCAT(CONCAT(q_sLastSentString[iStringTx], ' received '), q_sLastReceivedString[iStringRx]);
END_IF
IF iStringTxRx = 40 THEN iStringTxRx := 1; ELSE iStringTxRx := iStringTxRx +1; END_IF
9000:
q_xError := TRUE;;
END_CASE
END_FUNCTION_BLOCK
fSHDLCChecksum
FUNCTION fSHDLCChecksum : BYTE
VAR_INPUT
i_bAdr : BYTE;
i_bCmd : BYTE;
i_bLen : BYTE;
i_pbaTxData : POINTER TO BYTE;
END_VAR
VAR
iIndex : INT;
END_VAR
(* Checksum is calc like so
Sum all the bytes within the frame
Take the LSN ie. nibble of the result and invert it
*)
fSHDLCChecksum := i_bAdr + i_bCmd + i_bLen;
IF i_bLen > 0 THEN
FOR iIndex:=0 TO BYTE_TO_INT(i_bLen)DO
fSHDLCChecksum := fSHDLCChecksum + i_pbaTxData^;
i_pbaTxData := i_pbaTxData + 1;
END_FOR
END_IF
// Inverting LSN
fSHDLCChecksum := fSHDLCChecksum XOR 16#FF;
END_FUNCTION
MAIN
PROGRAM MAIN
VAR
END_VAR
M3SelectorA();
M3SelectorB();
M3SelectorC();
M3SelectorD();
PCMA();
PCMB();
PCMC();
PCMD();
GasMan();
BronkhorstA();
BronkhorstB();
BronkhorstC();
BronkhorstD();
SolenoidPairA();
(* Following execution of all the programs, set the first pass false *)
g_xFirstPass := FALSE;
END_PROGRAM
MC_SmoothMover
FUNCTION_BLOCK MC_SmoothMover
VAR_IN_OUT
Axis : AXIS_REF;
END_VAR
VAR_INPUT
Velocity : LREAL;
ReqAbsPos : LREAL; //A
Enable : BOOL; //While true the block will accept new positions and attempt to move to them if they are different
Execute : BOOL; //Will retry a move if the target position is the same
END_VAR
VAR_OUTPUT
Done : BOOL;
Busy : BOOL;
Error : BOOL;
END_VAR
VAR
mcMoveAbsolute : ARRAY[1..2] OF MC_MoveAbsolute;
iI: INT;
imcBlockIndex: INT;
ReqAbsPosPrevious : LREAL;
rtExecute: R_TRIG;
END_VAR
(* Smooth Mover
2017-8-30
A. Wallace
Enable means the block will always aquire new positions as they are updated. Execute
can be used to retry a move.
*)
rtExecute(CLK:=Execute);
IF ( (ReqAbsPos <> ReqAbsPosPrevious AND Enable) OR rtExecute.Q) THEN
mcMoveAbsolute[imcBlockIndex].Execute := FALSE;
imcBlockIndex := imcBlockIndex + 1;
IF imcBlockIndex >2 THEN imcBlockIndex := 1; END_IF
mcMoveAbsolute[imcBlockIndex].Position := ReqAbsPos;
mcMoveAbsolute[imcBlockIndex].Execute := TRUE;
ReqAbsPosPrevious := ReqAbsPos;
ELSIF mcMoveAbsolute[imcBlockIndex].Done OR
mcMoveAbsolute[imcBlockIndex].CommandAborted OR
mcMoveAbsolute[imcBlockIndex].Busy OR
mcMoveAbsolute[imcBlockIndex].Error THEN
mcMoveAbsolute[imcBlockIndex].Execute := FALSE;
END_IF
FOR iI := 1 TO 2 DO
mcMoveAbsolute[iI](Axis := Axis, Velocity:=Velocity, BufferMode:=MC_Aborting);
END_FOR
Error := mcMoveAbsolute[1].Error OR mcMoveAbsolute[2].Error;
Done S= mcMoveAbsolute[1].Done OR mcMoveAbsolute[2].Done;
Busy := mcMoveAbsolute[1].Busy OR mcMoveAbsolute[2].Busy;
Done R= Busy OR Error;
END_FUNCTION_BLOCK
p_ALI
PROGRAM p_ALI
VAR
fbALI_A : FB_ALI;
END_VAR
fbALI_A(iq_Injector:=stALI);
END_PROGRAM
- Related:
p_Autosave
PROGRAM p_Autosave
VAR
END_VAR
(* If first pass, copy all persistent variables (which should be intialized to old values) back into the operational variables *)
IF g_xFirstPass THEN
stRegProp1 := gp_stRegProp1;
stRegProp2 := gp_stRegProp2;
stRegProp3 := gp_stRegProp3;
stRegProp4 := gp_stRegProp4;
ELSE
gp_stRegProp1 := stRegProp1;
gp_stRegProp2 := stRegProp2;
gp_stRegProp3 := stRegProp3;
gp_stRegProp4 := stRegProp4;
END_IF
END_PROGRAM
p_AutoWash
PROGRAM p_AutoWash
VAR
END_VAR
END_PROGRAM
p_CoolerShaker
PROGRAM p_CoolerShaker
VAR
afbTECDriver : ARRAY[1..3] OF FB_TECDriver;
index: INT :=1 ;
END_VAR
stTECCtrl[1].sAddress := '01'; //62 is 98 in hex which is default address
stTECCtrl[2].sAddress := '02';
stTECCtrl[3].sAddress := '62';
stTECCtrl[1].diTempSetpoint := 30; //98 in hex which is default address
stTECCtrl[2].diTempSetpoint := 30;
stTECCtrl[3].diTempSetpoint := 30;
afbTECDriver[index](
i_xExecute:= TRUE,
i_tTimeOut:= t#1s,
i_stControl:= stTECCtrl[index],
iq_stSerialRXBuffer:= SerialRXBuffer_CoolerShakerTEC,
iq_stSerialTXBuffer:= SerialTXBuffer_CoolerShakerTEC,
q_stStatus=>stTECStatus[index]);
IF afbTECDriver[index].q_xDone OR
afbTECDriver[index].q_xError OR
afbTECDriver[index].q_xTimeout THEN
(* reset function for next time *)
afbTECDriver[index](i_xExecute:=FALSE, iq_stSerialRXBuffer:= SerialRXBuffer_CoolerShakerTEC, iq_stSerialTXBuffer:= SerialTXBuffer_CoolerShakerTEC);
index := index + 1;
IF index > 2 THEN index := 1; END_IF
END_IF
END_PROGRAM
- Related:
p_ESTOP
PROGRAM p_ESTOP
VAR
rtEstop : R_TRIG;
END_VAR
(*
Estop checks for a rising edge on the estop global variable then triggeres the following actions
1. Set selector to position 12, the default resting place
2. Set the water regulator to 0
The sheath regulator is not set to zero at this point because we don't want to ice the jet.
*)
rtEstop(CLK:=g_xEstop);
(* Check for E-Stop *)
IF rtEstop.Q THEN
stSelector.iVici1ReqPos := 12;
stSelector.iVici2ReqPos := 12;
stSelector.iSyncReqPos := 12;
END_IF
END_PROGRAM
p_Manifold
PROGRAM p_Manifold
VAR
fbManiValve : FB_ManiValve;
END_VAR
(* Valve 1 *)
stGasMani.stManiVlv1.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv1);
(* Valve 2 *)
stGasMani.stManiVlv2.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv2);
(* Valve 3 *)
stGasMani.stManiVlv3.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv3);
(* Valve 4 *)
stGasMani.stManiVlv4.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv4);
(* Valve 5 *)
stGasMani.stManiVlv5.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv5);
(* Valve 6 *)
stGasMani.stManiVlv6.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv6);
(* Valve 7 *)
stGasMani.stManiVlv7.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv7);
(* Valve 8 *)
stGasMani.stManiVlv8.xILK := stGasMani.xOnline;
fbManiValve(iq_valve:=stGasMani.stManiVlv8);
END_PROGRAM
- Related:
p_Regulators
PROGRAM p_Regulators
VAR
fbRegProp1 : FB_ProportionairRegulator;
fbRegProp2 : FB_ProportionairRegulator;
fbRegProp3 : FB_ProportionairRegulator;
fbRegProp4 : FB_ProportionairRegulator;
END_VAR
(* Proportionair regulators *)
fbRegProp1(iq_stRegProp:=stRegProp1);
fbRegProp2(iq_stRegProp:=stRegProp2);
fbRegProp3(iq_stRegProp:=stRegProp3);
fbRegProp4(iq_stRegProp:=stRegProp4);
END_PROGRAM
- Related:
p_Shakers
PROGRAM p_Shakers
VAR
END_VAR
END_PROGRAM
p_SoftIO
PROGRAM p_SoftIO
VAR
END_VAR
(* Shakers *)
stSelector.stShaker01.i_xSwitch := iq_stM2SelectorA.i_EP2338_Ch5;
stSelector.stShaker02.i_xSwitch := iq_stM2SelectorA.i_EP2338_Ch6;
stSelector.stShaker03.i_xSwitch := iq_stM2SelectorA.i_EP2338_Ch7;
stSelector.stShaker04.i_xSwitch := iq_stM2SelectorA.i_EP2338_Ch8;
iq_stM2SelectorA.q_EP2338_Ch1 := stSelector.stShaker01.q_xPwrDO;
iq_stM2SelectorA.q_EP2338_Ch2 := stSelector.stShaker02.q_xPwrDO;
iq_stM2SelectorA.q_EP2338_Ch3 := stSelector.stShaker03.q_xPwrDO;
iq_stM2SelectorA.q_EP2338_Ch4 := stSelector.stShaker04.q_xPwrDO;
stSelector2.stShaker01.i_xSwitch := iq_stM2SelectorB.i_EP2338_Ch5;
stSelector2.stShaker02.i_xSwitch := iq_stM2SelectorB.i_EP2338_Ch6;
stSelector2.stShaker03.i_xSwitch := iq_stM2SelectorB.i_EP2338_Ch7;
stSelector2.stShaker04.i_xSwitch := iq_stM2SelectorB.i_EP2338_Ch8;
iq_stM2SelectorB.q_EP2338_Ch1 := stSelector2.stShaker01.q_xPwrDO;
iq_stM2SelectorB.q_EP2338_Ch2 := stSelector2.stShaker02.q_xPwrDO;
iq_stM2SelectorB.q_EP2338_Ch3 := stSelector2.stShaker03.q_xPwrDO;
iq_stM2SelectorB.q_EP2338_Ch4 := stSelector2.stShaker04.q_xPwrDO;
stRegProp1.iPressureRaw := iq_stPressCtrlA.i_EP4374_Ch1;
stRegProp2.iPressureRaw := iq_stPressCtrlA.i_EP4374_Ch2;
iq_stPressCtrlA.q_EP4374_Ch3 := stRegProp1.iSetpointRaw;
iq_stPressCtrlA.q_EP4374_Ch4 := stRegProp2.iSetpointRaw;
stRegProp3.iPressureRaw := iq_stPressCtrlB.i_EP4374_Ch1;
stRegProp4.iPressureRaw := iq_stPressCtrlB.i_EP4374_Ch2;
iq_stPressCtrlB.q_EP4374_Ch3 := stRegProp3.iSetpointRaw;
iq_stPressCtrlB.q_EP4374_Ch4 := stRegProp4.iSetpointRaw;
stGasMani.xOnline := NOT iq_stM3GasManifold.i_SyncUnitWC;
iq_stM3GasManifold.q_EP2338_Ch1 := stGasMani.stManiVlv1.qxDO;
iq_stM3GasManifold.q_EP2338_Ch2 := stGasMani.stManiVlv2.qxDO;
iq_stM3GasManifold.q_EP2338_Ch3 := stGasMani.stManiVlv3.qxDO;
iq_stM3GasManifold.q_EP2338_Ch4 := stGasMani.stManiVlv4.qxDO;
iq_stM3GasManifold.q_EP2338_Ch5 := stGasMani.stManiVlv5.qxDO;
iq_stM3GasManifold.q_EP2338_Ch6 := stGasMani.stManiVlv6.qxDO;
iq_stM3GasManifold.q_EP2338_Ch7 := stGasMani.stManiVlv7.qxDO;
iq_stM3GasManifold.q_EP2338_Ch8 := stGasMani.stManiVlv8.qxDO;
stGasMani.stManiVlv1.ixOPN := iq_stM3GasManifold.i_EP2338_Ch1;
stGasMani.stManiVlv2.ixOPN := iq_stM3GasManifold.i_EP2338_Ch2;
stGasMani.stManiVlv3.ixOPN := iq_stM3GasManifold.i_EP2338_Ch3;
stGasMani.stManiVlv4.ixOPN := iq_stM3GasManifold.i_EP2338_Ch4;
stGasMani.stManiVlv5.ixOPN := iq_stM3GasManifold.i_EP2338_Ch5;
stGasMani.stManiVlv6.ixOPN := iq_stM3GasManifold.i_EP2338_Ch6;
stGasMani.stManiVlv7.ixOPN := iq_stM3GasManifold.i_EP2338_Ch7;
stGasMani.stManiVlv8.ixOPN := iq_stM3GasManifold.i_EP2338_Ch8;
END_PROGRAM
p_StatusLights
PROGRAM p_StatusLights
VAR
END_VAR
gxRedLight := xEnableRemoteControl;//Remote control in use
gxGreenLight := NOT (gxRedLight OR gxYellowLight); (* attach system error or something here someday *)
END_PROGRAM
p_VacGaugeSup
PROGRAM p_VacGaugeSup
VAR
END_VAR
END_PROGRAM
SerialTxRx
PROGRAM SerialTxRx
VAR
(*
fbSerialLineControl_M2SelAVici: SerialLineControl;
fbSerialLineControl_M2SelBVici: SerialLineControl;
fbSerialLineControl_M2SelAFM: SerialLineControl;
fbSerialLineControl_M2SelBFM: SerialLineControl;
fbSerialLineControl_M3SelAVici: SerialLineControl;
fbSerialLineControl_M3SelAFM: SerialLineControl;
fbSerialLineControl_M3SelBVici: SerialLineControl;
fbSerialLineControl_M3SelBFM: SerialLineControl;
*)
END_VAR
M3SelectorA.SerialComm();
M3SelectorB.SerialComm();
M3SelectorC.SerialComm();
M3SelectorD.SerialComm();
PCMA.SerialComm();
PCMB.SerialComm();
PCMC.SerialComm();
PCMD.SerialComm();
END_PROGRAM