DUTs
E_ArbDosTestStates
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_ArbDosTestStates :
(
Init := 0,
SecondReq := 100,
WaitForAck := 200,
WaitFor2ndAck := 300,
RunAsserts := 8000
);
END_TYPE
- Related:
E_BPTMState
TYPE E_BPTMState :
(
Init := 0,
NewTarget := 1000,
RequestBP := 1500,
WaitForBP := 2500,
WaitingForTransitionAssertion := 2000,
WaitingForFinalAssertion := 3000,
Transitioning := 4000,
WaitForFinalBP := 5000,
CleaningUp := 6000,
Idle := 10000,
Done := 8000,
Error := 9000
);
END_TYPE
E_BPTMTestStates
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_BPTMTestStates :
(
Init := 0,
WaitingForValidID := 50,
WaitingForTransitionAssertion := 100,
StalledAtTransition := 150,
WaitingForFinalAssertion := 200,
WaitingForBeam := 250,
Transitioning := 300,
CleaningUp := 400,
AnotherState := 500,
Retry := 1000,
Done := 8000,
Error := 9000
);
END_TYPE
E_MPSBeamRates
TYPE E_MPSBeamRates :
(
MPSRateInvalid := 0,
MPSRate0Hz := 1,
(* Previously used 2 and 3 for single shot and burst *)
MPSRate1Hz := 4,
MPSRate10Hz := 5,
MPSRate30Hz := 6,
MPSRate60Hz := 7,
MPSRate120Hz := 8,
MPSRateUnknown := 9,
MPSRateSingleShot := 10,
MPSRateBurstMode := 11,
MPSRateBurstModeNotActive := 12,
MPSNumberOfBeamRates := 13,
MPSRateBurstInvalid := 14
)UINT;
END_TYPE
K_Apertures
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE K_Apertures :
(
SL1K0 := 1,
SL2K0 := 2
);
END_TYPE
K_Attenuators
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE K_Attenuators :
(
AT1K0 := 1, // FEE Gas Attenuator
AT1K4 := 2, // TMO Solid Attenuator in the FEE
AT1K2 := 3, // RIX Solid Attenuator
AT2K2 := 4, // RIX Solid Attenuator
AT1K3 := 5 // TXI Solid Attenuator
);
END_TYPE
K_Stopper
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE K_Stopper :
(
ST3K4 := 1, // TMO PPS stopper in
ST1K2 := 2, // RIX PPS stopper in
MR1K1_IN := 3, // Monochromator focus optic vertical state
MR1K1_OUT := 4, // Monochromator focus optic vertical state
MR1K3_IN := 5, // TXI Offset Mirror horizontal state
MR1K3_OUT := 6, // TXI Offset Mirror horizontal state
DEFAULT := PMPS_GVL.MAX_VETO_DEVICES // DO NOT USE FOR ANYTHING
);
END_TYPE
- Related:
L_Apertures
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE L_Apertures :
(
SL1L0 := 1,
SL2L0 := 2
);
END_TYPE
L_Attenuators
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE L_Attenuators :
(
AT1L0 := 1, // FEE Combo Gas/Solid Attenuator
AT2L0 := 2 // FEE Solid Attenuator
);
END_TYPE
L_Stopper
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE L_Stopper :
(
ST1L0 := 1, // ST1
ST1L1 := 2, // ST2
DEFAULT := PMPS_GVL.MAX_VETO_DEVICES // DO NOT USE FOR ANYTHING
);
END_TYPE
- Related:
PMPS_CODES
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE PMPS_CODES :
(
FAST_FAULT := 16#1, // Generic fast fault
PEW_FAULT := 16#7, // Fault occurs when the calculated machine photon energy (K value calculated by undulator gap, and electron energy) falls outside the permitted range.
// Arbiter codes
//////////////////////////////////
ARB_FULL := 16#201, // Arbiter pool is full.
//BPTM Codes
//////////////////////////////////
BadTargetID := 16#300,
BadTransID := 16#301,
TransAssrtFail := 16#302,
FinalAssrtFail := 16#303,
NoRoomInArb := 16#304
) UINT;
END_TYPE
ST_AttenuatorStatus
TYPE ST_AttenuatorStatus :
STRUCT
AtTarget : BIT; //0
Moving : BIT; //1
LocalMode : BIT; //2 (* means the attenuator ignores PMPS preemptive requests and operates from a local setpoint *)
PMPSMode : BIT; //3 (* means the attenuator is only taking setpoints from the pmps *)
OK : BIT; //4
Toggle : BIT; //5 just here for fun
Include : BIT; //6 Consider this device status
END_STRUCT
END_TYPE
ST_BeamParams
TYPE ST_BeamParams :
STRUCT
(* Requested pre-optic attenuation % *)
{attribute 'pytmc' := 'pv: Transmission
io: i
field: HOPR 1;
field: LOPR 0;
field: PREC 2;
'}
nTran : REAL := 0;
(* Pulse-rate *)
{attribute 'pytmc' := 'pv: Rate
io: i
field: EGU Hz
'}
nRate : UDINT := 0;
(* Photon energy ranges *)
{attribute 'pytmc' := 'pv: PhotonEnergyRanges
io: i
field: EGU eV'}
{attribute 'displaymode' := 'binary'}
neVRange : DWORD := 0;
(* Beamline attenuators *)
{attribute 'pytmc' := 'pv: AuxAtt
io: i'}
astAttenuators : ARRAY [1..PMPS_GVL.AUX_ATTENUATORS] OF ST_PMPS_Attenuator;
(* Stoppers *)
{attribute 'pytmc' := 'pv: Veto
io: i'}
aVetoDevices : ARRAY [1..PMPS_GVL.MAX_VETO_DEVICES] OF BOOL;
(* Apertures *)
astApertures : ARRAY [1..PMPS_GVL.MAX_APERTURES] OF ST_PMPS_Aperture := [(Width:=1E3, Height:=1E3), (Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3)];
// Toggle for watchdog
xValidToggle : BOOL;
// Beam parameter set is valid (if readback), or acknowledged (if request)
{attribute 'pytmc' := 'pv: Valid
io: i'}
xValid : BOOL;
// Cohort index. Identifies which cohort this BP set was included in arbitration
{attribute 'pytmc' := 'pv: Cohort
io: i
field: DESC Cohort inc on each arb cycle
'}
nCohortInt : UDINT;
END_STRUCT
END_TYPE
- Related:
ST_BP_ArbInternal
TYPE ST_BP_ArbInternal EXTENDS ST_BeamParams :
STRUCT
{attribute 'pytmc' := 'pv: ID
io: i
'}
nId : DWORD;
{attribute 'pytmc' := 'pv: Live
io: i
'}
LiveInTable : BOOL;
{attribute 'pytmc' := 'pv: Device
io: i
'}
sDevName: STRING;
END_STRUCT
END_TYPE
- Related:
ST_Device
TYPE ST_Device :
STRUCT
//IO
stAxis : AXIS_REF;
//The following limit switches must be TRUE or high (ie. NC) to enable that direction of motion
//The EPICS display should show an inverted indicator to communicate end of travel reached
i_xLoLim AT %I* : BOOL; //Wire normally-closed
i_xHiLim AT %I* : BOOL; //Wire normally-closed
//Acuator Parameters
lrUpperPositionLimit : LREAL := 10000000000; //Use in conjuntion with limit switches or set to a huge number to ignore
lrLowerPositionLimit : LREAL := -10000000000; //Use in conjuntion with limit switches or set to a huge number to ignore
xEnable : BOOL := TRUE;
//Supervisory System
//MPS Override
xOverrideMPSLimits : BOOL := FALSE; //Set true to move beyond MPS limits. Note this will trip beam off.
//Status and Faults
xClearFaults : BOOL; //Rising edge tries to clear faults of all kinds
xGovernorFault : BOOL; //Indicates governor is faulted
xGovernorOverridden : BOOL; //Indicates governor is in override mode
xSMFault : BOOL; //Indicates component state machine is faulted
//Device States
StateTable : FB_LinearDeviceStateTable;
stTransitionState : ST_DeviceState; //The transition state might have non-zero beam parameters
nSafeState : UDINT; //State where the device is considered safe
nTransitionState : UDINT;
nRequestedState : UDINT; //EPICS control, cleaned (checked against array limits) and deposited in nTargetState
nTargetState : UDINT; //The requested state should be deposited here after cleaning
nActiveState : UDINT; //The last successfully reached state
END_STRUCT
END_TYPE
- Related:
ST_DeviceState
TYPE ST_DeviceState :
STRUCT
//State name
sStateName : STRING;
nStateRef : UDINT; //Primary key of device state database
//////////////////
// This stuff may belong in a sub-class or something
//Position
rPosition : REAL := 0; //Center of the state
//Tolerance
rTolerance : REAL := 0; //+/- amount
//////////////////
//Required beam parameters
stReqBeamParam : ST_BeamParams;
END_STRUCT
END_TYPE
- Related:
ST_DeviceStateExt
//Use when working with states in code
//Extended with a valid boolean to indicate the structure was loaded
//properly
TYPE ST_DeviceStateExt EXTENDS ST_DeviceState :
STRUCT
xValid : BOOL := FALSE;
END_STRUCT
END_TYPE
- Related:
ST_DeviceStateTable
TYPE ST_DeviceStateTable :
STRUCT
iDeviceID:DINT;
iStateID:DINT;
rTargetPosition: REAL;
rTolerance: REAL;
rTransmission: REAL;
iBeamClass : DINT;
iPulseRate: DINT;
rPerPulseEnergy:REAL;
rUpper_eV: REAL;
rLower_eV: REAL;
rPhotonEnergy: REAL;
END_STRUCT
END_TYPE
ST_FF
TYPE ST_FF :
STRUCT
{attribute 'pytmc' := '
pv: Info
'}
Info : ST_FFInfo;
{attribute 'pytmc' := '
pv: Ovrd
'}
Ovrd : ST_FFOverride;
{attribute 'pytmc' := '
pv: OK
io: i
'}
OK : BOOL; // Fault logic state
FaultAck : BOOL; // Set when faulted, reset by logger.
ClearAck : BOOL;
{attribute 'pytmc' := '
pv: BeamPermitted
io: i
'}
BeamPermitted : BOOL; // Result of reset, veto, and fault logic, true beam off boolean
{attribute 'pytmc' := '
pv: Reset
io: o
'}
Reset : BOOL;
bsFF : RS;
rtReset : R_TRIG;
ftCountFault : F_TRIG;
END_STRUCT
END_TYPE
- Related:
ST_FFInfo
// These elements should be set at init and never changed.
TYPE ST_FFInfo :
STRUCT
{attribute 'pytmc' := '
pv: Path
io: i
'}
sPath : T_MaxString; // Full PLC path to FF object
{attribute 'pytmc' := '
pv: Desc
io: i
'}
Desc : T_MaxString; // Set at instantiation to a helpful description of the fast fault purpose
{attribute 'pytmc' := '
pv: DevName
io: i
'}
DevName : T_MaxString; // Component name, used in diagnostic to help narrow down where beam faults are coming from
{attribute 'pytmc' := '
pv: TypeCode
io: i
'}
TypeCode : UINT; // Set at instantiation to fault class code
///////////////////////////////////////////
// Warning, this variable is effectively a VETO of the fast fault if set FALSE
// Do not alter the state of this variable unless you really know what you're doing.
///////////////////////////////////////////
{attribute 'pytmc' := '
pv: InUse
io: i
'}
InUse : BOOL := FALSE; // If this FF is currently in-use
///////////////////////////////////////////
AutoReset : BOOL; // Automatically clear fast fault (latching vs non-latching)
Vetoable : BOOL := TRUE; // Can this fast fault be masked by the veto device input?
{attribute 'pytmc' := '
pv: InfoString
io: i
'}
InfoString : STRING;
END_STRUCT
END_TYPE
ST_FFOverride
TYPE ST_FFOverride :
STRUCT
{attribute 'pytmc' := '
pv: Duration
io: o
'}
Duration : DINT; // DINT to be compatible with EPICS
{attribute 'pytmc' := '
pv: Expiration
io: o
'}
Expiration : DINT; // DINT to be compatible with EPICS
{attribute 'pytmc' := '
pv: StartDT
io: o
'}
StartDT : DINT; // DINT to be compatible with EPICS
{attribute 'pytmc' := '
pv: Activate
io: o
'}
Activate : BOOL;
{attribute 'pytmc' := '
pv: Deactivate
io: o
'}
Deactivate : BOOL;
{attribute 'pytmc' := '
pv: ElapsedTime
io: i
'}
ElapsedTime : DINT; // DINT to be compatible with EPICS
{attribute 'pytmc' := '
pv: RemainingTime
io: i
'}
RemainingTime : DINT; // DINT to be compatible with EPICS
{attribute 'pytmc' := '
pv: Active
io: i
'}
Active: BOOL;
Timer : TP;
OvrdActLogAck : BOOL;
OvrdExpLogAck : BOOL;
tOvrdActivate : R_TRIG;
tOvrdExpiring : F_TRIG;
END_STRUCT
END_TYPE
ST_PMPS_Aperture
TYPE ST_PMPS_Aperture EXTENDS ST_PMPS_Aperture_IO :
STRUCT
(*
{attribute 'pytmc' := 'pv: Width
io: i
field: EGU mm'}
Width : REAL; // distance between horizontal slits (x)
{attribute 'pytmc' := 'pv: Height
io: i
field: EGU mm'}
Height : REAL; // distance between vertical slits (y)
{attribute 'pytmc' := 'pv: OK
io: i'}
xOK : BOOL; // status of aperture, false if error or in motion
*)
END_STRUCT
END_TYPE
ST_PMPS_Attenuator
TYPE ST_PMPS_Attenuator EXTENDS ST_PMPS_Attenuator_IO :
STRUCT
(*
{attribute 'pytmc' := 'pv: Att
io: i
field: EGU %
'}
nTran : UINT;
{attribute 'pytmc' := 'pv: OK
io: i
'}
xAttOK : UINT; // true = no errors and attenuator is stable
*)
END_STRUCT
END_TYPE
T_HashTableEntry
TYPE T_HashTableEntry :
STRUCT
{attribute 'pytmc' := '
pv: Key
io: i
'}
key : DWORD := 0;
value : PVOID := 0;
END_STRUCT
END_TYPE
GVLs
Global_Version
{attribute 'TcGenerated'}
// This function has been automatically generated from the project information.
VAR_GLOBAL CONSTANT
{attribute 'const_non_replaced'}
{attribute 'linkalways'}
stLibVersion_PMPS : ST_LibVersion := (iMajor := 2, iMinor := 2, iBuild := 0, iRevision := 0, sVersion := '2.2.0');
END_VAR
PMPS_GVL
{attribute 'qualified_only'}
VAR_GLOBAL
{attribute 'pytmc' := '
pv: @(PREFIX)RequestedBP
io: i
archive: 1Hz monitor
'}
stRequestedBeamParameters : ST_BeamParams; //Summarized request for the line, as recognized by the line arbiter PLC
{attribute 'pytmc' := '
pv: @(PREFIX)CurrentBP
io: i
archive: 1Hz monitor
'}
stCurrentBeamParameters : ST_BeamParams; //Currently active BP set, broadcast by the line arbiter PLC
{attribute 'pytmc' := '
pv: @(PREFIX)eVRangeCnst
io: i
archive: 1Hz monitor
field: DESC Active eV Range constants
field: EGU eV
'}
g_areVBoundaries : ARRAY [0..g_cBoundaries] OF REAL;
PERange : PE_Ranges; //Included to place the ev ranges properly
END_VAR
VAR_GLOBAL RETAIN
// Statistics
{attribute 'pytmc' := '
pv: @(PREFIX)SuccessfulPreemptions
io: i
'}
SuccessfulPreemption : UDINT; // Any time BPTM applies a new BP request which is confirmed
{attribute 'pytmc' := '
pv: @(PREFIX)AccumulatedFastFaults
io: i
'}
AccumulatedFF : UDINT; // Any time a FF occurs
END_VAR
VAR_GLOBAL CONSTANT
EXCLUDED_ASSERTION_ID : UDINT := 16#FFFFFFFF; //An assertion ID that should always return "not found" in the assertion pool
VISIBLE_TEST_VELOCITY : LREAL := 10;
FAST_TEST_VELOCITY : LREAL := 100;
MAX_DEVICE_STATES : UDINT := 300;
TRANS_SCALING_FACTOR : REAL := REAL#1.0; // Scaling factor for fixed-point transmission
AUX_ATTENUATORS : UINT := 16; // Maximum # of attenuators in the PMPS
MAX_VETO_DEVICES : UINT := 16;
stAttenuators : ST_PMPS_Attenuator :=(nTran:=1,xAttOK:=1);
{attribute 'pytmc' := '
pv: @(PREFIX)FullBeamCnst
io: i
archive: 1Hz monitor
field: DESC Full beam constant
'}
cstFullBeam : ST_BeamParams := (
nTran := 1,
neVRange := 2#1111_1111_1111_1111_1111_1111_1111_1111,
nRate := UDINT#1000000,
astApertures := [(Width:=1E3, Height:=1E3), (Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3)],
astAttenuators := [PMPS_GVL.AUX_ATTENUATORS(stAttenuators)]
);
{attribute 'pytmc' := '
pv: @(PREFIX)0RateBeamCnst
io: i
archive: 1Hz monitor
field: DESC 0-rate beam constant
'}
cst0RateBeam : ST_BeamParams := ( // Use for transition requests
nTran := 1,
neVRange := 2#1111_1111_1111_1111_1111_1111_1111_1111,
nRate := 0,
astApertures := [(Width:=1E3, Height:=1E3), (Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3),(Width:=1E3, Height:=1E3)],
astAttenuators := [PMPS_GVL.AUX_ATTENUATORS(stAttenuators)]
);
(* {attribute 'pytmc' := '
pv: @(PREFIX)SafeBeamCnst
io: i
archive: 1Hz monitor
field: DESC Safe beam constant
'}
cstSafeBeam : ST_BeamParams := (
nTran := 0,
neVRange := 0,
nRate := 0
);*)
cnMaxStateArrayLen : INT := 20;
MAX_APERTURES : UINT := 4; // Maximum # of power slits in the PMPS
{warning disable C0228}
DUMMY_AUX_ATT_ARRAY : ARRAY [1..PMPS_GVL.AUX_ATTENUATORS] OF ST_PMPS_Attenuator;
{warning restore C0228}
g_cBoundaries : INT := 31;
//////////////////////////
// L Undulator constants
//////////////////////////
///////////////////////////////////////
{attribute 'pytmc' := '
pv: @(PREFIX)eVRangeHyst
io: i
archive: 1Hz monitor
field: DESC eV Range hystersis
field: EGU eV
'}
reVHyst: REAL := 5; //
{attribute 'pytmc' := '
pv: @(PREFIX)L:eVRangeCnst
io: i
archive: 1Hz monitor
field: DESC eV Range constants
field: EGU eV
'}
g_areVBoundariesL : ARRAY [0..g_cBoundaries] OF REAL := [
1.0E3,
1.7E3,
2.1E3,
2.5E3,
3.8E3,
4.0E3,
5.0E3,
7.0E3,
7.5E3,
7.7E3,
8.9E3,
10.0E3,
11.1E3,
12.0E3,
13.0E3,
13.5E3,
14.0E3,
16.9E3,
18.0E3,
20.0E3,
22.0E3,
24.0E3,
25.0E3,
25.5E3,
26.0E3,
27.0E3,
28.0E3,
28.5E3,
29.0E3,
30.0E3,
60.0E3,
90.0E3
];
{attribute 'pytmc' := '
pv: @(PREFIX)K:eVRangeCnst
io: i
archive: 1Hz monitor
field: DESC eV Range constants
field: EGU eV
'}
g_areVBoundariesK : ARRAY [0..g_cBoundaries] OF REAL := [
100,
250,
270,
350,
400,
450,
480,
530,
680,
730,
850,
1.10E3,
1.15E3,
1.25E3,
1.45E3,
1.50E3,
1.55E3,
1.65E3,
1.70E3,
1.75E3,
1.82E3,
1.85E3,
2.00E3,
2.20E3,
2.50E3,
2.80E3,
3.00E3,
3.15E3,
3.50E3,
4.00E3,
5.30E3,
7.00E3
];
END_VAR
- Related:
PMPS_PARAM
{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
MAX_FAST_FAULTS : UINT := 50; // Max fast faults for an FFO
MAX_ASSERTIONS : UDINT := 20; //Maximum number of BP requests in the arbiter
TRANS_MARGIN : REAL := REAL#0.05; // Allowed % margin above requested transmission level in SafeBPCompare (0.0500 = 5deci% default). Note: change this value if scaling factor changes.
END_VAR
PMPS_TOOLS
{attribute 'qualified_only'}
VAR_GLOBAL
fbJson : FB_JsonSaxWriter;
END_VAR
POUs
APT_TO_IO
FUNCTION APT_TO_IO : ST_PMPS_Aperture_IO
VAR_INPUT
Apt : ST_PMPS_Aperture;
END_VAR
VAR
END_VAR
APT_TO_IO.Height := Apt.Height;
APT_TO_IO.Width := Apt.Width;
APT_TO_IO.xOK := Apt.xOK;
END_FUNCTION
- Related:
ATT_TO_IO
FUNCTION ATT_TO_IO : ST_PMPS_Attenuator_IO
VAR_INPUT
Att : ST_PMPS_Attenuator;
END_VAR
VAR
END_VAR
ATT_TO_IO.nTran := Att.nTran;
ATT_TO_IO.xAttOK := Att.xAttOK;
END_FUNCTION
- Related:
BeamParameterTransitionManager
(*
Implements the procedure for safely transitioning between device states.
NOTE:
The BPTM will throw an error if the arbiter does not have enough space for the transition and new final assertion.
*)
FUNCTION_BLOCK BeamParameterTransitionManager
VAR_IN_OUT
fbArbiter : FB_Arbiter; //Connect to local arbiter
END_VAR
VAR_INPUT
i_sDeviceName : STRING:='Device'; // Name of the device requesting the transition
i_TransitionAssertionID : UDINT := 0; // Must not be 0 or EXCLUDED_ID
i_stTransitionAssertion : ST_BeamParams := PMPS_GVL.cst0RateBeam; //Assertion required during transition (always safer than anything inbetween)
i_nRequestedAssertionID : UDINT := 0; // Must not be 0 or EXCLUDED_ID
i_stRequestedAssertion : ST_BeamParams := ( nTran := 0, neVRange := 0, nRate := 0);// PMPS_GVL.cstSafeBeam; //Requested assertion, change whenever
i_xMoving : BOOL := FALSE; //Provide rising edge when device begins moving <remove>
i_xDoneMoving : BOOL := FALSE; //Provide rising edge when device is done with a move
stCurrentBeamParameters : ST_BeamParams := PMPS_GVL.cstFullBeam; //Connect to current beam parameters
// Rising edge to cycle back through the BPTM process. Use if something in the process timed out or failed. This will interrupt a current process
bRetry : BOOL := FALSE;
END_VAR
VAR_OUTPUT
q_xTransitionAuthorized : BOOL := FALSE; //Rising edge indicating the device is safe to move, use as input to move execute (which requires a rising edge)
bError : BOOL; // Set if some issue occurs within the bptm
nErrId : UINT; // Set to non-zero to help understand the error.
bDone : BOOL;
bBusy : BOOL;
END_VAR
VAR
nTargetAssertionID : UDINT := 0;
stTargetAssertion : ST_BeamParams; // Target assertion
nCurrentAssertionID : UDINT := 0; // ID of last set state (zero until a state is reached)
xNewBP : BOOL;
xTranBP : BOOL;
xFinalBPInArb : BOOL;
xFinalBP : BOOL;
eBPTMState : E_BPTMState := Init;
ePrevState : E_BPTMState := Init;
xEntry : BOOL;
rTransition : R_TRIG;
xNewTarget : BOOL;
bTransAssertionFailed : BOOL;
bFinalAssertionFailed : BOOL;
LogStrBuffer : ARRAY [0..LogBuffSize] OF STRING;
LogBuffIdx : FB_Index := (LowerLimit:=0, UpperLimit:=LogBuffSize);
nAssrtAttempt : INT; // Number of times we have tried asserting a BP set
rtRetry : R_TRIG;
rtError : R_TRIG;
ffTimeout : FB_FastFault := (
i_Desc := 'Preemptive requests timed out in BPTM',
i_TypeCode := 16#A,
i_xAutoReset := FALSE);
rtDoneMoving : R_TRIG;
bLatchDoneMoving : BOOL;
bFirstMove : BOOL := TRUE;
END_VAR
VAR CONSTANT
LogBuffSize : INT := 40;
cMaxAttempts : INT := 3;
(* The thought here is, a BPTM needs at most 2 arbiter slots to complete a transition.
If we're at capacity, it means some BPTM before this one has begun a transition,
and will require at least one more arbiter spot to complete.
*)
cReqArbCapacity : UDINT := 2;
END_VAR
// Logic for retry button
rtRetry(CLK:=bRetry);
bRetry := FALSE;
// Logic for catching Move Done rising edge
rtDoneMoving(CLK:=i_xDoneMoving);
bLatchDoneMoving S= rtDoneMoving.Q;
// Logic for interrupting BPTM and changing requests
IF nTargetAssertionID <> i_nRequestedAssertionID OR rtRetry.Q THEN
bError := FALSE;
nErrID := 0;
bDone := FALSE;
bBusy := TRUE;
// Execute exit actions before transition back to NewTarget
CASE eBPTMState OF
Transitioning:
DeauthorizeTransition();
//CleaningUp:
END_CASE
eBPTMState := E_BPTMState.NewTarget;
xEntry := eBPTMState <> ePrevState;
END_IF
IF xEntry THEN nAssrtAttempt := 0; END_IF
rtError(CLK:=bError);
IF rtError.Q THEN
eBPTMState := E_BPTMState.Error;
END_IF
// States
CASE eBPTMState OF
NewTarget:
IF xEntry THEN
NewTarget_ENTRY();
ELSIF xNewBP THEN
eBPTMState := E_BPTMState.RequestBP;
ELSE
SetNewTarget();
END_IF
RequestBP:
IF PMPS_PARAM.MAX_ASSERTIONS - fbArbiter.nEntryCount >= cReqArbCapacity THEN
AssertTransitionBP();
AssertFinalBP();
eBPTMState := E_BPTMState.WaitForBP;
ELSE
LogActions('Arbiter at capacity, leaving space for other BPTM to finish.');
nErrId := PMPS_CODES.NoRoomInArb;
bError := TRUE;
END_IF
WaitForBP:
WaitingForTransitionAssertion_DO();
WaitingForFinalAssertion_DO();
IF xTranBP AND xFinalBPInArb THEN eBPTMState := E_BPTMState.Transitioning; END_IF
Transitioning:
IF xEntry THEN
AuthorizeTransition();
ELSIF bLatchDoneMoving OR (bFirstMove AND i_xDoneMoving) THEN
DeauthorizeTransition();
eBPTMState := E_BPTMState.WaitForFinalBP;
END_IF
WaitForFinalBP:
xFinalBP := F_SafeBPCompare(stCurrentBeamParameters, stTargetAssertion);
IF xFinalBP THEN
eBPTMState := E_BPTMState.CleaningUp;
END_IF
CleaningUp:
IF xEntry THEN
RemoveTransitionAssertion();
ELSE
eBPTMState := E_BPTMState.Done;
END_IF
Done:
bDone := TRUE;
bFirstMove := FALSE;
LogActions('Returning to idle');
eBPTMState := E_BPTMState.Idle;
Error:
bError := TRUE;
eBPTMState := E_BPTMState.Idle;
END_CASE
xEntry := ePrevState <> eBPTMState;
ePrevState := eBPTMState;
bDone R= bError;
bBusy R= bError OR bDone;
END_FUNCTION_BLOCK
ACTION AssertFinalBP:
//q_stFinalAssertion := stTargetAssertion;
//Clearing previous target parameters
(* This is considered safe at this step as the transition
state beam parameters should always be safer than the
current or target state beam parameters
*)
IF nCurrentAssertionID <> 0 THEN
LogActions(CONCAT('Removing previous request: ', DWORD_TO_HEXSTR(nCurrentAssertionID, 4, false)) );
fbArbiter.RemoveRequest( nCurrentAssertionID );
END_IF
LogActions(CONCAT('Asserting target req.: ', DWORD_TO_HEXSTR(nTargetAssertionID, 4, false)) );
//Asserting target parameters
bFinalAssertionFailed := NOT fbArbiter.AddRequest(
nTargetAssertionID,
stTargetAssertion,
i_sDeviceName
);
IF bFinalAssertionFailed THEN
LogActions('Assert final failed. ');
nAssrtAttempt := nAssrtAttempt + 1;
IF nAssrtAttempt > cMaxAttempts THEN
bError := TRUE;
nErrID := PMPS_CODES.FinalAssrtFail;
END_IF
ELSE
// Remembering final assertionID for removal later
nCurrentAssertionID := nTargetAssertionID;
END_IF
END_ACTION
ACTION AssertTransitionBP:
LogActions( CONCAT('Removing transition id: ', DWORD_TO_STRING(i_TransitionAssertionID)) );
fbArbiter.RemoveRequest(i_TransitionAssertionID);
LogActions( CONCAT('Requesting transition id: ', DWORD_TO_STRING(i_TransitionAssertionID)) );
bTransAssertionFailed := NOT fbArbiter.AddRequest(
i_TransitionAssertionID,
i_stTransitionAssertion,
i_sDeviceName
);
IF bTransAssertionFailed THEN
LogActions('Assert trans. failed. ');
nAssrtAttempt := nAssrtAttempt + 1;
IF nAssrtAttempt > cMaxAttempts THEN
bError := TRUE;
nErrID := PMPS_CODES.TransAssrtFail;
END_IF
END_IF
END_ACTION
ACTION AuthorizeTransition:
q_xTransitionAuthorized := TRUE;
bLatchDoneMoving R= q_xTransitionAuthorized;
LogActions('Authorizing transition');
END_ACTION
ACTION DeauthorizeTransition:
q_xTransitionAuthorized := FALSE;
LogActions('Deauthorizing transition');
END_ACTION
ACTION NewTarget_ENTRY:
xNewBP := False;
END_ACTION
ACTION RemoveTransitionAssertion:
//q_stTransitionAssertion := PMPS_GVL.cstFullBeam;
//Removing transition assertion
fbArbiter.RemoveRequest( i_TransitionAssertionID );
LogActions('Removing transition req');
//TODO add something to verify removal
END_ACTION
ACTION RequestBP_DO:
// Request BP atomically so we know we'll finish the BPTM cycle
IF fbArbiter.nEntryCount < cReqArbCapacity THEN
LogActions( CONCAT('Removing transition id: ', DWORD_TO_STRING(i_TransitionAssertionID)) );
fbArbiter.RemoveRequest(i_TransitionAssertionID);
LogActions( CONCAT('Requesting transition id: ', DWORD_TO_STRING(i_TransitionAssertionID)) );
bTransAssertionFailed := NOT fbArbiter.AddRequest(
i_TransitionAssertionID,
i_stTransitionAssertion,
i_sDeviceName
);
IF bTransAssertionFailed THEN
LogActions('Assert trans. failed. ');
nAssrtAttempt := nAssrtAttempt + 1;
IF nAssrtAttempt > cMaxAttempts THEN
bError := TRUE;
nErrID := PMPS_CODES.TransAssrtFail;
END_IF
END_IF
ELSE
LogActions('Arbiter at capacity, leaving space for other BPTM to finish.');
END_IF
END_ACTION
ACTION SetNewTarget:
IF F_ValidReqID(i_nRequestedAssertionID) AND
F_ValidReqID(i_TransitionAssertionID) THEN
stTargetAssertion := i_stRequestedAssertion;
nTargetAssertionID := i_nRequestedAssertionID;
LogActions('New target set');
xNewBP := TRUE;
ELSE
IF NOT F_ValidReqID(i_nRequestedAssertionID) THEN
nErrID := PMPS_CODES.BadTargetID;
ELSIF NOT F_ValidReqID(i_TransitionAssertionID) THEN
nErrID := PMPS_CODES.BadTransID;
END_IF
LogActions('Error in set new target');
bError := TRUE;
END_IF
END_ACTION
ACTION WaitingForFinalAssertion_DO:
//Final Assertion Verification
xFinalBPInArb := fbArbiter.CheckRequest( i_nRequestedAssertionID);;
END_ACTION
ACTION WaitingForFinalAssertion_EXIT:
//Set this bool false so we can get a rising edge on the next try
xFinalBP := FALSE;
END_ACTION
ACTION WaitingForTransitionAssertion_DO:
//Transition Assertion Verification
xTranBP := F_SafeBPCompare0Rate(stCurrentBeamParameters, i_stTransitionAssertion) AND
fbArbiter.CheckRequest(i_TransitionAssertionID);
END_ACTION
ACTION WaitingForTransitionAssertion_EXIT:
xTranBP := FALSE;
END_ACTION
BP_TO_IO
FUNCTION BP_TO_IO : ST_BeamParams_IO
VAR_INPUT
BP : ST_BeamParams;
END_VAR
VAR
idx : UINT := 0;
END_VAR
FOR idx := 1 TO PMPS_GVL.AUX_ATTENUATORS DO
BP_TO_IO.astAttenuators[idx] := ATT_TO_IO(BP.astAttenuators[idx]);
END_FOR
FOR idx := 1 TO PMPS_GVL.MAX_APERTURES DO
BP_TO_IO.astApertures[idx] := APT_TO_IO(BP.astApertures[idx]);
END_FOR
BP_TO_IO.aVetoDevices := BP.aVetoDevices;
BP_TO_IO.nTran := BP.nTran;
BP_TO_IO.nCohortInt := ULINT_TO_UDINT(BP.nCohortInt);
BP_TO_IO.neVRange := BP.neVRange;
BP_TO_IO.nRate := BP.nRate;
BP_TO_IO.xValid := BP.xValid;
BP_TO_IO.xValidToggle := BP.xValidToggle;
END_FUNCTION
- Related:
CheckBounds
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckBounds : DINT
VAR_INPUT
index, lower, upper: DINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF index < lower THEN
CheckBounds := lower;
ELSIF index > upper THEN
CheckBounds := upper;
ELSE
CheckBounds := index;
END_IF
{flow}
END_FUNCTION
CheckDivDInt
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckDivDInt : DINT
VAR_INPUT
divisor:DINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF divisor = 0 THEN
CheckDivDInt:=1;
ELSE
CheckDivDInt:=divisor;
END_IF;
{flow}
END_FUNCTION
CheckDivLInt
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckDivLInt : LINT
VAR_INPUT
divisor:LINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF divisor = 0 THEN
CheckDivLInt:=1;
ELSE
CheckDivLInt:=divisor;
END_IF;
{flow}
END_FUNCTION
CheckDivLReal
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckDivLReal : LREAL
VAR_INPUT
divisor:LREAL;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF divisor = 0 THEN
CheckDivLReal:=1;
ELSE
CheckDivLReal:=divisor;
END_IF;
{flow}
END_FUNCTION
CheckDivReal
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckDivReal : REAL
VAR_INPUT
divisor:REAL;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF divisor = 0 THEN
CheckDivReal:=1;
ELSE
CheckDivReal:=divisor;
END_IF;
{flow}
END_FUNCTION
CheckLRangeSigned
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckLRangeSigned : LINT
VAR_INPUT
value, lower, upper: LINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF (value < lower) THEN
CheckLRangeSigned := lower;
ELSIF(value > upper) THEN
CheckLRangeSigned := upper;
ELSE
CheckLRangeSigned := value;
END_IF
{flow}
END_FUNCTION
CheckLRangeUnsigned
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckLRangeUnsigned : ULINT
VAR_INPUT
value, lower, upper: ULINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF (value < lower) THEN
CheckLRangeUnsigned := lower;
ELSIF(value > upper) THEN
CheckLRangeUnsigned := upper;
ELSE
CheckLRangeUnsigned := value;
END_IF
{flow}
END_FUNCTION
CheckPointer
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckPointer : POINTER TO BYTE
VAR_INPUT
ptToTest : POINTER TO BYTE; // Destination address of the pointer
iSize : DINT; // Size of the type the pointer points to. (e.g: 20 for POINTER TO ARRAY [0..9] OF INT)
iGran : DINT; // Granularity of the pointer access. This is the size of the biggest non-structured data type in the type the pointer points to. (e.g: 2 for POINTER TO ARRAY [0..9] OF INT)
bWrite: BOOL; // Indicates read or write Access. TRUE = write access.
END_VAR
// No standard way of implementation. Fill your own code here
CheckPointer := ptToTest;
{flow}
END_FUNCTION
CheckRangeSigned
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckRangeSigned : DINT
VAR_INPUT
value, lower, upper: DINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF (value < lower) THEN
CheckRangeSigned := lower;
ELSIF(value > upper) THEN
CheckRangeSigned := upper;
ELSE
CheckRangeSigned := value;
END_IF
{flow}
END_FUNCTION
CheckRangeUnsigned
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckRangeUnsigned : UDINT
VAR_INPUT
value, lower, upper: UDINT;
END_VAR
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF (value < lower) THEN
CheckRangeUnsigned := lower;
ELSIF(value > upper) THEN
CheckRangeUnsigned := upper;
ELSE
CheckRangeUnsigned := value;
END_IF
{flow}
END_FUNCTION
F_AttenuatorOK
(*
Indicates if the attenuator can be considered safe for a device,
given its status word.
In local mode, the atteunator ignores PMPS requests, following a local
setpoint. In this mode, a device cannot trust that the attenuator will not
increase transmission beyond its own setpoint. Therefore any
indication of a changing transmission should cause a fault.
In PMPS mode, if a device has submitted a preemptive request and received
acknowledgement, then the transmission setpoint will be governed at least
by that device's setpoint. Therefore, the attenuator may be moving without
generating a fault.
*)
FUNCTION F_AttenuatorOK : BOOL
VAR_INPUT
xStatus : WORD;
END_VAR
VAR
END_VAR
IF xStatus.2 THEN // Local mode
F_AttenuatorOK := xStatus.0 and NOT xStatus.1;
ELSIF xStatus.3 AND NOT xStatus.2 THEN // PMPS mode
F_AttenuatorOK := TRUE;
END_IF
F_AttenuatorOK := xStatus.4 AND F_AttenuatorOK;
END_FUNCTION
F_BPWithID
FUNCTION F_BPWithID : ST_BP_ArbInternal
VAR_INPUT
BP : REFERENCE TO ST_BeamParams;
ID : DWORD;
END_VAR
VAR
BpInt : ST_BP_ArbInternal;
END_VAR
MEMCPY(ADR(F_BPWithID), ADR(BP), SIZEOF(BP));
F_BPWithID.nId := ID;
END_FUNCTION
- Related:
F_CalculatePhotonEnergy
FUNCTION F_CalculatePhotonEnergy : LREAL
VAR_INPUT
(* Electron energy in GeV *)
fElectronEnergy_GeV : LREAL;
(* Undulator period in mm *)
fUndulatorPeriod_mm : LREAL;
(* Unitless undulator K parameter / strength *)
fUndulatorStrength : LREAL;
END_VAR
VAR
fDenominator: LREAL;
END_VAR
VAR CONSTANT
(* GeV [electron rest mass/energy] *)
m_e : LREAL := 0.0005109989461;
(* Js [Planck's constant] *)
h : LREAL := 6.62607004E-34;
(* C [electron charge] *)
e : LREAL := 1.6021766208E-19;
(* m/s, speed of light *)
c : LREAL := 299792458;
END_VAR
(*
Reference Python implementation
def calculate_photon_energy(electron_energy, period, k):
'''
Calculate photon energy, in eV.
Parameters
----------
electron_energy : float
Electron energy in GeV
period : float
Undulator period, in mm
k : float
Undulator K parameter (strength), unitless
'''
m_e = 0.0005109989461 # GeV [electron rest mass/energy]
h = 6.62607004e-34 # Js [Planck's constant]
e = 1.6021766208e-19 # C [electron charge]
c = 299792458 # m/s, speed of light
return ((2. * (electron_energy / m_e) ** 2 * h * c) /
(e * period * 1e-3 * (1 + k ** 2 / 2.)))
*)
fDenominator := (e * fUndulatorPeriod_mm * 1E-3 * (1.0 + EXPT(fUndulatorStrength, 2.0) / 2.0));
F_CalculatePhotonEnergy := (2.0 * EXPT(fElectronEnergy_GeV / m_e, 2.0) * h * c) / MAX(fDenominator, 4.94065645841247E-324);
END_FUNCTION
F_DeviceState_To_DeviceStateExt
FUNCTION F_DeviceState_To_DeviceStateExt : ST_DeviceStateExt
VAR_INPUT
stDeviceState : ST_DeviceState;
xValid : BOOL;
END_VAR
VAR
END_VAR
F_DeviceState_To_DeviceStateExt.nStateRef := stDeviceState.nStateRef;
F_DeviceState_To_DeviceStateExt.rPosition := stDeviceState.rPosition;
F_DeviceState_To_DeviceStateExt.rTolerance := stDeviceState.rTolerance;
F_DeviceState_To_DeviceStateExt.sStateName := stDeviceState.sStateName;
F_DeviceState_To_DeviceStateExt.stReqBeamParam := stDeviceState.stReqBeamParam;
F_DeviceState_To_DeviceStateExt.xValid := xValid;
END_FUNCTION
- Related:
F_DifferentBeamParams
(*Compares BeamParam1 to BeamParam2, if any parameters of BeamParam1 are different than BeamParam2
the result will be true. *)
FUNCTION F_DifferentBeamParams : BOOL
VAR_INPUT
BeamParam1 : ST_BeamParams;
BeamParam2 : ST_BeamParams;
END_VAR
VAR
xAttOK: BOOL := FALSE;
xPPmjOK: BOOL := FALSE;
xEvOK: BOOL := FALSE;
xRateOK: BOOL := FALSE;
xaStopper : BOOL := FALSE;
xaAtt : BOOL := FALSE;
xaApt : BOOL := FALSE;
idx : UINT;
END_VAR
xAttOK := BeamParam1.nTran <> BeamParam2.nTran;
xEvOK := BeamParam1.neVRange <> BeamParam2.neVRange;
xRateOK := BeamParam1.nRate <> BeamParam2.nRate;
//ast Attenuators
FOR idx:=1 TO PMPS_GVL.AUX_ATTENUATORS DO
xaAtt S= (BeamParam1.astAttenuators[idx].nTran <> BeamParam2.astAttenuators[idx].nTran);
END_FOR
// Stoppers
FOR idx:=1 TO PMPS_GVL.MAX_VETO_DEVICES DO
xaStopper S= (BeamParam1.aVetoDevices[idx] <> BeamParam2.aVetoDevices[idx]);
END_FOR
// ast Apertures
FOR idx:=1 TO PMPS_GVL.MAX_APERTURES DO
xaApt S= (BeamParam1.astApertures[idx].Height <> BeamParam2.astApertures[idx].Height) OR
(BeamParam1.astApertures[idx].Width <> BeamParam2.astApertures[idx].Width);
END_FOR
F_DifferentBeamParams := xAttOK OR xPPmjOK OR xEvOK OR xRateOK OR xaStopper OR xaAtt OR xaApt;
END_FUNCTION
- Related:
F_eVExcludeRange
FUNCTION F_eVExcludeRange : DWORD
(*
Given an lower and upper end of an exclusion range, return the corresponding eV bitmask.
eVs between fLower and fUpper will be considered unsafe, and eVs outside of this range will be
considered safe, with the exception of the endpoints and values near the endpoints if they land
far from an eV boundary.
Call this in your init cycle to set up your eV bitmasks for more readable code
that is also more resiliant to eV range definition adjustments.
*)
VAR_INPUT
fLower: REAL;
fUpper: REAL;
END_VAR
F_eVExcludeRange := F_eVIncludeRange(0, fLower) OR F_eVIncludeRange(fUpper, PMPS_GVL.g_areVBoundaries[PMPS_GVL.g_cBoundaries]);
END_FUNCTION
- Related:
F_eVIncludeRange
FUNCTION F_eVIncludeRange : DWORD
(*
Given an lower and upper end of an inclusion range, return the corresponding eV bitmask.
eVs between fLower and fUpper will be considered safe, with the exception of the endpoints and values
near the endpoints if they land far from an eV boundary.
Call this in your init cycle to set up your eV bitmasks for more readable code
that is also more resiliant to eV range definition adjustments.
*)
VAR_INPUT
fLower: REAL;
fUpper: REAL;
END_VAR
VAR
nBitmask: DWORD := 0;
nIndex: INT;
nBit: USINT := 0;
fPrev: REAL := 0;
END_VAR
FOR nIndex := 0 TO PMPS_GVL.g_cBoundaries DO
IF fLower <= fPrev AND fUpper >= PMPS_GVL.g_areVBoundaries[nIndex] THEN
nBitmask := nBitmask + SHL(1, nBit);
END_IF
fPrev := PMPS_GVL.g_areVBoundaries[nIndex];
nBit := nBit + 1;
END_FOR
F_eVIncludeRange := nBitmask;
END_FUNCTION
- Related:
F_eVRangeCalculator
(*
A. Wallace 2019-8-8
Provides bit range of eV.
LastWord = 0 (Result)
----------------------------------
reV = 300 = (0000_0000_0000_0010)
reV = 301 = (0000_0000_0000_0100)
LastWord = 0000_0000_0000_0010 (Result)
----------------------------------
reV = 300 + <rHyst = (0000_0000_0000_0110)
reV = 300 + >rHyst = (0000_0000_0000_0100)
*)
FUNCTION F_eVRangeCalculator : DWORD // Bit-range of current photon energy
VAR_INPUT
reV : REAL; // Photon energy (keV)
LastWord : DWORD; // Range word of previous cycle
END_VAR
VAR
nIndex: INT;
RangeWord: DWORD;
rPreviousBoundary: REAL := 0;
MaxEv : REAL := PMPS_GVL.g_areVBoundaries[PMPS_GVL.g_cBoundaries];
Boundaries : ARRAY [0..PMPS_GVL.g_cBoundaries] OF REAL;
END_VAR
{IF defined (L)}
Boundaries := PMPS_GVL.g_areVBoundariesL;
{ELSIF defined (K)}
Boundaries := PMPS_GVL.g_areVBoundariesK;
{END_IF}
MaxEv := Boundaries[PMPS_GVL.g_cBoundaries];
IF reV <= 0 OR reV > MaxEv THEN F_eVRangeCalculator := 16#FFFF_FFFF; RETURN; END_IF // Failsafe
FOR nIndex := 0 TO PMPS_GVL.g_cBoundaries DO
// If previously active then check if it should be cleared
IF LastWord.0 THEN
// Hysteresis condition
IF (reV > MIN(Boundaries[nIndex] + PMPS_GVL.reVHyst, MaxEv) ) OR
(reV < MAX(rPreviousBoundary - PMPS_GVL.reVHyst, 0) ) THEN
RangeWord.0 := 0; // If out of range of hys. mark inactive
ELSE
RangeWord.0 := 1;
END_IF
ELSE // Check if we should mark as active
IF (reV <= Boundaries[nIndex]) AND
(reV > rPreviousBoundary) OR
(nIndex = 0 AND reV = 0 ) THEN
RangeWord.0 := 1;
END_IF
END_IF
// Catch if the hysteresis is set too large for the range
IF rPreviousBoundary + PMPS_GVL.reVHyst >= Boundaries[nIndex] THEN
RangeWord.0 := 1; // Setting the bit true here as a failsafe
//<TODO> Add major error or warning here
END_IF
rPreviousBoundary := Boundaries[nIndex];
// Shift to next bit
RangeWord := ROR(RangeWord, 1);
LastWord := SHR(LastWord, 1);
END_FOR
F_eVRangeCalculator := RangeWord;
END_FUNCTION
- Related:
F_eVWithinSpec
//reV must be within permitted range.
FUNCTION F_eVWithinSpec : BOOL
VAR_INPUT
reV : REAL; //Photon energy to check if within permitted range
nPermittedRange : DWORD; //Permitted range
END_VAR
VAR
//Holding register for permitted ranges
nPermittedRangeHolding : DWORD;
//For loop counter
nIndex : INT;
// Lower boundary for range check
rPreviousBoundary: REAL := 0;
Boundaries: ARRAY [0..PMPS_GVL.g_cBoundaries] OF REAL;
END_VAR
(* How this works:
The within range bool is initialized to false.
Load the word representation of ranges into a holding register.
If the bit at position zero of the holding register is true, and the input eV is
below the boundary at the index position of the boundary array, and the previous boundary
(which initializes at 0), then the within range bool is set.
Then, we stash the upper boundary as the lower boundary for the next comparison,
and shift the holding register right, which moves the next bit to the zero position.
*)
{IF defined (L)}
Boundaries := PMPS_GVL.g_areVBoundariesL;
{ELSIF defined (K)}
Boundaries := PMPS_GVL.g_areVBoundariesK;
{END_IF}
nPermittedRangeHolding := nPermittedRange;
FOR nIndex := 0 TO PMPS_GVL.g_cBoundaries DO
F_eVWithinSpec S= nPermittedRangeHolding.0 AND(
(reV <= Boundaries[nIndex]) AND
(reV >= rPreviousBoundary) );
IF F_eVWithinSpec THEN RETURN; END_IF
rPreviousBoundary := Boundaries[nIndex];
nPermittedRangeHolding := SHR(nPermittedRangeHolding, 1);
END_FOR
END_FUNCTION
- Related:
F_InitTestBP
// Establishes a BP set for testing.
// Specifically this means a BP that has all the status bits set to an OK state
FUNCTION F_InitTestBP : ST_BeamParams
VAR_INPUT
BP : ST_BeamParams;
END_VAR
VAR
END_VAR
END_FUNCTION
- Related:
F_PMPS_JSON
FUNCTION F_PMPS_JSON : STRING
VAR_INPUT
sDevName : STRING := '';
sPath : STRING := '';
nTypeCode : UINT := 0;
END_VAR
VAR
END_VAR
PMPS_TOOLS.fbJson.StartObject();
PMPS_TOOLS.fbJson.AddKey('pmps_typecode');
PMPS_TOOLS.fbJson.AddUdint(nTypeCode);
PMPS_TOOLS.fbJson.AddKey('pmps_path');
PMPS_TOOLS.fbJson.AddString(sPath);
PMPS_TOOLS.fbJson.AddKey('pmps_device_name');
PMPS_TOOLS.fbJson.AddString(sDevName);
PMPS_TOOLS.fbJson.EndObject();
F_PMPS_JSON := PMPS_TOOLS.fbJson.GetDocument();
PMPS_TOOLS.fbJson.ResetDocument();
END_FUNCTION
- Related:
F_SafeBPCompare
(*Compares BeamParam1 to BeamParam2: if the parameters of BeamParam1 are more conservative than BeamParam2
the result will be true. *)
// Does not consider status. Status must be evaluated elsewhere.
FUNCTION F_SafeBPCompare : BOOL
VAR_INPUT
BeamParam1 : ST_BeamParams; //Must be more conservative than 2
BeamParam2 : ST_BeamParams;
END_VAR
VAR
// Internal Attenuation OK boolean
xAttOK: BOOL := FALSE;
// Internal Per-pulse energy OK boolean
xPPmjOK: BOOL := FALSE;
// Internal photon energy OK boolean
xEvOK: BOOL := FALSE;
// Internal Beam Rate OK boolean
xRateOK: BOOL;
idx : UINT;
xAuxAttOK : BOOL := TRUE;
xAuxAprtOK : BOOL := TRUE;
Att1 : REFERENCE TO ST_PMPS_Attenuator;
Att2 : REFERENCE TO ST_PMPS_Attenuator;
END_VAR
xAttOK := BeamParam1.nTran <= MIN(PMPS_GVL.TRANS_SCALING_FACTOR, (BeamParam2.nTran *(PMPS_GVL.TRANS_SCALING_FACTOR+PMPS_PARAM.TRANS_MARGIN))/PMPS_GVL.TRANS_SCALING_FACTOR );
xEvOK := (BeamParam1.neVRange AND BeamParam2.neVRange) = BeamParam1.neVRange;
xRateOK := BeamParam1.nRate <= BeamParam2.nRate;
FOR idx:=1 to PMPS_GVL.AUX_ATTENUATORS DO
Att1 REF= BeamParam1.astAttenuators[idx];
Att2 REF= BeamParam2.astAttenuators[idx];
xAuxAttOK R= ( Att1.nTran > MIN( PMPS_GVL.TRANS_SCALING_FACTOR, ( (Att2.nTran *(PMPS_GVL.TRANS_SCALING_FACTOR+PMPS_PARAM.TRANS_MARGIN))/PMPS_GVL.TRANS_SCALING_FACTOR ) ) );
END_FOR
FOR idx:=1 to PMPS_GVL.MAX_APERTURES DO
xAuxAprtOK R= BeamParam1.astApertures[idx].Height > BeamParam2.astApertures[idx].Height OR
BeamParam1.astApertures[idx].Width > BeamParam2.astApertures[idx].Width;
END_FOR
F_SafeBPCompare := xAttOK AND xEvOK AND xRateOK AND xAuxAprtOK AND xAuxAttOK;
END_FUNCTION
F_SafeBPCompare0Rate
(*Compares BeamParam1 to BeamParam2: if the parameters of BeamParam1 are more conservative than BeamParam2
the result will be true. *)
(* 0-rate means this function will return true regardless of other conditions if the beam rate
is zero *)
FUNCTION F_SafeBPCompare0Rate : BOOL
VAR_INPUT
BeamParam1 : ST_BeamParams; //Must be more conservative than 2
BeamParam2 : ST_BeamParams;
END_VAR
VAR
// Beam-rate is zero, masks all others considerations.
xZeroRate: BOOL;
END_VAR
xZeroRate := BeamParam1.nRate = 0;
F_SafeBPCompare0Rate := xZeroRate OR F_SafeBPCompare(BeamParam1, BeamParam2);
END_FUNCTION
- Related:
F_SetBeamParams
FUNCTION F_SetBeamParams : ST_BeamParams
VAR_INPUT
nTran : REAL := 0;
neVRange : DWORD := 0;
nRate : UDINT := 0;
astAuxAtt : ARRAY [1..PMPS_GVL.AUX_ATTENUATORS] OF ST_PMPS_Attenuator;
END_VAR
VAR
BeamParams : ST_BeamParams;
END_VAR
BeamParams.nTran := LIMIT(0,nTran,PMPS_GVL.TRANS_SCALING_FACTOR);
BeamParams.astAttenuators := astAuxAtt;
BeamParams.neVRange := neVRange;
BeamParams.nRate := MIN(nRate,1000000);
F_SetBeamParams := BeamParams;
END_FUNCTION
- Related:
F_SetGoodAttStatus
//Use for testing purposes to set the attenuator status to a good state
FUNCTION F_SetGoodAttStatus : UINT
VAR_INPUT
xStatus : UINT;
END_VAR
VAR
END_VAR
xStatus.0 := 1;
xStatus.3 := 1;
xStatus.4 := 1;
END_FUNCTION
F_SetStateParams
FUNCTION F_SetStateParams : BOOL;
VAR_INPUT
nStateRef : UDINT;
rPosition : REAL;
rTolerance: REAL;
stBeamParams : ST_BeamParams;
END_VAR
VAR_IN_OUT
Table : FB_LinearDeviceStateTable;
END_VAR
VAR
stDeviceState : ST_DeviceState;
END_VAR
stDeviceState.nStateRef := nStateRef;
stDeviceState.rPosition := rPosition;
stDeviceState.rTolerance := rTolerance;
stDeviceState.stReqBeamParam := stBeamParams;
Table.A_Add(
key := nStateRef,
putValue := stDeviceState
);
F_SetStateParams := Table.bOk;
END_FUNCTION
F_ValidReqID
FUNCTION F_ValidReqID : BOOL
VAR_INPUT
ReqID : DWORD;
END_VAR
VAR
END_VAR
F_ValidReqID := ReqID <> PMPS_GVL.EXCLUDED_ASSERTION_ID AND ReqID <> 0;
END_FUNCTION
- Related:
FB_AcceleratorLinks
FUNCTION_BLOCK FB_AcceleratorLinks
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: eEnrg:Hgvpu
link: BEND:DMPH:400:BACT
field: EGU GeV
'}
fbHgvpuElectronEnergy : FB_LREALFromEPICS;
{attribute 'pytmc' := '
pv: eEnrg:SXU
link: BEND:DMPS:400:BACT
field: EGU GeV
'}
fbSxuElectronEnergy : FB_LREALFromEPICS;
END_VAR
fbHgvpuElectronEnergy();
fbSxuElectronEnergy();
END_FUNCTION_BLOCK
FB_Arbiter
(* FB Arbiter
A. Wallace 2020-6-26
The arbiter primary objectives are:
- Provide a simple interface for devices to request beam parameter sets
- Provide a way for devices to verify their BPS is active in the arbiter
- Provide a way for devices remove their requests from evaluation
- Evaluate all active beam parameter requests registered with the aribiter,
to determine the safest combination of all sets, provide this set as an output.
- Do all of this with minimal overhead
To these ends, the arbiter uses a hash-table, the rows being a state-id as the key, and a corresponding
beam parameter set to be evaluated against all the other sets (or rows), in the table.
The hash table can be thought of as an array on steriods, they are worth reading about, suffice to say
the hash table will tell you when you reach the end of all the entries, and enables us to find entries quickly.
These features efficiently address the addition, removal, and verification of beam parameter sets listed in the above requirements.
*)
{attribute 'reflection'}
FUNCTION_BLOCK FB_Arbiter IMPLEMENTS I_HigherAuthority, I_LowerAuthority
VAR
nRequestsCount : UDINT; // How many requests are currently in the arbiter
{attribute 'pytmc' := '
pv: AP
io: i
field: DESC Assertion Pool
'}
fbBPAssertionPool : FB_BeamParamAssertionPool; //Table of active beam parameter assertions
xRequestMade : BOOL; // Arbiter has confirmed its request has made it into the beam parameter request
{attribute 'pytmc' := '
pv: ArbiterID
io: i
field: DESC Arbiter ID for elev. req.
'}
nArbiterID : UDINT; // Arbiter ID, used for making higher-level BP requests
nNextCohort : UDINT := 1; // The cohort ID any new requests will adopt, will become ReqInProgCohort at the start of the next acknowledgement cycle
nAckInProgCohort : UDINT := 0; // The cohort ID currently being acknowledged, will become nActiveCohort after acknowledgement from HA
{attribute 'pytmc' := '
pv: CohortCounter
io: i
field: DESC Intrnl cohort counter
'}
nActiveCohort : UDINT := 0; // Requests with cohorts <= to this value will be considered active in CheckRequest
bStartNewAckRequest : BOOL; // Set by an add or remove method call, triggers an ack cycle
bAckInProgress : BOOL; // Set by ElevateReq when there is a new ack request and reset when the ack cycle is complete
// The following IDs are set by the last BP request that was most conservative
// ie. other requests may limit the parameter, but this one is the most recent to limit it
idTransmission : DWORD; // ID of BP limiting transmission
idRate : DWORD; // ID of BP limiting rate
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
sArbName : T_MaxString;
InfoStringFmtr : FB_FormatString;
bVerbose : BOOL := FALSE;
END_VAR
VAR_INPUT
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv: ArbitratedBP
io: i
field: DESC Arbitrated BP
'}
q_stBeamParams : ST_BeamParams; //Updated on each cycle of the arbiter FB with the current arbitrated beam parameter set
q_xStateIDFound : BOOL; //Set true if a state-id is found in the assertion pool after calling A_VerifyAssertion
END_VAR
END_FUNCTION_BLOCK
- Related:
FB_Arbiter_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_Arbiter_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
Arbitration();
RequestCheckRemoveBP();
FullArbitrationStack();
StackArbiters();
RemoveReq();
DoS();
END_FUNCTION_BLOCK
FB_ArbiterToSubSys_IO
(* Exchanges preemption requests and current beam state with subsystems in arbiter
network.
2019-12-2, A. Wallace
Use in conjunction with FB_SubSysToArbiter_IO.
This FB runs in the beamline arbiter PLC (BAP). There should be one of these for every
subsystem interfaced to the BAP. Each subsystem PLC should run a sibling FB_SubSysToArbiter_IO FB.
This FB and its subordinate communicate through the ethercat connection between the subsystem PLC and the BAP.
A beam parameter set request is sent from the subsystem to this PLC, and includes a cohort number.
This cohort number (i_RequestedBP.nCohortInt) is compared to a local cohort number (nRequestedCohort).
A request from the subsystem to update or modify its current request is indicated by an increase in the
i_RequestedBP.nCohortInt value. Then this function block updates the local arbiter request for the subsystem
and awaits confirmation. Once the new request is confirmed in the preemptive system this function block increments
the ActiveCohort value to match the nRequestedCohort. ActiveCohort is also relayed back to the subsystem
as the active cohort value in the o_CurrentBP set. The subsystem then knows its request has been heard,
and can carry out its own acknowledgement to its various requestors.
Things to note:
If a subsystem has any new or changed preemptive requests you should see the cohort value increment a bit.
If the cohort number is incrementing out of control, it usually means the subsysetm has some preemptive
request loop (two arbiters elevating to each other), or there is a RequestAdd, RequestRemove loop.
*)
FUNCTION_BLOCK FB_ArbiterToSubSys_IO
VAR_INPUT
RequestingSystemID : DWORD := PMPS_GVL.EXCLUDED_ASSERTION_ID; // System ID for making requests to the supreme arbiter
sName : STRING := 'ArbiterToSubSys';
Reset : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter;
fbFFHWO : FB_HardwareFFOutput;
END_VAR
VAR
i_RequestedBP AT %I* : ST_BeamParams_IO; // Requested BP from subsystem
{attribute 'pytmc' := '
pv: RequestedBP:FromSubSys
'}
RequestedBP: ST_BeamParams;
o_CurrentBP AT %Q* : ST_BeamParams_IO; // Current BP set
// EL6692 Diagnostics
i_Connected AT %I* : BOOL; // SYNC Inputs^External device not connected !!! Doesn't really work.
{attribute 'pytmc' := 'pv: WcState
io: i
field: DESC Working counter state.
field: ZNAM OK
field: ONAM Error'}
i_WcState AT %I* : BOOL := TRUE; // WcState^WcState
{attribute 'pytmc' := 'pv: TxPDO_state
io: i
field: DESC PDO Transmission is OK
field: ZNAM OK
field: ONAM Error'}
i_TxPDOState AT %I* : BOOL := TRUE; // SYNC Inputs^TxPDO state
{attribute 'pytmc' := 'pv: TxPDO_toggle
io: i
field: DESC PDO Transmission is OK
field: ZNAM OK
field: ONAM Error'}
i_TxPDOToggle AT %I* : BOOL; // TxPDO toggle
// Fast faults
ffPMPSIO_Disconnect : FB_FastFault := (i_Desc:='Issue w/ arbiter interface to subsystem, verify subsystem is OK, or ethercat connection.'); // Fast fault for ethercat issues
nActiveCohort : UDINT := 0; // Active cohort. Updated to the req. number from the sub system after request is confirmed.
nRequestedCohort : UDINT := 0;
rtToggle : R_TRIG;
END_VAR
// Incoming request from subsystem PLC
RequestedBP := IO_TO_BP(i_RequestedBP);
// Update Arbiter with our request
rtToggle(CLK:= i_RequestedBP.xValid); // If interface resumes, reset/catch up
// xValid will go false if the PLC program on the other side is stopped. Ironically the ethercat interface continues happily.
// If subsystem cohort has incremented, update beam parameter request with our arbiter
IF rtToggle.Q THEN
nActiveCohort := 0;
END_IF
IF i_RequestedBP.nCohortInt > nRequestedCohort OR rtToggle.Q THEN
Arbiter.RemoveRequest(RequestingSystemID);
Arbiter.AddRequest(RequestingSystemID, RequestedBP,sName);
nRequestedCohort := i_RequestedBP.nCohortInt;
END_IF
// Check if latest request is acknowledged by this arbiter
// Acknowledgement happens after an arbitration cycle, which means if RequestedCohort just incremented
// due to a new request, CheckRequest will be false.
IF Arbiter.CheckRequest(RequestingSystemID) THEN
nActiveCohort := nRequestedCohort; // If it is, then the active cohort is set to the requested cohort number.
END_IF
// Sending current beam parameter set to subsystems
o_CurrentBP := BP_TO_IO(PMPS_GVL.stCurrentBeamParameters);
o_CurrentBP.nCohortInt := nActiveCohort;
//////////////////////////////////////
// Verifying the ethercat interface health
/////////////////////////////////////
// Both of these values should be 0 for a fully valid exchange
ffPMPSIO_Disconnect.i_xOK := NOT i_WcState AND NOT i_TxPDOState;
ffPMPSIO_Disconnect(
io_fbFFHWO := fbFFHWO,
i_xReset := Reset,
i_DevName := sName,
i_TypeCode:=5);
END_FUNCTION_BLOCK
FB_ArbToSubsys_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_ArbToSubsys_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
BasicFunction();
END_FUNCTION_BLOCK
FB_AttenuatorSimulator
FUNCTION_BLOCK FB_AttenuatorSimulator
VAR_INPUT
i_rRequestedAttenuation : REAL;
i_stBPReadback : ST_BeamParams;
END_VAR
VAR_OUTPUT
q_xFault : BOOL;
q_rCurrentAttenuation : REAL;
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter; //Higher level arbiter from which upstream attenuation can be requested
END_VAR
VAR
tonDelay : TON := (
PT := T#3S
);
END_VAR
// Really basic delay
tonDelay(IN := i_rRequestedAttenuation <> q_rCurrentAttenuation);
IF tonDelay.Q THEN
q_rCurrentAttenuation := i_rRequestedAttenuation;
END_IF
END_FUNCTION_BLOCK
- Related:
FB_BeamClassOutputs
(*
Sets the beam class assertion lines for a given beam class.
*)
FUNCTION_BLOCK FB_BeamClassOutputs
VAR_INPUT
BP : ST_BeamParams;
END_VAR
VAR_OUTPUT
END_VAR
VAR
nBeamClass : BYTE;
wBeamClass : BYTE;
InitCounter: BYTE;
counter : INT;
// Beam class lines are restricted to 8 channels in the current design, since
// there are no plans to use all 16. Channels 1-7 may be allocated to any other
// beam classes so long as they are ordered least to greatest.
// Channel 8 is reserved for full beam.
{attribute 'pytmc' := 'pv: BeamClassChannel
io: i
field: DESC Hardwire channel state'}
epicsBitmap : WORD;
{attribute 'TcLinkTo' := '[1] := TIIB[PMPS_Premp]^Channel 1^Output;
[2] := TIIB[PMPS_Premp]^Channel 2^Output;
[3] := TIIB[PMPS_Premp]^Channel 3^Output;
[4] := TIIB[PMPS_Premp]^Channel 4^Output;
[5] := TIIB[PMPS_Premp]^Channel 5^Output;
[6] := TIIB[PMPS_Premp]^Channel 6^Output;
[7] := TIIB[PMPS_Premp]^Channel 7^Output;
[8] := TIIB[PMPS_Premp]^Channel 8^Output;'} // 8 - Full beam
q_BC_ASSERTION_LINES AT %Q* : ARRAY [1..MAX_BEAM_CLASS_LINES] OF BOOL;
END_VAR
VAR CONSTANT
MAX_BEAM_CLASS_LINES : BYTE := 8;
BC_1HZ : BYTE := 1;
BC_10HZ : BYTE := 2;
BC_FULL : BYTE := 16;
END_VAR
// Determine BC
IF BP.nRate >= 120 THEN
nBeamClass := BC_FULL;
ELSIF BP.nRate >= 10 THEN
nBeamClass := BC_10HZ;
ELSIF BP.nRate >= 1 THEN
nBeamClass := BC_1HZ;
ELSE
nBeamClass := 0;
END_IF
//Assert Beam Class
////////////////////////////////////
//0x0 = 0000 0000 0000 0000
//0x1 = 0000 0000 0000 0001
//0xF = 1111 1111 1111 1111
//Initialize BC lines to zero on every pass
FOR InitCounter := 1 TO MAX_BEAM_CLASS_LINES DO
q_BC_ASSERTION_LINES[InitCounter] := FALSE;
END_FOR
//Set BC lines according to beam class
//A BC of 0x0 would pass over this loop, setting none of the lines high
// , as FOR loops check the initialized variable at the top to see if it's >
// than the "TO" variable.
FOR wBeamClass:=1 TO MIN(MAX_BEAM_CLASS_LINES-1, nBeamClass) DO
q_BC_ASSERTION_LINES[wBeamClass] := TRUE;
END_FOR
q_BC_ASSERTION_LINES[8] := nBeamClass = 16; //Set channel 8 true if BC is 16
// Readbacks for EPICS
epicsBitmap.0 := q_BC_ASSERTION_LINES[1];
epicsBitmap.1 := q_BC_ASSERTION_LINES[2];
epicsBitmap.2 := q_BC_ASSERTION_LINES[3];
epicsBitmap.3 := q_BC_ASSERTION_LINES[4];
epicsBitmap.4 := q_BC_ASSERTION_LINES[5];
epicsBitmap.5 := q_BC_ASSERTION_LINES[6];
epicsBitmap.6 := q_BC_ASSERTION_LINES[7];
epicsBitmap.7 := q_BC_ASSERTION_LINES[8];
END_FUNCTION_BLOCK
- Related:
FB_BeamParamAssertionPool
(* This function block implements simple database. Data element values are stored in the hash table. *)
FUNCTION_BLOCK FB_BeamParamAssertionPool
VAR_INPUT
key : DWORD := 0;(* Entry key: used by A_Lookup, A_Remove method, the key variable is also used by A_Add method *)
putPosPtr : POINTER TO T_HashTableEntry := 0;(* Hash table entry position pointer (used by A_Find, A_GetNext, A_GetPrev) *)
putValue : ST_BP_ArbInternal;(* Hash table entry value (used by A_AddHead, A_AddTail, A_Find )*)
END_VAR
VAR_OUTPUT
bOk : BOOL := FALSE;(* TRUE = Success, FALSE = Failed *)
getPosPtr : POINTER TO T_HashTableEntry := 0;(* Returned hash table entry position pointer *)
getValue : ST_BP_ArbInternal;(* Returned hash table entry value *)
nCount : UDINT := 0;(* Hash table size (number of used entries, used by A_Count) *)
END_VAR
VAR
{attribute 'pytmc' := '
pv: Entry
io: i
'}
epicsDataPool : ARRAY[1..PMPS_PARAM.MAX_ASSERTIONS] OF ST_BP_ArbInternal;(* Structured data element pool for display in EPICS*)
dataPool : ARRAY[0..PMPS_PARAM.MAX_ASSERTIONS*3] OF ST_BP_ArbInternal;(* Structured data element pool *)
entries : ARRAY[0..PMPS_PARAM.MAX_ASSERTIONS*3] OF T_HashTableEntry;(* Max. number of hash table entries. The value of table entry = 32 bit integer (pointer to dataPool-array-entry) *)
fbTable : FB_HashTableCtrl;(* basic hash table control function block *)
hTable : T_HHASHTABLE;(* hash table handle *)
pRefPtr : POINTER TO ST_BP_ArbInternal := 0;
indexOfElem : ULINT;(* Integer value (max. size: x86=>32bit, x64=>64bit)*)
cstSafeBeam :ST_BeamParams := ( nTran := 0, neVRange := 0, nRate := 0);// MG
END_VAR
;
END_FUNCTION_BLOCK
ACTION A_Add:
(* Adds entry to the table *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Add( hTable := hTable, key := key, putValue := ADR(cstSafeBeam)(* we will set this value later *), getPosPtr=>getPosPtr, bOk=>bOk );(* Add new element to the table, getPosPtr points to the new entry *)
IF fbTable.bOk THEN(* Success *)
fbTable.A_GetIndexAtPosPtr( hTable := hTable, putPosPtr := getPosPtr, getValue =>indexOfElem, bOk=>bOk );(* Get array index of getPosPtr entry *)
IF fbTable.bOk THEN(* Success *)
pRefPtr := ADR( dataPool[indexOfElem] );(* Get pointer to the data element *)
pRefPtr^ := putValue;(* copy application value *)
fbTable.A_Add( hTable := hTable, key := key, putValue := pRefPtr, bOk=>bOk );(* Assign the entry value = pointer to the data element *)
IF fbTable.bOk THEN(* Success *)
getValue := putValue;
END_IF
END_IF
END_IF
DataPoolToEpics();
END_ACTION
ACTION A_Count:
(* Count number of used entries *)
nCount := hTable.nCount;
bOk := TRUE;
END_ACTION
ACTION A_GetFirst:
(* Get first entry position pointer and value *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_GetFirst( hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_GetNext:
(* Get next entry position pointer and value *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
bOk := FALSE;
IF putPosPtr = 0 THEN
RETURN;
END_IF
fbTable.A_GetNext( hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_Lookup:
(* Lookup for entry by key *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Lookup( key := key, hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_Remove:
(* Search for entry and remove it *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Remove( key := key, hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
DataPoolToEpics();
END_ACTION
ACTION A_Reset:
(* Reset/initialize linked list *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
bOk := F_CreateHashTableHnd( ADR( entries ), SIZEOF( entries ), hTable );(* Intialize table handle *)
fbTable.A_Reset( hTable := hTable, bOk=>bOk );
nCount := 0;
END_ACTION
ACTION DataPoolToEpics:
MEMCPY(ADR(epicsDataPool), ADR(dataPool), (PMPS_PARAM.MAX_ASSERTIONS) * SIZEOF(ST_BP_ArbInternal));
END_ACTION
FB_BPControlDevice
(*
Used to request a beam parameter set from EPICS.
Technically just one of these is needed in the entire
PMPS for a line.
*)
FUNCTION_BLOCK FB_BPControlDevice
VAR_INPUT
(* Requested pre-optic attenuation % *)
{attribute 'pytmc' := 'pv: ReqBP:Transmission
io: o
field: HOPR 1;
field: LOPR 0;
field: PREC 2;
'}
nTran : REAL := PMPS_GVL.TRANS_SCALING_FACTOR;
(* Pulse-rate *)
{attribute 'pytmc' := 'pv: ReqBP:Rate
io: o
field: EGU Hz
'}
nRate : UDINT := 10;
(* Photon energy ranges *)
{attribute 'pytmc' := 'pv: ReqBP:PhotonEnergyRanges
io: o
field: EGU eV'}
{attribute 'displaymode' := 'binary'}
neVRange : DWORD := 4294967295;
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter;
END_VAR
VAR
EpicsReqBP : ST_BeamParams;
{attribute 'pytmc' := '
pv: ReqBP:Apply
io: o
'}
bApply : BOOL;
nControlDeviceID : DWORD;
sControlDeviceName: STRING:= 'BP Cntrl Dev';
rtApply : R_TRIG;
END_VAR
EpicsReqBP.nTran := nTran;
EpicsReqBP.neVRange := neVRange;
EpicsReqBP.nRate := nRate;
rtApply(CLK:=bApply);
IF rtApply.Q THEN
Arbiter.RemoveRequest(nControlDeviceID);
Arbiter.AddRequest(nControlDeviceID, EpicsReqBP, sControlDeviceName);
bApply := FALSE;
END_IF
END_FUNCTION_BLOCK
- Related:
FB_BPRequestor
(* BP Requestor
Elevates the BP request from an arbiter to PMPS_GVL.stRequestedBP
*)
FUNCTION_BLOCK FB_BPRequestor IMPLEMENTS I_HigherAuthority
VAR_INPUT
END_VAR
VAR_OUTPUT
q_ReqBP : ST_BeamParams; // Arbitrated BP
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter;
//FFO : FB_HardwareFFOutput;
END_VAR
VAR
ReqBP : ST_BeamParams;
nCohort : ULINT; // Current cohort, inc. 1 cycle after IO is updated
nRequestCohort : ULINT; // Cohort number recorded at the time of the request.
END_VAR
Arbiter.ElevateRequest(THIS^); // Executes arbitration and retrieves ReqBP
IF nRequestCohort >= nCohort THEN
// Update requested BP set
q_ReqBP := ReqBP;
// Note: we may do more here someday... perhaps all of the output control
// will reside in here, and be solely controlled by this block. For now
// I am going to adopt a flat management structure.
// Start a new cohort
nCohort := nCohort + 1;
END_IF
END_FUNCTION_BLOCK
FB_BPTM_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_BPTM_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
VAR CONSTANT
BPTM_ARR : UDINT := PMPS_PARAM.MAX_ASSERTIONS;
cCycle : UINT := 20; // How many cycles does it take to complete a BPTM
END_VAR
BPTMBasicFunction();
InterruptAtAllSteps();
FullArbError();
END_FUNCTION_BLOCK
- Related:
FB_CTLS_Outputs
(*
Controls auxiliary beam class outputs.
1-3 are mapped to control the Cu beamline rate via the LCLS II MPS.
Maps beam rate requests to 1, 10, or full rate beam for Cu linac.
*)
FUNCTION_BLOCK FB_CTLS_Outputs
VAR_INPUT
BP : ST_BeamParams;
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := 'pv: CTLSChannel
io: i
field: DESC CTLS Rate Control hardwire channel state'}
epicsBitmap : WORD;
{attribute 'TcLinkTo' := '[1] := TIIB[PMPS_Premp]^Channel 9^Output;
[2] := TIIB[PMPS_Premp]^Channel 10^Output;
[3] := TIIB[PMPS_Premp]^Channel 11^Output;'}
q_CLTS_ASSERTION_LINES AT %Q* : ARRAY [1..3] OF BOOL;
END_VAR
// CLTS 1Hz
q_CLTS_ASSERTION_LINES[1] := BP.nRate >= 1;
// CLTS 10Hz
q_CLTS_ASSERTION_LINES[2] := BP.nRate >= 10;
// CLTS Full rate
q_CLTS_ASSERTION_LINES[3] := BP.nRate >= 120;
epicsBitMap.0 := q_CLTS_ASSERTION_LINES[1];
epicsBitMap.1 := q_CLTS_ASSERTION_LINES[2];
epicsBitMap.2 := q_CLTS_ASSERTION_LINES[3];
END_FUNCTION_BLOCK
- Related:
FB_DiffBP_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_DiffBP_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
BPDiff();
END_FUNCTION_BLOCK
FB_DummyArbIO
(* Use to test other library integrations of PMPS preemptive functionality *)
(* This block stands in for the FB_SubSysToArb_IO block. *)
FUNCTION_BLOCK FB_DummyArbIO IMPLEMENTS I_HigherAuthority, I_LowerAuthority
VAR_INPUT
//stCurrentBP : REFERENCE TO ST_BeamParams; //Set to something to redirect currentBP update
LA : I_LowerAuthority;
tRateDelay : TIME; // Delay until rate is looped back into q_stSimulatedBPReadback
tTransDelay : TIME; // Delay until transmission is looped back into q_stSimulatedBPReadback
tPEDelay : TIME; // Delay until PE is looped back into q_stSimulatedBPReadback
tAptDelay : TIME; // Delay until apertures are looped back into q_stSimulatedBPReadback
tSattDelay : TIME; // Delay until apertures are looped back into q_stSimulatedBPReadback
END_VAR
VAR_OUTPUT
q_stSimulatedBPReadback : ST_BeamParams; // Updated to
END_VAR
VAR_IN_OUT
FFO : FB_HardwareFFOutput;
END_VAR
VAR
bAutoUpdateBP : BOOL := FALSE; // Set by AutoUpdateBP property, if true causes this FB to update PMPS_GVL.stCurrentBP to the
// requested value automatically within the RequestBP method and restore it within the RemoveRequest method
stNewReqBP : ST_BeamParams;
stReqOutBP : ST_BeamParams;// To be written on next ApplyBPReq call
stSaveCurrentBP : ST_BeamParams; // Will be restored after RemoveRequest then ApplyBPReq call
nSavedID : DWORD; // Used to simulate CheckRequest method
tonRate : TON;
tonTrans : TON;
tonPE : TON;
tonApt : TON;
tonSatt : TON;
END_VAR
LA.ElevateRequest(THIS^);
ApplyBPReq(FALSE); //If autoupdate is set, this will propagate the request to PMPS_GVL.stCurrentBP;
END_FUNCTION_BLOCK
- Related:
FB_DummyHA
FUNCTION_BLOCK FB_DummyHA IMPLEMENTS I_HigherAuthority, I_LowerAuthority
VAR_INPUT
ReqAcknowledged : BOOL := TRUE; // Use to control when the HA CHeckRequest Returns TRUE
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
END_FUNCTION_BLOCK
FB_evRangeCalculator_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_evRangeCalculator_Test EXTENDS FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
VAR CONSTANT
RandomBoundary : INT := 2;
END_VAR
BasicFunction();
END_FUNCTION_BLOCK
FB_eVSimulator
(* eV Simulator
A. Wallace 2019-8-30
Adds noise to the eV and changes eV position occasionally.
*)
FUNCTION_BLOCK FB_eVSimulator
VAR_INPUT
NoiseLevel : REAL := 0; // eV Noise
ChangeTime : TIME := T#10S;
END_VAR
VAR_OUTPUT
eV : REAL := 300;
END_VAR
VAR
eVRange : REAL := 1600;
timer: TON;
eVRand : DRAND :=(Seed:=0);
NoiseRand : DRAND := (Seed:=0);
END_VAR
timer(in:=TRUE, PT:=ChangeTime);
//Occasionally change eV
IF timer.Q THEN
timer(in:=FALSE);
eVRand();
eV := LIMIT(0, eVRange*LREAL_TO_REAL(eVRand.Num), eVRange);
END_IF
// Noise generation
NoiseRand();
eV := eV + NoiseLevel*LREAL_TO_REAL(NoiseRand.Num);
NoiseRand();
eV := LIMIT(0, ev - NoiseLevel*LREAL_TO_REAL(NoiseRand.Num), eVRange);
END_FUNCTION_BLOCK
FB_eVWithinSpec_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_eVWithinSpec_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
evWithinRangeChecks();
END_FUNCTION_BLOCK
FB_FastFault
(* Fast Fault
2019-9-13 A. Wallace
Use this block to generate a beam-off fault. Connects to a fast fault hardware output
function block to contribute to the state of the fast fault output (FFO).
If the i_xOK goes false, the associated FFO will go false, despite the state of any other
contributing fast faults, unless the FFO is currently vetoed.
*)
{attribute 'reflection'}
FUNCTION_BLOCK FB_FastFault
VAR_INPUT
i_xOK : BOOL; // Connect to fast-fault condition (false produces fault)
i_xReset : BOOL; // Resets when i_xOK is true and this is true
i_xAutoReset : BOOL := FALSE; // Automatically clear fast fault (latching vs non-latching)
i_xVetoable : BOOL := TRUE; // Mask this fast fault if the FFO veto device is true
i_DevName : T_MaxString := ''; // Device name for diagnostic
i_Desc : T_MaxString := ''; // Description of fast fault (you should set at init)
i_TypeCode : UINT; // Error code for classifying fast faults
END_VAR
VAR_OUTPUT
o_xFFLine : BOOL; //Connect to HW output or another FF input if you like (Optional)
END_VAR
VAR_IN_OUT
io_fbFFHWO : FB_HardwareFFOutput; //Point to FB_HardwareFFOutput of your choice
END_VAR
VAR
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
FFInfo : ST_FFInfo;
RegistrationIdx : UINT := 1; // The index this FF was registered in the FFO
xInit : BOOL :=TRUE;
InfoStringFmtr : FB_FormatString;
InUse : T_MaxString;
AutoReset : T_MaxString;
END_VAR
IF xInit THEN
FFInfo.sPath := sPath;
FFInfo.InUse := True;
FFInfo.TypeCode := i_TypeCode;
FFInfo.DevName := i_DevName;
FFInfo.Desc := i_Desc;
FFInfo.AutoReset := i_xAutoReset;
FFInfo.Vetoable := i_xVetoable;
InUse := BOOL_TO_STRING(FFInfo.InUse);
AutoReset := BOOL_TO_STRING(FFInfo.AutoReset);
InfoStringFmtr(sFormat:='%s;%s;%X;%s;%s;%s;',
arg1 := F_STRING(FFinfo.sPath),
arg2 := F_STRING(InUse),
arg3 := F_WORD(FFInfo.TypeCode),
arg4 := F_STRING(FFInfo.DevName),
arg5 := F_STRING(FFInfo.Desc),
arg6 := F_STRING(AutoReset),
sOut=>FFInfo.InfoString);
io_fbFFHWO.Register(
stFFInfo:=FFInfo,
Idx=>RegistrationIdx);
//<TODO> if registration doesn't succeed, send warning to diagnostic
xInit := FALSE;
END_IF
io_fbFFHWO.IdxCheckIn(Idx:=RegistrationIdx, OK := i_xOK, Reset:= i_xReset);
END_FUNCTION_BLOCK
- Related:
FB_FastFault_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_FastFault_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
fbTime : FB_LocalSystemTime := ( bEnable := TRUE, dwCycle := 1 ); //Get current system time, used for override
END_VAR
VAR CONSTANT
OvrdTime : TIME := T#2s;
END_VAR
FFCombinedFunctionality();
FFRegistration();
FFOvrdAndNextFault();
END_FUNCTION_BLOCK
FB_HardwareFFOutput
{attribute 'reflection'}
FUNCTION_BLOCK FB_HardwareFFOutput
VAR CONSTANT
FF_ARRAY_UPPER_BOUND : UINT := PMPS_PARAM.MAX_FAST_FAULTS;
END_VAR
VAR_INPUT
{attribute 'pytmc' := '
pv: ClearFault
io: o
field: DESC Might be overidden by PLC writes
'}
i_xReset : BOOL;
{attribute 'pytmc' := '
pv: EnableVeto
io: o
'}
i_xVeto : BOOL;
bAutoReset : BOOL := FALSE; // Set true for the FFO to automatically permit beam again after all fast faults are cleared
i_sNetID : T_AmsNetID:=''; //Set to the Arbiter AmsNetID to be used for the synchronisation. An empty string means the system will sue local time
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv: FaultHWO
io: i
field: DESC Hardware Output Status
'}
q_xFastFaultOut AT %Q* : BOOL;
q_xValidSyncTime : BOOL;// system time bValid output True when sync is successful
END_VAR
VAR_IN_OUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: FF
'}
astFF : ARRAY[1..FF_ARRAY_UPPER_BOUND] OF ST_FF;
{attribute 'pytmc' := '
pv: RegistrationFailure
io: io
'}
xFastFaultRegFail : BOOL := FALSE; // Set true if a fast fault fails to register. Holds beam off.
tFFRegFail : F_TRIG;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
{attribute 'pytmc' := '
pv: OK
io: i
'}
xOK : BOOL:= TRUE; // Current internal state of FFO, indicates if FFO will accept a reset
rtReset : R_TRIG;
rtResetandOK : R_TRIG;
nIndex : UINT := 1;
IdxOK: BOOL;
fbTime : FB_LocalSystemTime := ( bEnable := TRUE, dwCycle := 1 ); //Get current system time, used for override
fbTime_to_UTC: FB_TzSpecificLocalTimeToSystemTime;
fbGetTimeZone: FB_GetTimeZoneInformation;
fbJson : FB_JsonSaxWriter;
pmpsTypeCode : UDINT := 0; // shows up in json as pmps_typecode
fbLogger : FB_LogMessage := (
eSevr := TcEventSeverity.Critical,
eSubsystem := E_Subsystem.MPS,
nMinTimeViolationAcceptable := PMPS_PARAM.MAX_FAST_FAULTS);
END_VAR
// <TODO> latch off beam if DI card status goes bad
END_FUNCTION_BLOCK
ACTION EvaluateOutput:
////////////////////////////////////////////////////////////////
// Critical code. Do not touch unless you know what you're doing
////////////////////////////////////////////////////////////////
//<TODO> add diagnostic for success or failure of reset
rtReset(CLK:=i_xReset);
rtResetandOK(CLK:=(rtReset.Q OR bAutoReset) AND xOK);
q_xFastFaultOut S= rtResetandOK.Q;
q_xFastFaultOut R= (NOT xOK OR xFastFaultRegFail);
//Reset OK for next cycle
xOK := True;
////////////////////////////////////////////////////////////////
END_ACTION
ACTION Execute:
EvaluateOverrides();
EvaluateOutput();
ExecuteLogging();
END_ACTION
ACTION ExecuteNoLog:
EvaluateOverrides();
EvaluateOutput();
//ExecuteLogging();
END_ACTION
- Related:
FB_Hgvpu
FUNCTION_BLOCK FB_Hgvpu IMPLEMENTS I_UndulatorComplex
VAR_INPUT
fbElectronEnergy : REFERENCE TO FB_LREALFromEPICS;
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv: CurrentPhotonEnergy
io: i
field: DESC Calculated current photon energy
field: PREC 3
field: EGU eV
'}
fCurrentPhotonEnergy : LREAL;
{attribute 'pytmc' := '
pv: TargetPhotonEnergy
io: i
field: DESC Calculated desired photon energy
field: PREC 3
field: EGU eV
'}
fTargetPhotonEnergy : LREAL;
{attribute 'pytmc' := '
pv: SeedUndulatorNumber
io: i
field: DESC Seed undulator number
'}
nSeedUndulator : UDINT; // Set to zero when no undulators are active
{attribute 'pytmc' := '
pv: TargetSeedUndulatorNumber
io: i
field: DESC Seed undulator number for target K
'}
nTargetSeedUndulator : UDINT; // Set to zero when no undulators are active
END_VAR
VAR
// From lcls-srv01: grep -e KDes /u1/lcls/epics/ioc/data/ioc-undh-uc*/iocInfo/IOC.pvlist |sort
{attribute 'pytmc' := 'pv: 24; link: 2450:'}
fbSegment_24 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 25; link: 2550:'}
fbSegment_25 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 26; link: 2650:'}
fbSegment_26 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 27; link: 2750:'}
fbSegment_27 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 29; link: 2950:'}
fbSegment_29 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 30; link: 3050:'}
fbSegment_30 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 31; link: 3150:'}
fbSegment_31 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 32; link: 3250:'}
fbSegment_32 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 33; link: 3350:'}
fbSegment_33 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 34; link: 3450:'}
fbSegment_34 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 35; link: 3550:'}
fbSegment_35 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 36; link: 3650:'}
fbSegment_36 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 37; link: 3750:'}
fbSegment_37 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 38; link: 3850:'}
fbSegment_38 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 39; link: 3950:'}
fbSegment_39 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 40; link: 4050:'}
fbSegment_40 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 41; link: 4150:'}
fbSegment_41 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 42; link: 4250:'}
fbSegment_42 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 43; link: 4350:'}
fbSegment_43 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 44; link: 4450:'}
fbSegment_44 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 45; link: 4550:'}
fbSegment_45 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 46; link: 4650:'}
fbSegment_46 : FB_UndulatorSegment;
fbSegment : ARRAY [iLowBound_LUnd..iHighBound_LUnd] OF POINTER TO FB_UndulatorSegment;
fbCurrentSegment : REFERENCE TO FB_UndulatorSegment;
iIndex : UDINT;
bInitialized : BOOL := FALSE;
END_VAR
VAR CONSTANT
{attribute 'pytmc' := '
pv: FirstSegment
io: i
'}
iLowBound_LUnd : UDINT := 24;
{attribute 'pytmc' := '
pv: LastSegment
io: i
'}
iHighBound_LUnd : UDINT := 46;
{attribute 'pytmc' := '
pv: Period
io: i
field: EGU mm
'}
fPeriod_mm_LUnd : LREAL := 26.0;
{attribute 'pytmc' := '
pv: LowK
io: i
'}
fLowK_LUnd : LREAL := 0.54;
{attribute 'pytmc' := '
pv: HiK
io: i
'}
fHiK_LUnd : LREAL := 2.6;
END_VAR
IF NOT bInitialized THEN
Init();
END_IF
UndAdrUpdate();
nSeedUndulator := 0;
nTargetSeedUndulator := 0;
FOR iIndex := iLowBound_LUnd TO iHighBound_LUnd DO
IF fbSegment[iIndex] <> 0 THEN
fbCurrentSegment REF= fbSegment[iIndex]^;
fbCurrentSegment(fbElectronEnergy:=fbElectronEnergy);
//Mark the seed undulator, first undulator operating within K bounds
IF fbCurrentSegment.xActive AND nSeedUndulator = 0 THEN
nSeedUndulator := iIndex;
fCurrentPhotonEnergy := fbCurrentSegment.fPhotonEnergyAct;
END_IF
IF fbCurrentSegment.xTargetActive AND nTargetSeedUndulator = 0 THEN
nTargetSeedUndulator := iIndex;
fTargetPhotonEnergy := fbCurrentSegment.fPhotonEnergyDes;
END_IF
END_IF
END_FOR
IF nSeedUndulator = 0 THEN
fCurrentPhotonEnergy := 0;
END_IF
IF nTargetSeedUndulator = 0 THEN
fTargetPhotonEnergy := 0;
END_IF
END_FUNCTION_BLOCK
ACTION Init:
UndAdrUpdate();
FOR iIndex := iLowBound_LUnd TO iHighBound_LUnd DO
IF fbSegment[iIndex] <> 0 THEN
fbCurrentSegment REF= fbSegment[iIndex]^;
fbCurrentSegment.fPeriod_mm := fPeriod_mm_LUnd;
fbCurrentSegment.fLowK := fLowK_LUnd;
fbCurrentSegment.fHiK := fHiK_LUnd;
END_IF
END_FOR
bInitialized := TRUE;
END_ACTION
ACTION UndAdrUpdate:
fbSegment[24] := ADR(fbSegment_24);
fbSegment[25] := ADR(fbSegment_25);
fbSegment[26] := ADR(fbSegment_26);
fbSegment[27] := ADR(fbSegment_27);
fbSegment[28] := 0;
fbSegment[29] := ADR(fbSegment_29);
fbSegment[30] := ADR(fbSegment_30);
fbSegment[31] := ADR(fbSegment_31);
fbSegment[32] := ADR(fbSegment_32);
fbSegment[33] := ADR(fbSegment_33);
fbSegment[34] := ADR(fbSegment_34);
fbSegment[35] := ADR(fbSegment_35);
fbSegment[36] := ADR(fbSegment_36);
fbSegment[37] := ADR(fbSegment_37);
fbSegment[38] := ADR(fbSegment_38);
fbSegment[39] := ADR(fbSegment_39);
fbSegment[40] := ADR(fbSegment_40);
fbSegment[41] := ADR(fbSegment_41);
fbSegment[42] := ADR(fbSegment_42);
fbSegment[43] := ADR(fbSegment_43);
fbSegment[44] := ADR(fbSegment_44);
fbSegment[45] := ADR(fbSegment_45);
fbSegment[46] := ADR(fbSegment_46);
END_ACTION
- Related:
FB_KPhotonEnergy
FUNCTION_BLOCK FB_KPhotonEnergy EXTENDS FB_PhotonEnergy
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: eEnrg
link: BEND:DMPS:400:BACT
field: EGU GeV
'}
fbSxuElectronEnergy : FB_LREALFromEPICS;
{attribute 'pytmc' := '
pv: EEnergy
io: i
field: DESC Electron Energy
field: EGU GeV
'}
fElectronEnergyVal : LREAL;
{attribute 'pytmc' := '
pv: EEnergyValid
io: i
field: DESC Electron Energy Valid
'}
bElectronEnergyValid : BOOL;
{attribute 'pytmc' := '
pv: UND
link: USEG:UNDS:
'}
fbSxu : FB_SXU;
END_VAR
// Update PVs
fbSxuElectronEnergy();
fElectronEnergyVal := fbSxuElectronEnergy.fValue;
bElectronEnergyValid := fbSxuElectronEnergy.bValid;
fbSxu(fbElectronEnergy:=fbSxuElectronEnergy);
SUPER^(BP:=SUPER^.BP, Undulator:=fbSxu );
END_FUNCTION_BLOCK
- Related:
FB_KStopper
FUNCTION_BLOCK FB_KStopper EXTENDS FB_StopperWatcher
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR
END_VAR
SUPER^(stCurrentBP:=SUPER^.stCurrentBP);
END_FUNCTION_BLOCK
- Related:
FB_KVetoDevice
FUNCTION_BLOCK FB_KVetoDevice EXTENDS FB_VetoDevice
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
SUPER^(stCurrentBP:=SUPER^.stCurrentBP);
END_FUNCTION_BLOCK
- Related:
FB_LinearDeviceStateTable
(* This function block implements simple database. Data element values are stored in the hash table. *)
FUNCTION_BLOCK FB_LinearDeviceStateTable
VAR_INPUT
key : DWORD := 0;(* Entry key: used by A_Lookup, A_Remove method, the key variable is also used by A_Add method *)
putPosPtr : POINTER TO T_HashTableEntry := 0;(* Hash table entry position pointer (used by A_Find, A_GetNext, A_GetPrev) *)
putValue : ST_DeviceState;(* Hash table entry value (used by A_AddHead, A_AddTail, A_Find )*)
END_VAR
VAR_OUTPUT
bOk : BOOL := FALSE;(* TRUE = Success, FALSE = Failed *)
getPosPtr : POINTER TO T_HashTableEntry := 0;(* Returned hash table entry position pointer *)
getValue : ST_DeviceState;(* Returned hash table entry value *)
nCount : UDINT := 0;(* Hash table size (number of used entries, used by A_Count) *)
END_VAR
VAR
dataPool : ARRAY[0..PMPS_GVL.MAX_DEVICE_STATES] OF ST_DeviceState;(* Structured data element pool *)
entries : ARRAY[0..PMPS_GVL.MAX_DEVICE_STATES] OF T_HashTableEntry;(* Max. number of hash table entries. The value of table entry = 32 bit integer (pointer to dataPool-array-entry) *)
fbTable : FB_HashTableCtrl;(* basic hash table control function block *)
hTable : T_HHASHTABLE;(* hash table handle *)
pRefPtr : POINTER TO ST_DeviceState := 0;
indexOfElem : ULINT;(* Integer value (max. size: x86=>32bit, x64=>64bit)*)
END_VAR
;
END_FUNCTION_BLOCK
ACTION A_Add:
(* Adds entry to the table *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Add( hTable := hTable, key := key, putValue := 16#0000_0000(* we will set this value later *), getPosPtr=>getPosPtr, bOk=>bOk );(* Add new element to the table, getPosPtr points to the new entry *)
IF fbTable.bOk THEN(* Success *)
fbTable.A_GetIndexAtPosPtr( hTable := hTable, putPosPtr := getPosPtr, getValue =>indexOfElem, bOk=>bOk );(* Get array index of getPosPtr entry *)
IF fbTable.bOk THEN(* Success *)
pRefPtr := ADR( dataPool[indexOfElem] );(* Get pointer to the data element *)
pRefPtr^ := putValue;(* copy application value *)
fbTable.A_Add( hTable := hTable, key := key, putValue := pRefPtr, bOk=>bOk );(* Assign the entry value = pointer to the data element *)
IF fbTable.bOk THEN(* Success *)
getValue := putValue;
END_IF
END_IF
END_IF
END_ACTION
ACTION A_Count:
(* Count number of used entries *)
nCount := hTable.nCount;
bOk := TRUE;
END_ACTION
ACTION A_GetFirst:
(* Get first entry position pointer and value *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_GetFirst( hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_GetNext:
(* Get next entry position pointer and value *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
bOk := FALSE;
IF putPosPtr = 0 THEN
RETURN;
END_IF
fbTable.A_GetNext( hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_Lookup:
(* Lookup for entry by key *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Lookup( key := key, hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_Remove:
(* Search for entry and remove it *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbTable.A_Remove( key := key, hTable := hTable, putPosPtr := putPosPtr, getValue=>pRefPtr, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
getValue := pRefPtr^;
END_IF
END_ACTION
ACTION A_Reset:
(* Reset/initialize linked list *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
bOk := F_CreateHashTableHnd( ADR( entries ), SIZEOF( entries ), hTable );(* Intialize table handle *)
fbTable.A_Reset( hTable := hTable, bOk=>bOk );
nCount := 0;
END_ACTION
- Related:
FB_LinearGovernor
(* The governor is meant to check your work and keep you from making mistakes.
Checks:
Does your state match the device position and visa versa?
Are the beam parameters safe for the device state?
Did you properly assert the transition and final beam parameters?
*)
FUNCTION_BLOCK FB_LinearGovernor
VAR_INPUT
i_stCurrentBeamParams : ST_BeamParams; //Link to global beam params
i_xMPSOverride : BOOL; //True releases MPS limits after a delay
i_xTransitionRequested : BOOL; //Set true to request a transition
i_xResetMPSFault : BOOL; //Set true to clear MPS fault
END_VAR
VAR_OUTPUT
//True when governor has released restrictions
q_xTransitionPermitted : BOOL;
//Set if MC_Power has an error, or override mode is requested
q_xFault : BOOL;
// Indicates override mode is active
q_xMPSLimitsOverridden: BOOL;
END_VAR
VAR_IN_OUT
stDevice : ST_Device; //The governed
Arbiter : FB_Arbiter;
FastFaultOutput : FB_HardwareFFOutput;
END_VAR
VAR
mcPower : MC_Power;
xActuatorPositiveEnable: BOOL;
xActuatorNegativeEnable: BOOL;
xMPSPositiveEnable : BOOL;
xMPSNegativeEnable : BOOL;
lrPosition: LREAL;
lrLatchedTargetPosition : LREAL;
lrLatchedTargetTolerance : LREAL;
stLatchedTargetState : ST_DeviceStateExt;
lrMPSUpperLimit: LREAL;
lrMPSLowerLimit: LREAL;
rtNewState: R_TRIG;
xTransitionOK: BOOL;
tonMPSOverrideMode : TON := (
PT := T#5S
);
srMPSFault : SR;
// Actuator out of bounds fault
xActuatorOOB: BOOL;
//Beam parameters out of spec
xBeamParamsOOS: BOOL;
//Beam parameters of currently latched state
stLatchedBeamParams: ST_BeamParams;
stTargetState: ST_DeviceStateExt;
rtLatchNewState : R_TRIG;
xInit: BOOL := TRUE;
stInitDeviceState: ST_DeviceStateExt;
// Indicates an error with initialization
xInitFault: BOOL;
// Indicates the state could not be found in the state table. There may be something wrong in the device state machine implementation or state table instantiation.
xStateLookupError: BOOL;
// A transition needs to be requested at least once before we start latching any state.
xTransitionHasBeenRequested: BOOL := FALSE;
ffBeamParamsOK : FB_FastFault;
ffActuatorBoundsOK : FB_FastFault;
END_VAR
lrPosition := stDevice.stAxis.NcToPlc.ActPos; //Actuator position
stDevice.stAxis.ReadStatus();
//Init
(* The init strategy is:
If the device is restored and it's residing at the safe position,
no PMPS faults. *)
////////////////////////////////
IF xInit THEN
xInit := FALSE;
//Use nSafeSate for initialization.
stDevice.StateTable.A_Lookup(key := stDevice.nSafeState);
IF stDevice.StateTable.bOk THEN
stInitDeviceState := F_DeviceState_To_DeviceStateExt(stDevice.StateTable.getValue, TRUE);
xInitFault := FALSE;
ELSE
xInitFault := TRUE;
stInitDeviceState.xValid := FALSE;
END_IF
lrLatchedTargetPosition := stInitDeviceState.rPosition;
lrLatchedTargetTolerance := stInitDeviceState.rTolerance;
stLatchedBeamParams := stInitDeviceState.stReqBeamParam;
END_IF
// The governor will permit transitions once it verifies the transition assertion is active
// Note the governor does not verify the target state beam params are asserted, perhaps it should <TODO>
////////////////////////////////
IF i_xTransitionRequested THEN
xTransitionHasBeenRequested := TRUE;
xTransitionOK := Arbiter.CheckRequest( stDevice.stTransitionState.nStateRef) AND F_SafeBPCompare(i_stCurrentBeamParams, stDevice.stTransitionState.stReqBeamParam);
//Set the target state for evaluation in further logic
stdevice.StateTable.A_Lookup(key := stDevice.nTargetState);
IF stDevice.StateTable.bOk THEN
stTargetState := F_DeviceState_To_DeviceStateExt(stDevice.StateTable.getValue, TRUE);
xStateLookupError := FALSE;
ELSE
xStateLookupError := TRUE;
stTargetState.xValid := FALSE;
END_IF
ELSE
xTransitionOK := FALSE;
END_IF
(* Note about TransitionRequested
If TransitionRequested goes false during a transition, an MPS fault may be induced. This occurs
because the Actuator Out of Bounds fault will go back to using the original state limits, which
the axis may have already moved beyond.
To avoid this transient fault, make sure your device state machine is implemented such that the
transition request to the governor block remains high until the target position is reached.
*)
//Determine if a transition is complete and latch the new state
(* A transition is complete when:
Axis at standstill.
Axis within tolerance.
No transition req.
Once these conditions are met, the MPS limits shall re-engage to restrict the
axis motion to the tolerance around the state position and the MPS fault monitors
shall evaluate based on the newly latched target state.
*)
rtLatchNewState(CLK := stDevice.stAxis.Status.StandStill AND
lrPosition <= stTargetState.rPosition + stTargetState.rTolerance AND
lrPosition >= stTargetState.rPosition - stTargetState.rTolerance AND
NOT i_xTransitionRequested AND xTransitionHasBeenRequested);
IF rtLatchNewState.Q THEN
lrLatchedTargetPosition := stTargetState.rPosition;
lrLatchedTargetTolerance := stTargetState.rTolerance;
stLatchedBeamParams := stTargetState.stReqBeamParam;
stLatchedTargetState := stTargetState;
END_IF
//Let other blocks know the governor won't interfer with transitions
q_xTransitionPermitted := xTransitionOK;
//Adjust limit ranges based on active state and current target
IF xTransitionOK THEN
//Extend range to include target state
lrMPSUpperLimit := MAX(lrMPSUpperLimit, stTargetState.rPosition + stTargetState.rTolerance);
lrMPSLowerLimit := MIN(lrMPSLowerLimit, stTargetState.rPosition - stTargetState.rTolerance);
ELSE
//Calculate the limit positions for MPS based on current state
lrMPSUpperLimit := lrLatchedTargetPosition + lrLatchedTargetTolerance;
lrMPSLowerLimit := lrLatchedTargetPosition - lrLatchedTargetTolerance;
END_IF
//MPS override
(* In override mode, the device will be free to move anywhere within it's travel range *)
//Permit motion after a small delay
tonMPSOverrideMode(IN:=i_xMPSOverride OR stDevice.xOverrideMPSLimits, Q=>q_xMPSLimitsOverridden);
//Virtual limit switch evaluation
xActuatorPositiveEnable := stDevice.i_xHiLim AND lrPosition <= stDevice.lrUpperPositionLimit;
xActuatorNegativeEnable := stDevice.i_xLoLim AND lrPosition >= stDevice.lrLowerPositionLimit;
//Evaluate MPS limit states
xMPSPositiveEnable := lrPosition <= lrMPSUpperLimit;
xMPSNegativeEnable := lrPosition >= lrMPSLowerLimit;
//Final Limit switch evaluation
mcPower.Enable_Positive := xActuatorPositiveEnable AND (tonMPSOverrideMode.Q OR xMPSPositiveEnable);
mcPower.Enable_Negative := xActuatorNegativeEnable AND (tonMPSOverrideMode.Q OR xMPSNegativeEnable);
mcPower(
Axis := stDevice.stAxis,
Enable := stDevice.xEnable //link to device enable <TODO>
);
//MPS Faults
//Fault if the beam is unsafe for current state
ffBeamParamsOK(i_xOK:=F_SafeBPCompare(i_stCurrentBeamParams, stLatchedBeamParams),
i_xReset:=i_xResetMPSFault,
io_fbFFHWO := FastFaultOutput);
//Fault if the position is outside of the permitted range
ffActuatorBoundsOK(i_xOK := (lrPosition <= lrMPSUpperLimit AND lrPosition >= lrMPSLowerLimit),
i_xReset:=i_xResetMPSFault,
io_fbFFHWO := FastFaultOutput);
//Collect all other faults
q_xFault := NOT ffBeamParamsOK.o_xFFLine OR
NOT ffActuatorBoundsOK.o_xFFLine OR
mcPower.Error OR
i_xMPSOverride OR
xInitFault;
//Any faults trip the fast fault line for this FB
//FastFaultOutput.CheckIn(NOT q_xFault);
END_FUNCTION_BLOCK
FB_LPhotonEnergy
FUNCTION_BLOCK FB_LPhotonEnergy EXTENDS FB_PhotonEnergy
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
{attribute 'pytmc' := '
pv: eEnrg
link: BEND:DMPH:400:BACT
field: EGU GeV
'}
fbHgvpuElectronEnergy : FB_LREALFromEPICS;
{attribute 'pytmc' := '
pv: EEnergy
io: i
field: DESC Electron Energy
field: EGU GeV
'}
fElectronEnergyVal : LREAL;
{attribute 'pytmc' := '
pv: EEnergyValid
io: i
field: DESC Electron Energy Valid
'}
bElectronEnergyValid : BOOL;
{attribute 'pytmc' := '
pv: UND
link: USEG:UNDH:
'}
fbHgvpu : FB_Hgvpu;
END_VAR
// Update PVs
fbHgvpuElectronEnergy();
fElectronEnergyVal := fbHgvpuElectronEnergy.fValue;
bElectronEnergyValid := fbHgvpuElectronEnergy.bValid;
fbHgvpu(fbElectronEnergy:=fbHgvpuElectronEnergy);
SUPER^(BP:=SUPER^.BP, Undulator:=fbHgvpu );
END_FUNCTION_BLOCK
- Related:
FB_LStopper
FUNCTION_BLOCK FB_LStopper EXTENDS FB_StopperWatcher
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR
END_VAR
SUPER^(stCurrentBP:=SUPER^.stCurrentBP);
END_FUNCTION_BLOCK
- Related:
FB_MachineSimulator
(* Simulates the machine responding to requests by adding a bit of delay/ ramps
*)
FUNCTION_BLOCK FB_MachineSimulator
VAR_INPUT
i_stAssertedParams : ST_BeamParams;
i_xFault : BOOL;
xEnableAtt : BOOL;
xEnablePE : BOOL;
xEnableRate : BOOL;
END_VAR
VAR_IN_OUT
iq_stMachineParams : ST_BeamParams;
END_VAR
VAR
nTargetTran: REAL;
tonTranTimer : TON := (
PT := T#2S
);
fTargetPP_mJ: REAL;
tonPPETimer: TON := (
PT := T#2S
);
END_VAR
//Attenuation
IF xEnableAtt THEN
IF i_stAssertedParams.nTran <> nTargetTran THEN
tonTranTimer(IN:=FALSE);
nTargetTran := i_stAssertedParams.nTran;
ELSIF NOT tonTranTimer.Q THEN
tonTranTimer(IN:=TRUE);
ELSE
iq_stMachineParams.nTran := nTargetTran;
END_IF
END_IF
//Pulse Energy
(*
IF xEnablePE THEN
IF i_stAssertedParams.fPP_mJ <> fTargetPP_mJ THEN
tonPPETimer(IN:=FALSE);
fTargetPP_mJ := i_stAssertedParams.fPP_mJ;
ELSIF NOT tonTranTimer.Q THEN
tonPPETimer(IN:=TRUE);
ELSE
iq_stMachineParams.fPP_mJ := fTargetPP_mJ;
END_IF
END_IF
*)
//Rate
IF xEnableRate THEN
IF i_xFault THEN
iq_stMachineParams.nRate := 0;
ELSE
iq_stMachineParams.nRate := i_stAssertedParams.nRate;
END_IF
END_IF
END_FUNCTION_BLOCK
- Related:
FB_PhotonEnergy
FUNCTION_BLOCK FB_PhotonEnergy
VAR_INPUT
Undulator : I_UndulatorComplex;
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
BP : ST_BeamParams;
END_VAR
VAR
nCurrentPhotonEnergyBitmask : UDINT;
nTargetPhotonEnergyBitmask : UDINT;
END_VAR
//Update BP
nCurrentPhotonEnergyBitmask := F_eVRangeCalculator(Undulator.rCurrentPhotonEnergy, nCurrentPhotonEnergyBitmask);
nTargetPhotonEnergyBitmask := F_eVRangeCalculator(Undulator.rTargetPhotonEnergy, nTargetPhotonEnergyBitmask);
BP.neVRange := nCurrentPhotonEnergyBitmask OR nTargetPhotonEnergyBitmask;
END_FUNCTION_BLOCK
- Related:
FB_PhotonEnergyWatcher
(*
A. Wallace 2019-4-22
The photon energy watcher ensures the current and target photon energy is within
the arbirated bounds. Target in this case means, the calculated target photon energy
from the PVs that control the mechatronics of the undulators/ the electron energy.
If there are control PVs that match the monitor PVs, the control PVs are used to
calculate a "target" photon energy. This is supposed to cover when the undulators/
electron energy is transitioning.
The abritrated bounds come from a simple AND of all the permitted ranges. See
the arbitrate action of the arbiter FB.
Note, this protection logic does not account for beam-off when determining fast-fault
status. If a device is requesting a limited range of eV, this request must be honored,
regardless of current beam-rate.
*)
{attribute 'reflection'}
FUNCTION_BLOCK FB_PhotonEnergyWatcher
VAR_INPUT
i_stCurrentBeamParams : ST_BeamParams; //Link to global beam params
i_stMachineTargetBeamParams : ST_BeamParams; //Link to global machine target beam params
i_stRequestedBeamParams : ST_BeamParams; //Link to arbiter output or beam param. requestor
// Reset fault
i_xReset: BOOL;
sName : STRING := 'PhotonEnergyWatcher';
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
io_fbFFHWO : FB_HardwareFFOutput;
END_VAR
VAR
xPhotonEnergyWithinBounds : BOOL;
fbFF : FB_FastFault :=(
i_DevName := sName,
i_Desc := 'Fault occurs when the calculated machine photon energy (K value calculated by undulator gap, and electron energy) falls outside the permitted range.',
i_TypeCode := 7 );
{attribute 'pytmc' := '
pv: ResidualPhotonEnergies
io: i
archive: 1Hz monitor
field: DESC Portions of beam eV not permitted
field: EGU eV-bitmask
'}
evResidual : DWORD;
fbLog : FB_LogMessage := (
eSubSystem := E_Subsystem.MPS,
eSevr := TcEventSeverity.Critical
);
bLogOneShot : BOOL;
sDevName : T_MaxString := 'Photon Energy Watcher';
fbGetHN : FB_GetHostName;
bInit : BOOL := TRUE;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
fbStr : FB_FormatString := (
sOut := 'Non-zero photon energy residual: %32b; Req: %32b; Act: %32b');
END_VAR
IF bInit THEN
fbGetHN(bExecute:=TRUE);
bInit R= NOT fbGetHN.bBusy;
END_IF
xPhotonEnergyWithinBounds := (i_stCurrentBeamParams.neVRange AND i_stRequestedBeamParams.neVRange) = i_stCurrentBeamParams.neVRange;
evResidual := (i_stCurrentBeamParams.neVRange XOR i_stRequestedBeamParams.neVRange) AND i_stCurrentBeamParams.neVRange;
IF evResidual <> 0 AND bLogOneShot THEN
fbLog.sJson := F_PMPS_JSON(
CONCAT(fbGetHN.sHostName, sDevName),
sPath,
PMPS_CODES.PEW_FAULT);
fbStr.arg1 := F_DWORD(evResidual);
fbStr.arg2 := F_DWORD(i_stCurrentBeamParams.neVRange);
fbStr.arg3 := F_DWORD(i_stRequestedBeamParams.neVRange);
fbStr();
fbLog(sMsg:=fbStr.sOut);
bLogOneShot := FALSE;
ELSIF evResidual = 0 THEN
bLogOneShot := TRUE;
END_IF
fbFF(i_xOK := xPhotonEnergyWithinBounds,
io_fbFFHWO := io_fbFFHWO,);
END_FUNCTION_BLOCK
FB_PhotonEnergyWatcher_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_PhotonEnergyWatcher_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
PEWatcherBasicFunction();
END_FUNCTION_BLOCK
FB_RateFromEPICS
FUNCTION_BLOCK FB_RateFromEPICS
(*
enum MPSBeamRates {
MPSRateInvalid = 0,
MPSRate0Hz = 1,
/* Previously used 2 and 3 for single shot and burst */
MPSRate1Hz = 4,
MPSRate10Hz = 5,
MPSRate30Hz = 6,
MPSRate60Hz = 7,
MPSRate120Hz = 8,
MPSRateUnknown = 9,
MPSRateSingleShot = 10,
MPSRateBurstMode = 11,
MPSRateBurstModeNotActive = 12,
MPSNumberOfBeamRates = 13,
MPSRateBurstInvalid = 14
}
*)
VAR_IN_OUT
BP : ST_BeamParams;
fbBYKIK_Rate : FB_LREALFromEPICS;
FFO : FB_HardwareFFOutput;
END_VAR
VAR_OUTPUT
xError : BOOL;
END_VAR
VAR
eMPSRate : E_MPSBeamRates;
ffRateReadBack : FB_FastFault := (
i_DevName := 'Arbiter',
i_Desc := 'Issue with rate readback from Accelerator. Gateway or EPICS connection. Must be fixed.',
i_TypeCode := 16#203,
i_xAutoReset:=True);
END_VAR
VAR CONSTANT
cFailSafeRate : UDINT := 1000001;
END_VAR
fbBYKIK_Rate();
IF fbBYKIK_Rate.bValid THEN
eMPSRate := LREAL_TO_UINT(fbBYKIK_Rate.fValue);
ELSE
eMPSRate := E_MPSBeamRates.MPSRateInvalid;
END_IF
CASE eMPSRate OF
MPSRateInvalid:
BP.nRate := cFailSafeRate;
MPSRate0Hz:
BP.nRate := 0;
MPSRate1Hz:
BP.nRate := 1;
MPSRate10Hz:
BP.nRate := 10;
MPSRate30Hz:
BP.nRate := 30;
MPSRate60Hz:
BP.nRate := 60;
MPSRate120Hz:
BP.nRate := 120;
MPSRateUnknown:
BP.nRate := cFailSafeRate;
ELSE
BP.nRate := cFailSafeRate;
END_CASE
ffRateReadback(i_xOK:=fbBYKIK_Rate.bValid, io_fbFFHWO:=FFO);
BP.xValid R= NOT fbBYKIK_Rate.bValid OR
eMPSRate = MPSRateUnknown OR
eMPSRate = MPSRateInvalid;
END_FUNCTION_BLOCK
FB_SafeBPCompare_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_SafeBPCompare_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
BeamOffIsSafe();
BPComparisonCheck();
END_FUNCTION_BLOCK
FB_SolidAttenuator
FUNCTION_BLOCK FB_SolidAttenuator
VAR_INPUT
i_rRequestedAttenuation : REAL;
END_VAR
VAR_OUTPUT
q_xFault : BOOL;
q_rCurrentAttenuation : REAL;
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter; //Higher level arbiter from which upstream attenuation can be requested
END_VAR
VAR
stAttenuator : ST_BinarySolidAttenuator;
nSearchStart: INT;
nArrayLength: INT;
axNewBladeStates : ARRAY [0..9] OF BOOL;
nTryIndex: INT;
rCurrentAccumulatedAttenuation: REAL;
rTryFilter: REAL;
rTryAttenuation: REAL;
rRequestedAttenuation: REAL;
END_VAR
//
//Assuming the blades are ordered thickest to thinnest
REPEAT
rTryAttenuation := rCurrentAccumulatedAttenuation + stAttenuator.axBlades[nTryIndex].rThickness;
IF rRequestedAttenuation >= rTryAttenuation THEN
rCurrentAccumulatedAttenuation := rTryAttenuation;
axNewBladeStates[nTryIndex] := TRUE;
ELSE
axNewBladeStates[nTryIndex] := FALSE;
END_IF
UNTIL
nTryIndex >= nArrayLength
END_REPEAT
END_FUNCTION_BLOCK
- Related:
FB_StopperWatcher
// Relays stopper state and sends a message when stopper state changes.
FUNCTION_BLOCK FB_StopperWatcher
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
stCurrentBP : ST_BeamParams;
END_VAR
VAR
i_StopperOutLS AT %I* : BOOL;
i_StopperInLS AT %I* : BOOL;
q_StopperOUT_Relay AT %Q* : BOOL;
q_StopperIN_Relay AT %Q* : BOOL;
Stopper : UINT;
StopperName : STRING;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
// Logging
fbLogMsg : FB_LogMessage := (
eSubSystem := E_Subsystem.MPS);
rtIn : R_TRIG;
rtOut : R_TRIG;
bInit: BOOL := TRUE;
END_VAR
IF bInit THEN
fbLogMsg.sJson := F_PMPS_JSON(StopperName, sPath, 1000);
bInit := FALSE;
END_IF
rtIn(CLK := i_StopperInLS);
rtOut(CLK := i_StopperOutLS);
IF rtIn.Q THEN
fbLogMsg.sMsg := CONCAT(StopperName, ' moved IN');
fbLogMsg();
ELSIF rtOut.Q THEN
fbLogMsg.sMsg := CONCAT(StopperName, ' moved OUT');
fbLogMsg();
END_IF
q_StopperOUT_Relay := i_StopperOutLS;
q_StopperIN_Relay := i_StopperInLS;
// Update current beam parameters
stCurrentBP.aVetoDevices[Stopper] := i_StopperInLS;
END_FUNCTION_BLOCK
- Related:
FB_SubsysToArb_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_SubsysToArb_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
BasicFunction();
VetoFunction();
END_FUNCTION_BLOCK
FB_SubSysToArbiter_IO
// Use on a subsystem PLC to request from the arbiter
// Run at the top of your cycle to receive the latest BP
FUNCTION_BLOCK FB_SubSysToArbiter_IO IMPLEMENTS I_HigherAuthority
VAR_INPUT
Reset : BOOL; // Fast fault reset
sName : STRING := 'SubSysToArbiter';
i_bVeto : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
Arbiter : FB_Arbiter;
fbFFHWO : FB_HardwareFFOutput;
END_VAR
VAR
{attribute 'TcLinkTo' := 'TIIB[PMPS_PRE]^IO Inputs^CurrentBP'}
i_stCurrentBP AT %I* : ST_BeamParams_IO;
{attribute 'TcLinkTo' := 'TIIB[PMPS_PRE]^IO Outputs^RequestedBP'}
q_stRequestedBP AT %Q* : ST_BeamParams_IO;
{attribute 'pytmc' := 'pv: TxPDO_toggle
io: i'}
{attribute 'TcLinkTo' := 'TIIB[PMPS_PRE]^SYNC Inputs^TxPDO toggle'}
xTxPDO_toggle AT %I* : BIT;
{attribute 'pytmc' := 'pv: TxPDO_state
io: i'}
{attribute 'TcLinkTo' := 'TIIB[PMPS_PRE]^SYNC Inputs^TxPDO state'}
xTxPDO_state AT %I* : BIT;
// Fast faults
ffPMPSIO_Disconnect : FB_FastFault := (i_Desc:='Arbiter network interface disconnected or not OP',i_DevName := sName);
// A request is not considered included until the active cohort number is >= the request cohort number.
{attribute 'pytmc' := 'pv: RequestCohort
io: i'}
nRequestCohort : UDINT := 0; // Request cohort
{attribute 'pytmc' := 'pv: ActiveCohort
io: i'}
nActiveCohort : UDINT := 0; // Active cohort, updated by incoming BP from arbiter PLC, in the ElevateRequest arbiter call
fbVetoArb : FB_VetoArbiter;
fbLog : FB_LogMessage;
END_VAR
//Receiving current BP state
PMPS_GVL.stCurrentBeamParameters := IO_TO_BP(i_stCurrentBP);
PMPS_GVL.stCurrentBeamParameters.xValidToggle := xTxPDO_toggle; // This line must follow the one above. Sequence is important.
// Forwarding BP request for the subsystem
fbVetoArb.bVeto := i_bVeto;
fbVetoArb(HigherAuthority:=THIS^, LowerAuthority:=Arbiter, FFO:=fbFFHWO);
//Broadcasting current request
PMPS_GVL.stRequestedBeamParameters := IO_TO_BP(q_stRequestedBP);
q_stRequestedBP.xValid := TRUE; // This is set and held true here every cycle to prove the PLC on this side is still running
ffPMPSIO_Disconnect(
i_xOK := xTxPDO_state = 0,
io_fbFFHWO := fbFFHWO,
i_xReset := Reset,
i_DevName := sName,
i_TypeCode := 6,
i_xVetoable := FALSE
);
END_FUNCTION_BLOCK
FB_SXU
FUNCTION_BLOCK FB_SXU IMPLEMENTS I_UndulatorComplex
VAR_INPUT
fbElectronEnergy : REFERENCE TO FB_LREALFromEPICS;
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv: CurrentPhotonEnergy
io: i
field: DESC Calculated current photon energy
field: PREC 3
field: EGU eV
'}
fCurrentPhotonEnergy : LREAL;
{attribute 'pytmc' := '
pv: TargetPhotonEnergy
io: i
field: DESC Calculated desired photon energy
field: PREC 3
field: EGU eV
'}
fTargetPhotonEnergy : LREAL;
{attribute 'pytmc' := '
pv: SeedUndulatorNumber
io: i
field: DESC Seed undulator number
'}
nSeedUndulator : UDINT; // Set to zero when no undulators are active
{attribute 'pytmc' := '
pv: TargetSeedUndulatorNumber
io: i
field: DESC Seed undulator number for target K
'}
nTargetSeedUndulator : UDINT; // Set to zero when no undulators are active
END_VAR
VAR
// From lcls-srv01: grep -e KDes /u1/lcls/epics/ioc/data/sioc-unds-uc*/iocInfo/IOC.pvlist |sort
{attribute 'pytmc' := 'pv: 26; link: 2650:'}
fbSegment_26 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 27; link: 2750:'}
fbSegment_27 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 28; link: 2850:'}
fbSegment_28 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 29; link: 2950:'}
fbSegment_29 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 30; link: 3050:'}
fbSegment_30 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 31; link: 3150:'}
fbSegment_31 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 32; link: 3250:'}
fbSegment_32 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 33; link: 3350:'}
fbSegment_33 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 34; link: 3450:'}
fbSegment_34 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 36; link: 3650:'}
fbSegment_36 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 37; link: 3750:'}
fbSegment_37 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 38; link: 3850:'}
fbSegment_38 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 39; link: 3950:'}
fbSegment_39 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 40; link: 4050:'}
fbSegment_40 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 41; link: 4150:'}
fbSegment_41 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 42; link: 4250:'}
fbSegment_42 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 43; link: 4350:'}
fbSegment_43 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 44; link: 4450:'}
fbSegment_44 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 45; link: 4550:'}
fbSegment_45 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 46; link: 4650:'}
fbSegment_46 : FB_UndulatorSegment;
{attribute 'pytmc' := 'pv: 47; link: 4750:'}
fbSegment_47 : FB_UndulatorSegment;
fbSegment : ARRAY [iLowBound..iHighBound] OF POINTER TO FB_UndulatorSegment;
fbCurrentSegment : REFERENCE TO FB_UndulatorSegment;
iIndex : UDINT;
bInitialized : BOOL := FALSE;
END_VAR
VAR CONSTANT
{attribute 'pytmc' := '
pv: FirstSegment
io: i'}
iLowBound : UDINT := 26;
{attribute 'pytmc' := '
pv: LastSegment
io: i'}
iHighBound : UDINT := 47;
{attribute 'pytmc' := '
pv: Period
io: i
field: EGU mm
'}
fPeriod_mm : LREAL := 39.0;
{attribute 'pytmc' := '
pv: LowK
io: i
'}
fLowK : LREAL := 1.5;
{attribute 'pytmc' := '
pv: HiK
io: i
'}
fHiK : LREAL := 5.5;
END_VAR
IF NOT bInitialized THEN
Init();
END_IF
UndAdrUpdate();
nSeedUndulator := 0;
nTargetSeedUndulator := 0;
FOR iIndex := iLowBound TO iHighBound DO
IF fbSegment[iIndex] <> 0 THEN
fbCurrentSegment REF= fbSegment[iIndex]^;
fbCurrentSegment(fbElectronEnergy:=fbElectronEnergy);
//Mark the seed undulator, first undulator operating within K bounds
IF fbCurrentSegment.xActive AND nSeedUndulator = 0 THEN
nSeedUndulator := iIndex;
fCurrentPhotonEnergy := fbCurrentSegment.fPhotonEnergyAct;
END_IF
IF fbCurrentSegment.xTargetActive AND nTargetSeedUndulator = 0 THEN
nTargetSeedUndulator := iIndex;
fTargetPhotonEnergy := fbCurrentSegment.fPhotonEnergyDes;
END_IF
END_IF
END_FOR
IF nSeedUndulator = 0 THEN
fCurrentPhotonEnergy := 0;
END_IF
IF nTargetSeedUndulator = 0 THEN
fTargetPhotonEnergy := 0;
END_IF
END_FUNCTION_BLOCK
ACTION Init:
UndAdrUpdate();
FOR iIndex := iLowBound TO iHighBound DO
IF fbSegment[iIndex] <> 0 THEN
fbCurrentSegment REF= fbSegment[iIndex]^;
fbCurrentSegment.fPeriod_mm := fPeriod_mm;
fbCurrentSegment.fLowK := fLowK;
fbCurrentSegment.fHiK := fHiK;
END_IF
END_FOR
bInitialized := TRUE;
END_ACTION
ACTION UndAdrUpdate:
fbSegment[26] := ADR(fbSegment_26);
fbSegment[27] := ADR(fbSegment_27);
fbSegment[28] := ADR(fbSegment_28);
fbSegment[29] := ADR(fbSegment_29);
fbSegment[30] := ADR(fbSegment_30);
fbSegment[31] := ADR(fbSegment_31);
fbSegment[32] := ADR(fbSegment_32);
fbSegment[33] := ADR(fbSegment_33);
fbSegment[34] := ADR(fbSegment_34);
fbSegment[35] := 0;
fbSegment[36] := ADR(fbSegment_36);
fbSegment[37] := ADR(fbSegment_37);
fbSegment[38] := ADR(fbSegment_38);
fbSegment[39] := ADR(fbSegment_39);
fbSegment[40] := ADR(fbSegment_40);
fbSegment[41] := ADR(fbSegment_41);
fbSegment[42] := ADR(fbSegment_42);
fbSegment[43] := ADR(fbSegment_43);
fbSegment[44] := ADR(fbSegment_44);
fbSegment[45] := ADR(fbSegment_45);
fbSegment[46] := ADR(fbSegment_46);
fbSegment[47] := ADR(fbSegment_47);
END_ACTION
- Related:
FB_UndulatorSegment
FUNCTION_BLOCK FB_UndulatorSegment
VAR_INPUT
(* Undulator period in millimeters, to be set by subclasses *)
fPeriod_mm : LREAL := 1.0;
fbElectronEnergy : REFERENCE TO FB_LREALFromEPICS;
fLowK : LREAL := 0;
fHiK : LREAL := 6;
fKRangeHyst : LREAL := 0.01;
END_VAR
VAR_OUTPUT
{attribute 'pytmc' := '
pv: eVAct
field: DESC Calculated photon energy
field: PREC 3
field: EGU eV
'}
fPhotonEnergyAct : LREAL;
{attribute 'pytmc' := '
pv: eVDes
field: DESC Calculated desired photon energy
field: PREC 3
field: EGU eV
'}
fPhotonEnergyDes : LREAL;
{attribute 'pytmc' := '
pv: Active
io: i
field: DESC Undulator is considered active
'}
xActive : BOOL; // Undulator is considered active
{attribute 'pytmc' := '
pv: TargetActive
io: i
field: DESC Target K would make und active
'}
xTargetActive : BOOL; // Undulator is considered active at this target
{attribute 'pytmc' := '
pv: KAct
io: i
field: DESC Current K
'}
fKAct : LREAL;
{attribute 'pytmc' := '
pv: KDes
io: i
field: DESC Target K
'}
fKDes : LREAL;
{attribute 'pytmc' := '
pv: KActValid
io: i
field: DESC Current K Readback Valid
'}
bKActValid : BOOL;
{attribute 'pytmc' := '
pv: KDesValid
io: i
field: DESC Target K Readback Valid
'}
bKDesValid : BOOL;
END_VAR
VAR
{attribute 'pytmc' := '
pv: KDes
link: KDes
'}
fbKDesired : FB_LREALFromEPICS;
{attribute 'pytmc' := '
pv: KAct
link: KAct
'}
fbKActual : FB_LREALFromEPICS;
END_VAR
fbKDesired();
fbKActual();
fKAct := fbKActual.fValue;
bKActValid := fbKActual.bValid;
fKDes := fbKDesired.fValue;
bKDesValid := fbKDesired.bvalid;
IF __ISVALIDREF(fbElectronEnergy) THEN
IF fbKActual.bValid AND fbElectronEnergy.bValid THEN
fPhotonEnergyAct := F_CalculatePhotonEnergy(
fElectronEnergy_GeV:=fbElectronEnergy.fValue,
fUndulatorPeriod_mm:=fPeriod_mm,
fUndulatorStrength:=fbKActual.fValue
);
//Set this undulator active if actual K is within operational range
xActive S= fLowK <= fbKActual.fValue AND fbKActual.fValue <= fHiK AND
(fbKActual.bValid AND fbKActual.bValid);
xActive R= (fLowK - fKRangeHyst) > fbKActual.fValue OR fbKActual.fValue > (fHiK + fKRangeHyst) OR
(NOT fbKActual.bValid OR NOT fbKActual.bValid);
//Set this undulator active if target K is within operational range
xTargetActive S= fLowK <= fbKDesired.fValue AND fbKDesired.fValue <= fHiK AND
(fbKDesired.bValid AND fbKDesired.bValid);
xTargetActive R= (fLowK - fKRangeHyst) > fbKDesired.fValue OR fbKDesired.fValue > (fHiK + fKRangeHyst) OR
(NOT fbKDesired.bValid OR NOT fbKDesired.bValid);
END_IF
IF fbKDesired.bValid AND fbElectronEnergy.bValid THEN
fPhotonEnergyDes := F_CalculatePhotonEnergy(
fElectronEnergy_GeV:=fbElectronEnergy.fValue,
fUndulatorPeriod_mm:=fPeriod_mm,
fUndulatorStrength:=fbKDesired.fValue
);
END_IF
END_IF
END_FUNCTION_BLOCK
- Related:
FB_VetoArbiter
FUNCTION_BLOCK FB_VetoArbiter IMPLEMENTS I_HigherAuthority
VAR_INPUT
bVeto : BOOL := FALSE; // Rising edge clears request, hold true to veto continuously, falling edge restores request
HigherAuthority : I_HigherAuthority; // Typically connected to a higher-level arbiter.
LowerAuthority : I_LowerAuthority; // Lower authority to be vetoed
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
FFO : FB_HardwareFFOutput; // This should be the FFO upstream of the veto device
END_VAR
VAR
ffKeepItSecretKeepItSafe : FB_FastFault := (
i_xAutoReset := TRUE,
i_Desc := 'Holds beam off until request is back in arbitration',
i_TypeCode := 200,
i_xVetoable := TRUE
);
stStandbyBP : ST_BeamParams;
rtVeto : R_TRIG;
ftVeto : F_TRIG;
END_VAR
rtVeto(CLK:=bVeto);
ftVeto(CLK:=bVeto);
IF rtVeto.Q THEN
HigherAuthority.RemoveRequest(LowerAuthority.nLowerAuthorityID);
HigherAuthority.RequestBP(LowerAuthority.nLowerAuthorityID, PMPS_GVL.cstFullBeam);
ELSIF ftVeto.Q THEN
HigherAuthority.RemoveRequest(LowerAuthority.nLowerAuthorityID);
HigherAuthority.RequestBP(LowerAuthority.nLowerAuthorityID, stStandbyBP);
END_IF
LowerAuthority.ElevateRequest(THIS^);
//Fast fault that holds beam off until the request is added back into the system
// when bVeto goes false.
ffKeepItSecretKeepItSafe.i_xOK := HigherAuthority.CheckRequest(LowerAuthority.nLowerAuthorityID) OR bVeto;
ffKeepItSecretKeepItSafe(io_fbFFHWO:=FFO);
END_FUNCTION_BLOCK
FB_VetoArbiter_Test
{attribute 'call_after_init'}
FUNCTION_BLOCK FB_VetoArbiter_Test EXTENDS TcUnit.FB_TestSuite
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
VAR CONSTANT
ArbID : DWORD := 1;
HigherArbID : DWORD := 2;
END_VAR
VetoArbiter();
END_FUNCTION_BLOCK
FB_VetoDevice
// Relays veto device state, updates current BP and sends a message when veto state changes.
FUNCTION_BLOCK FB_VetoDevice
VAR_INPUT
i_bIn : BOOL;
i_bOut : BOOL;
END_VAR
VAR_OUTPUT
q_bIn : BOOL;
q_bOut : BOOL;
END_VAR
VAR_IN_OUT
stCurrentBP : ST_BeamParams;
END_VAR
VAR
VetoDevice_IN : UINT := PMPS_GVL.MAX_VETO_DEVICES; // Veto device state array index
VetoDevice_OUT : UINT := PMPS_GVL.MAX_VETO_DEVICES; // Veto device state array index
VetoDeviceName : STRING;
{attribute 'instance-path'}
{attribute 'noinit'}
sPath : T_MaxString;
// Logging
fbLogMsg : FB_LogMessage := (
eSubSystem := E_Subsystem.MPS);
rtIn : R_TRIG;
rtOut : R_TRIG;
////////////////////////////////
bInit: BOOL := TRUE;
END_VAR
IF bInit THEN
fbLogMsg.sJson := F_PMPS_JSON(VetoDeviceName, sPath, 1000);
bInit := FALSE;
END_IF
// Log
/////////////////////////////
rtIn(CLK := i_bIn);
rtOut(CLK := i_bOut);
IF rtIn.Q THEN
fbLogMsg.sMsg := CONCAT(VetoDeviceName, ' moved IN');
fbLogMsg();
ELSIF rtOut.Q THEN
fbLogMsg.sMsg := CONCAT(VetoDeviceName, ' moved OUT');
fbLogMsg();
END_IF
// Relay
///////////////////////
q_bIn := i_bIn;
q_bOut := i_bOut;
// Update current beam parameters
/////////////////////////////////////
stCurrentBP.aVetoDevices[VetoDevice_IN] := i_bIn;
stCurrentBP.aVetoDevices[VetoDevice_OUT] := i_bOut;
END_FUNCTION_BLOCK
- Related:
IO_TO_APT
FUNCTION IO_TO_APT : ST_PMPS_Aperture
VAR_INPUT
IO : ST_PMPS_Aperture_IO;
END_VAR
VAR
END_VAR
IO_TO_APT.Height := IO.Height;
IO_TO_APT.Width := IO.Width;
IO_TO_APT.xOK := IO.xOK;
END_FUNCTION
- Related:
IO_TO_ATT
FUNCTION IO_TO_ATT : ST_PMPS_Attenuator
VAR_INPUT
IO : ST_PMPS_Attenuator_IO;
END_VAR
VAR
END_VAR
IO_TO_ATT.nTran := IO.nTran;
IO_TO_ATT.xAttOK := IO.xAttOK;
END_FUNCTION
- Related:
IO_TO_BP
FUNCTION IO_TO_BP : ST_BeamParams
VAR_INPUT
IO : ST_BeamParams_IO;
END_VAR
VAR
idx : UINT;
END_VAR
FOR idx := 1 TO PMPS_GVL.AUX_ATTENUATORS DO
IO_TO_BP.astAttenuators[idx] := IO_TO_ATT(IO.astAttenuators[idx]);
END_FOR
FOR idx := 1 TO PMPS_GVL.MAX_APERTURES DO
IO_TO_BP.astApertures[idx] := IO_TO_APT(IO.astApertures[idx]);
END_FOR
IO_TO_BP.aVetoDevices := IO.aVetoDevices;
IO_TO_BP.nTran := IO.nTran;
IO_TO_BP.nCohortInt := ULINT_TO_UDINT(IO.nCohortInt);
IO_TO_BP.neVRange := IO.neVRange;
IO_TO_BP.nRate := IO.nRate;
IO_TO_BP.xValid := IO.xValid;
IO_TO_BP.xValidToggle := IO.xValidToggle;
END_FUNCTION
- Related:
MAIN
PROGRAM MAIN
VAR
fbSetPERanges : PE_Ranges;
fbDiffBPTest : FB_DiffBP_Test;
fbBPTMTest : FB_BPTM_Test;
fbSafeBPCompareTest : FB_SafeBPCompare_Test;
////fbevWithinSpecTest : FB_evWithinSpec_Test;---
////fbPEWTest : FB_PhotonEnergyWatcher_Test;---
fbFFTest : FB_FastFault_Test;
fbArbiterTest : FB_Arbiter_Test;
fbVetoArbiterTest : FB_VetoArbiter_Test;
fbevRangeCalcTest : FB_evRangeCalculator_Test;
fbSubSysToArbTest : FB_SubsysToArb_Test;
fbArbToSubSysTest : FB_ArbToSubsys_Test;
END_VAR
TcUnit.RUN();
END_PROGRAM
PE_Ranges
// Does nothing other than set the gvl for photon energy bitmask to one of two constants, K or L.
// Workaround for compile defines not fully working for libraries at the time of writing this.
// Otherwise I would have just used the compile define in the GVL declaration.
FUNCTION_BLOCK PE_Ranges
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR
END_FUNCTION_BLOCK