diff --git a/DUTs/ST_TechnosoftMotionError.TcDUT b/DUTs/ST_TechnosoftMotionError.TcDUT new file mode 100644 index 00000000..18d9a89e --- /dev/null +++ b/DUTs/ST_TechnosoftMotionError.TcDUT @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/I_SafetyProvider.TcIO b/DUTs/Safety/I_SafetyProvider.TcIO new file mode 100644 index 00000000..6216f7fc --- /dev/null +++ b/DUTs/Safety/I_SafetyProvider.TcIO @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_LEDState.TcDUT b/DUTs/Safety/PNOZ/ST_LEDState.TcDUT new file mode 100644 index 00000000..a897264a --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_LEDState.TcDUT @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_MCCStatusIn.TcDUT b/DUTs/Safety/PNOZ/ST_MCCStatusIn.TcDUT new file mode 100644 index 00000000..5c8f54e5 --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_MCCStatusIn.TcDUT @@ -0,0 +1,27 @@ + + + + IPC, will not change from cabinet to cabinet +TYPE ST_MCCStatusIn : +STRUCT + bHeartbeat: BIT; //The loop-back signal of the heartbeat signal from the PLC-to-PNOZ-to-PLC. + bEStopOK: BIT; //If no E-Stop connected to this cabinet is pressed, this bit is TRUE. When pressed, it is FALSE. + bRemote: BIT; //When remote mode is active, this allows remote resetting of E-Stop zones from EPICS/NICOS. Remote is active when the physical doors/access into said instruments are locked. + bFault: BIT; //Feedback to safety controller that fault has been acknowledged + bReserved4: BIT; + bReserved5: BIT; + bReserved6: BIT; + bReserved7: BIT; + bReserved8: BIT; + bReserved9: BIT; + bReserved10: BIT; + bReserved11: BIT; + bReserved12: BIT; + bReserved13: BIT; + bReserved14: BIT; + bReserved15: BIT; +END_STRUCT +END_TYPE +]]> + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_MCCStatusOut.TcDUT b/DUTs/Safety/PNOZ/ST_MCCStatusOut.TcDUT new file mode 100644 index 00000000..ef8a4697 --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_MCCStatusOut.TcDUT @@ -0,0 +1,27 @@ + + + + PNOZ, will not change from cabinet to cabinet +TYPE ST_MCCStatusOut : +STRUCT + bHeartbeat: BIT; //Pulsed every ~200ms by the PLC-to-PNOZ, the PNOZ is expected to loop back this signal. + bReserved1: BIT; + bReserved2: BIT; + bErrAck: BIT; //If there is a fault requiring intervention (from cabinet), CabAck will clear it + bReserved4: BIT; + bReserved5: BIT; + bSTOFbk: BIT; + bSTO_OK: BIT; + bReserved8: BIT; + bReserved9: BIT; + bReserved10: BIT; + bReserved11: BIT; + bReserved12: BIT; + bReserved13: BIT; + bReserved14: BIT; + bReserved15: BIT; +END_STRUCT +END_TYPE +]]> + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_PNOZCommDiag.TcDUT b/DUTs/Safety/PNOZ/ST_PNOZCommDiag.TcDUT new file mode 100644 index 00000000..4c2dc0df --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_PNOZCommDiag.TcDUT @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_PNOZEtherCATIn.TcDUT b/DUTs/Safety/PNOZ/ST_PNOZEtherCATIn.TcDUT new file mode 100644 index 00000000..abbef8e8 --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_PNOZEtherCATIn.TcDUT @@ -0,0 +1,15 @@ + + + + PLC EtherCAT input data copied from the raw %I* process image. +TYPE ST_PNOZEtherCATIn : +STRUCT + stMCCStatusIn: ST_MCCStatusIn; //Input Byte 00..01 + stMCC_CustomStatusIn: ST_MCC_CustomStatusIn; //Input Byte 02..03 + astSafetyFunctionIn: ARRAY[1..GVL_APP.nNUM_OF_SAFETY_FUNCTIONS] OF ST_SafetyFunctionIn; //Input Byte 04..15 + stLEDState: ST_LEDState; //Input Byte 16 +END_STRUCT +END_TYPE +]]> + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_PNOZEtherCATOut.TcDUT b/DUTs/Safety/PNOZ/ST_PNOZEtherCATOut.TcDUT new file mode 100644 index 00000000..df215d62 --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_PNOZEtherCATOut.TcDUT @@ -0,0 +1,14 @@ + + + + PNOZ EtherCAT output data mapped to the raw %Q* process image. +TYPE ST_PNOZEtherCATOut : +STRUCT + stMCCStatusOut: ST_MCCStatusOut; //Output Byte 00..01 + stMCC_CustomStatusOut: ST_MCC_CustomStatusOut; //Output Byte 02..03 + astSafetyFunctionOut: ARRAY[1..GVL_APP.nNUM_OF_SAFETY_FUNCTIONS] OF ST_SafetyFunctionOut; //Output Byte 04..15 +END_STRUCT +END_TYPE +]]> + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_SafetyFunctionIn.TcDUT b/DUTs/Safety/PNOZ/ST_SafetyFunctionIn.TcDUT new file mode 100644 index 00000000..335a30a9 --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_SafetyFunctionIn.TcDUT @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/PNOZ/ST_SafetyFunctionOut.TcDUT b/DUTs/Safety/PNOZ/ST_SafetyFunctionOut.TcDUT new file mode 100644 index 00000000..f72d9d6c --- /dev/null +++ b/DUTs/Safety/PNOZ/ST_SafetyFunctionOut.TcDUT @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/ST_Safety.TcDUT b/DUTs/Safety/ST_Safety.TcDUT new file mode 100644 index 00000000..09f0993a --- /dev/null +++ b/DUTs/Safety/ST_Safety.TcDUT @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/ST_SafetyIn.TcDUT b/DUTs/Safety/ST_SafetyIn.TcDUT new file mode 100644 index 00000000..21ac9854 --- /dev/null +++ b/DUTs/Safety/ST_SafetyIn.TcDUT @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/DUTs/Safety/ST_SafetyOut.TcDUT b/DUTs/Safety/ST_SafetyOut.TcDUT new file mode 100644 index 00000000..2850524d --- /dev/null +++ b/DUTs/Safety/ST_SafetyOut.TcDUT @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/GVLs/GVL_Safety.TcGVL b/GVLs/GVL_Safety.TcGVL new file mode 100644 index 00000000..8430e5a8 --- /dev/null +++ b/GVLs/GVL_Safety.TcGVL @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/POUs/FB_PulseGenerator.TcPOU b/POUs/FB_PulseGenerator.TcPOU new file mode 100644 index 00000000..d316525e --- /dev/null +++ b/POUs/FB_PulseGenerator.TcPOU @@ -0,0 +1,97 @@ + + + + + + tUsedFlashTime THEN + tGapTime := tFlashPeriod - tUsedFlashTime; +ELSE + tGapTime := tOffTime; //Fallback gap if configured period is too short +END_IF + +//Select active timer phase +IF bInGap THEN + tActiveTime := tGapTime; + bOut := FALSE; +ELSIF bOut THEN + tActiveTime := tOnTime; +ELSE + tActiveTime := tOffTime; +END_IF + +//Start new timer phase +IF NOT bTimerRun THEN + bTimerRun := TRUE; + tonTimer(IN := FALSE, PT := tActiveTime); +END_IF + +//Run timer +tonTimer(IN := TRUE, PT := tActiveTime); + +//Phase complete +IF tonTimer.Q THEN + tonTimer(IN := FALSE, PT := tActiveTime); + bTimerRun := FALSE; + + IF bInGap THEN + bInGap := FALSE; + nFlashCounter := 0; + bOut := TRUE; //Start next flash group directly after gap + ELSE + IF bOut THEN + bOut := FALSE; + nFlashCounter := nFlashCounter + 1; + + IF nFlashCounter >= nFlashCount THEN + bInGap := TRUE; + END_IF + ELSE + bOut := TRUE; + END_IF + END_IF +END_IF +]]> + + + \ No newline at end of file diff --git a/POUs/Monitoring/FB_ZoneMonitoring.TcPOU b/POUs/Monitoring/FB_ZoneMonitoring.TcPOU new file mode 100644 index 00000000..c8965d4f --- /dev/null +++ b/POUs/Monitoring/FB_ZoneMonitoring.TcPOU @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 AND_THEN iSafetyProvider.ReadFromSafetyController THEN + _EStopReleased := iSafetyProvider.EStopOK; + _STOReleased := iSafetyProvider.STO_OK; +END_IF; + +EStopReleased := _EStopReleased; +]]> + + + + + + aExtraMessageOld[i]); + + //Set the alarm LEDs according to first occurrence + IF NOT bLedActive + AND aAlarmList[i].bLedActive THEN + bLedActive := TRUE; + stMonitorLed.eErrorLED := aAlarmList[i].eErrorLED; + stMonitorLed.eStatusLED := aAlarmList[i].eStatusLED; + END_IF + + //If any error exists requiring an acknowledge, highlight Acknowledge LED. + IF aAlarmList[i].bLedActive AND NOT aAlarmList[i].bLedAutoReset THEN + stMonitorLed.eErrorAcknowledgeLED := aAlarmList[i].eErrorAcknowledgeLED; + END_IF +END_FOR + +aExtraMessageOld := aExtraMessage; //Save last messages for next iteration + +//Copy to output variables +aErrorID := aTempErrorID; +aErrorMsg := aTempErrorMsg; +nAlarmGrpBitNumber := nAlarmBitNumber; + +//Handle LED blinking +fbFastBlinkOn(IN:=NOT fbFastBlinkOff.Q, PT:=T#250MS); +fbFastBlinkOff(IN:=fbFastBlinkOn.Q, PT:=T#250MS); + +fbSlowBlinkOn(IN:=NOT fbSlowBlinkOff.Q, PT:=T#750MS); +fbSlowBlinkOff(IN:=fbSlowBlinkOn.Q, PT:=T#750MS); + +_bSafetyAcknowledgeLED := + (stMonitorLed.eErrorAcknowledgeLED = E_MonitorLed.eSteady) + OR (stMonitorLed.eErrorAcknowledgeLED = E_MonitorLed.eFast AND fbFastBlinkOn.Q) + OR (stMonitorLed.eErrorAcknowledgeLED = E_MonitorLed.eSlow AND fbSlowBlinkOn.Q); + +_bEStopLED := aAlarmList[4].bLedActive; + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/POUs/Safety/FB_MotionSafety.TcPOU b/POUs/Safety/FB_MotionSafety.TcPOU new file mode 100644 index 00000000..18fa7bc4 --- /dev/null +++ b/POUs/Safety/FB_MotionSafety.TcPOU @@ -0,0 +1,393 @@ + + + + + + stSafety.stSafetyOut.stMCCStatusOut.bHeartbeat, + bWatchdogFault => bMCCWatchdogFault + ); //Reset heartbeat generation and watchdog memory + + stSafety.stSafetyOut.stMCCStatusOut.bErrAck := FALSE; //Clear fault-acknowledge command + + fbSafetyAckLedPulse( + bEnable := FALSE, + bOut => bSafetyAcknowledgeLED + ); //Reset pulse generator and physical LED + + mHandleMotionPermission(); //Process falling edge of motion permission + RETURN; +END_IF; + +mReadInputs(stSafetyData := stSafety); +bDataValid := TRUE; + +mMCCStatus(stSafetyData := stSafety); //Heartbeat generation and MCC watchdog check +mTechnosoftStatus(stSafetyData := stSafety); + +mUpdateFaultFeedback(stSafetyData := stSafety); //Fault/Err Ack. handshake +mHandleMotionPermission(); + +]]> + + + + + + + + + + + + + + 100.0 THEN + fPWClamped := 100.0; +END_IF + +//Convert heartbeat period and pulse width into ON time +tHeartbeatOnTime := LREAL_TO_TIME(TIME_TO_LREAL(tHeartbeatPeriod) * fPWClamped / 100.0); + +//Generate heartbeat output +tHeartbeatElapsed := tHeartbeatElapsed + tTaskCycleTime; + +IF tHeartbeatElapsed >= tHeartbeatPeriod THEN + tHeartbeatElapsed := tHeartbeatElapsed - tHeartbeatPeriod; +END_IF + +bHeartbeatOut := tHeartbeatElapsed < tHeartbeatOnTime; + +//Initialize watchdog memory on first enabled scan +IF NOT bMCCHeartbeatInit THEN + bMCCHeartbeatInOld := bHeartbeatIn; + tMCCWatchdogElapsed := T#0MS; + bMCCHeartbeatInit := TRUE; +END_IF + +//Watch incoming heartbeat for any change, rising or falling +IF bHeartbeatIn <> bMCCHeartbeatInOld THEN + bMCCHeartbeatInOld := bHeartbeatIn; + tMCCWatchdogElapsed := T#0MS; +ELSE + IF tMCCWatchdogElapsed < tWatchdogTime THEN + tMCCWatchdogElapsed := tMCCWatchdogElapsed + tTaskCycleTime; + END_IF +END_IF + +//Watchdog result +bWatchdogFault := tMCCWatchdogElapsed >= tWatchdogTime; + +mCheckMCCHeartbeat := NOT bWatchdogFault; +]]> + + + + + + + + + + + + stSafetyData.stSafetyOut.stMCCStatusOut.bHeartbeat, + bWatchdogFault => bMCCWatchdogFault +); + +bAxisEnableAllowed := + stSafetyData.stSafetyIn.stMCCStatusIn.bEStopOK + AND bMCC_OK; +]]> + + + + + + 0 THEN + FOR i := 1 TO GVL_APP.nNUM_OF_TECHNOSOFT DO + stSafetyData.stSafetyIn.astTechnosoftMotionError[i] := + astTechnosoftMotionError[i]; + END_FOR +END_IF + +_bEStopOK := stSafetyData.stSafetyIn.stMCCStatusIn.bEStopOK; + +]]> + + + + + + + + + + + + 0 THEN + FOR nAxes := 1 TO GVL_APP.nNUM_OF_TECHNOSOFT DO + stSafetyData.stSafetyOut.bSTOActive := + stSafetyData.stSafetyOut.bSTOActive + OR stSafetyData.stSafetyIn.astTechnosoftMotionError[nAxes].bSTOActive; + + stSafetyData.stSafetyOut.bTechnosoftMotionErrors := + stSafetyData.stSafetyOut.bTechnosoftMotionErrors + OR F_TechnosoftErrorActive(stError := stSafetyData.stSafetyIn.astTechnosoftMotionError[nAxes]); + END_FOR +END_IF + +IF GVL_APP.nNUM_OF_TECHNOSOFT <> 0 THEN + stSafetyData.stSafetyOut.stMCCStatusOut.bSTOFbk := + stSafetyData.stSafetyOut.bSTOActive; //TRUE when any Technosoft drive reports STO active + + stSafetyData.stSafetyOut.stMCCStatusOut.bSTO_OK := + NOT stSafetyData.stSafetyOut.bTechnosoftMotionErrors; //Preserve the existing no-motion-error feedback +END_IF +]]> + + + + + + bSafetyAcknowledgeLED); +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/POUs/Safety/F_TechnosoftErrorActive.TcPOU b/POUs/Safety/F_TechnosoftErrorActive.TcPOU new file mode 100644 index 00000000..d6bef7ac --- /dev/null +++ b/POUs/Safety/F_TechnosoftErrorActive.TcPOU @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file