Added more components

This commit is contained in:
2025-11-13 09:19:39 +01:00
parent 4ad75a3534
commit ae9667622a
56 changed files with 7117 additions and 296 deletions

View File

@@ -0,0 +1,520 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Valve" Id="{658f8bac-5e87-43a2-9b4c-a425a60acda2}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FINAL FB_Valve
VAR_INPUT
xOpenFeedback AT %I* : BOOL;
xCloseFeedback AT %I* : BOOL;
// Open and close the valve
xAutomaticOpen : BOOL;
// Global switch to dissable all errors
xReleaseErrors : BOOL := TRUE;
// Config input
// Valve configuration parameters
stValveConfig : ST_ValveConfig;
// Release or block change to manual mode
xReleaseManualMode : BOOL;
// Process interlocks
wProcessINTLK : T_INTERLOCK;
wProcessINTLKUsed: T_INTERLOCK;
// Safety interlocks
wSafetyINTLK : T_INTERLOCK;
// Used safety interlocks
wSafetyINTLKUsed: T_INTERLOCK;
// Input to confirm all errors
xConfirmAlarms : BOOL;
// Input to tell the fb thats ist used inside a unit test
// FB will not throw error messages
{attribute 'hide'}
xInUnitTestMode : BOOL := FALSE;
END_VAR
VAR_OUTPUT
// Use xOpenValve for normally closed valves
xOpenValve AT %Q* : BOOL := FALSE;
// Use xCloseValve for normally open valves
xCloseValve AT %Q* : BOOL := TRUE;
// Error in valve active
xError : BOOL;
END_VAR
VAR_IN_OUT
// HMI interface
stHMIInterface : ST_HMI_VALVE_DATA;
END_VAR
VAR
// Internal command for manual mode open request
_xManualOpen : BOOL := FALSE;
// Manual mode active
_xManualModeActive : BOOL := FALSE;
// Automatic mode active
_xAutomaticModeActive : BOOL := TRUE;
// Sum of all activated interlocks
_xProcessINTLKOk : BOOL;
// Sum of all activated process interlocks
_xSafetyINTLKOk : BOOL := TRUE;
// Internal open state of the valve
_xIsOpen : BOOL;
// Internal closed state of the valve
_xIsClosed : BOOL;
// Name of valve
// Will be set in constructor and can be changed with the name property
_sName : STRING;
// Alarm handler for valve did not open
_fbAlarmDidNotOpen : FB_AlarmMessage(stEventEntry := TC_EVENTS.Valve.DidNotOpen, xWithConfirmation := TRUE);
// Alarm handler for valve did not close
_fbAlarmDidNotClose : FB_AlarmMessage(stEventEntry := TC_EVENTS.Valve.DidNotClose, xWithConfirmation := TRUE);
// Internal open command
_xOpenValve : BOOL;
// Helper variables used in reset error flag
_xAlarmsActive : BOOL;
_xInputErrorsActive : BOOL;
// Internal error flags
_xError : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// ===============
// Handle not used
// ===============
IF (NOT stValveConfig.xUsed) THEN
// Clear all pending alarms if there are any
_fbAlarmDidNotOpen(xActive := FALSE, xRelease := FALSE);
_fbAlarmDidNotClose(xActive := FALSE, xRelease := FALSE);
_xError := FALSE;
xError := FALSE;
RETURN;
END_IF
// ===========================
// Reset safetyinterlocks flag
// ===========================
IF xConfirmAlarms AND (NOT _xSafetyINTLKOk) THEN
_xSafetyINTLKOk := TRUE;
END_IF
// =========
// Prechecks
// =========
CheckInterlocks();
// =================
// Handle HMI inputs
// =================
HandleHMIInput();
// ===================================
// Handle opening and closing of valve
// ===================================
_xOpenValve := ((_xManualOpen AND _xManualModeActive AND (NOT _xAutomaticModeActive))
OR ( xAutomaticOpen AND _xAutomaticModeActive AND (NOT _xManualModeActive)));
// Check for interlocks
// If not, use valve state from settings
IF (NOT _xProcessINTLKOk) OR (NOT _xSafetyINTLKOk) THEN
_xOpenValve := stValveConfig.xOpenWhenInterlocksActive;
// Also reset manual open command if safetyinterlocks are set
IF (NOT _xSafetyINTLKOk) AND (_xManualOpen <> stValveConfig.xOpenWhenInterlocksActive) THEN
_xManualOpen := stValveConfig.xOpenWhenInterlocksActive;
END_IF
END_IF
// Handle open close feedback
_xIsOpen := (stValveConfig.xHasOpenFeedback AND xOpenFeedback AND (NOT xCloseFeedback)) OR ((NOT stValveConfig.xHasOpenFeedback) AND _xOpenValve);
_xIsClosed := (stValveConfig.xHasClosedFeedback AND xCloseFeedback AND (NOT xOpenFeedback)) OR ((NOT stValveConfig.xHasClosedFeedback) AND (NOT _xOpenValve));
// ===========================
// Valve did not open in time
// ===========================
_fbAlarmDidNotOpen(
xActive:= _xOpenValve AND (NOT _xIsOpen),
xRelease:= xReleaseErrors,
xAcknowledge:= xConfirmAlarms,
timOnDelay:= stValveConfig.timTimeoutOpen,
timOffDelay:= T#0S,
xInUnitTestMode:= xInUnitTestMode);
// Latch error signal
IF _fbAlarmDidNotOpen.Triggered THEN
_xError := TRUE;
END_IF
// ===========================
// Valve did not close in time
// ===========================
_fbAlarmDidNotClose(
xActive:= (NOT _xOpenValve) AND (NOT _xIsClosed),
xRelease:= xReleaseErrors,
xAcknowledge:= xConfirmAlarms,
timOnDelay:= stValveConfig.timTimeoutClose,
timOffDelay:= T#0S,
xInUnitTestMode:= xInUnitTestMode);
// Latch error signal
IF _fbAlarmDidNotClose.Triggered THEN
_xError := TRUE;
END_IF
// ================
// Reset error flag
// ================
_xAlarmsActive := _fbAlarmDidNotOpen.Active OR _fbAlarmDidNotClose.Active;
_xInputErrorsActive := _fbAlarmDidNotOpen.Triggered OR _fbAlarmDidNotClose.Triggered;
IF xConfirmAlarms AND _xError AND (NOT _xAlarmsActive) AND (NOT _xInputErrorsActive) THEN
_xError := FALSE;
END_IF
// ================================
// Copy internal signals to outputs
// ================================
xOpenValve := _xOpenValve;
xCloseValve := (NOT _xOpenValve);
xError := _xError;
// ==================
// Handle HMI outputs
// ==================
HandleHMIOutput();]]></ST>
</Implementation>
<Method Name="CheckInterlocks" Id="{30a74d3e-2b2c-4675-9a57-4f19eacc7578}">
<Declaration><![CDATA[METHOD PRIVATE FINAL CheckInterlocks
VAR
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Set interlocks ok
_xProcessINTLKOk := TRUE;
// Check if all active interlocks are ok
IF ((wProcessINTLK AND wProcessINTLKUsed) XOR wProcessINTLKUsed) > 0 THEN
_xProcessINTLKOk := FALSE;
END_IF
// Check safety interlocks
// Safety interlocks will not automatically reset
// They need to be reset via the xConfirmAlarms input
IF ((wSafetyINTLK AND wSafetyINTLKUsed) XOR wSafetyINTLKUsed) > 0 THEN
_xSafetyINTLKOk := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="CreateAlarmMSG" Id="{afc73f4f-5060-43b3-9def-13568c293122}">
<Declaration><![CDATA[METHOD PRIVATE CreateAlarmMSG
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Create did not in open alarm
_fbAlarmDidNotOpen.Arguments.Clear().AddString(_sName);
// Create did not close alarm
_fbAlarmDidNotClose.Arguments.Clear().AddString(_sName);]]></ST>
</Implementation>
</Method>
<Method Name="FB_init" Id="{0feef5ff-7efd-4523-9e63-83898cab15b4}">
<Declaration><![CDATA[//FB_Init ist immer implizit verfügbar und wird primär für die Initialisierung verwendet.
//Der Rückgabewert wird nicht ausgewertet. Für gezielte Einflussnahme können Sie
//die Methoden explizit deklarieren und darin mit dem Standard-Initialisierungscode
//zusätzlichen Code bereitstellen. Sie können den Rückgabewert auswerten.
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL; // TRUE: Die Retain-Variablen werden initialisiert (Reset warm / Reset kalt)
bInCopyCode: BOOL; // TRUE: Die Instanz wird danach in den Kopiercode kopiert (Online-Change)
sName : STRING(80);
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := sName;
// Create alarm messages
CreateAlarmMSG();]]></ST>
</Implementation>
</Method>
<Method Name="HandleHMIInput" Id="{daeab2cd-6c5b-4055-97d1-f9300c25c2a5}">
<Declaration><![CDATA[METHOD PRIVATE HandleHMIInput
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[{warning disable C0371}
// Handle automatic mode request
IF stHMIInterface.stAutomaticButton.xRequest AND (NOT _xAutomaticModeActive) THEN
_xAutomaticModeActive := TRUE;
_xManualModeActive := FALSE;
END_IF
IF stHMIInterface.stAutomaticButton.xRequest THEN
stHMIInterface.stAutomaticButton.xRequest := FALSE;
END_IF
// Handle manual mode request
IF stHMIInterface.stManualButton.xRequest AND (NOT _xManualModeActive) AND xReleaseManualMode THEN
_xAutomaticModeActive := FALSE;
_xManualModeActive := TRUE;
// Copy last requested valve state into manual
_xManualOpen := xAutomaticOpen;
END_IF
IF stHMIInterface.stManualButton.xRequest THEN
stHMIInterface.stManualButton.xRequest := FALSE;
END_IF
// Handle open request
IF stHMIInterface.stOpenButton.xRequest AND _xManualModeActive AND _xProcessINTLKOk AND _xSafetyINTLKOk THEN
_xManualOpen := TRUE;
END_IF
IF stHMIInterface.stOpenButton.xRequest THEN
stHMIInterface.stOpenButton.xRequest := FALSE;
END_IF
// Handle close request
IF stHMIInterface.stCloseButton.xRequest AND _xManualModeActive THEN
_xManualOpen := FALSE;
END_IF
IF stHMIInterface.stCloseButton.xRequest THEN
stHMIInterface.stCloseButton.xRequest := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="HandleHMIOutput" Id="{89ada59a-03b1-4917-880c-76f3bba59ef0}">
<Declaration><![CDATA[METHOD PRIVATE HandleHMIOutput : BOOL
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[{warning disable C0371}
// Handle open closed feedbacks
IF (NOT _xIsOpen) AND (NOT _xIsClosed) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.TRANSITIONING;
END_IF
IF _xIsOpen AND (NOT _xIsClosed) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.OPENED;
END_IF
IF _xIsClosed AND (NOT _xIsOpen) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.CLOSED;
END_IF
IF _xError THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.ERROR;
END_IF
// Copy config data to hmi interface
stHMIInterface.xUsed := stValveConfig.xUsed;
stHMIInterface.sName := _sName;
// Handle mode button feedback
IF _xManualModeActive THEN
stHMIInterface.stManualButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stManualButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
IF _xAutomaticModeActive THEN
stHMIInterface.stAutomaticButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stAutomaticButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
// Handle open close button feedback
IF _xIsOpen THEN
stHMIInterface.stOpenButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stOpenButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
IF _xIsClosed THEN
stHMIInterface.stCloseButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stCloseButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
// Handle release button signals
stHMIInterface.stManualButton.xRelease := xReleaseManualMode AND (NOT _xManualModeActive);
stHMIInterface.stAutomaticButton.xRelease := (NOT _xAutomaticModeActive);
stHMIInterface.stOpenButton.xRelease := _xManualModeActive AND _xProcessINTLKOk AND _xSafetyINTLKOk;
stHMIInterface.stCloseButton.xRelease := _xManualModeActive;
// Handle current mode
IF _xManualModeActive AND (NOT _xAutomaticModeActive) THEN
stHMIInterface.iCurrentMode := E_HMI_MODE.MANUAL;
END_IF
IF _xAutomaticModeActive AND (NOT _xManualModeActive) THEN
stHMIInterface.iCurrentMode := E_HMI_MODE.AUTOMATIC;
END_IF
// Handle interlock status
stHMIInterface.stInterlock.wProcessINTLKStatus := wProcessINTLK;
stHMIInterface.stInterlock.wProcessINTLKUsed := wProcessINTLKUsed;
stHMIInterface.stInterlock.xProcessINTLKOk := _xProcessINTLKOk;
stHMIInterface.stInterlock.wSafetyINTLKStatus := wSafetyINTLK;
stHMIInterface.stInterlock.wSafetyINTLKUsed := wSafetyINTLKUsed;
stHMIInterface.stInterlock.xSafetyINTLKOk := _xSafetyINTLKOk;]]></ST>
</Implementation>
</Method>
<Property Name="IsClosed" Id="{1f4ebe6c-a98f-48b1-8a5f-f4c1debefa5c}">
<Declaration><![CDATA[PROPERTY IsClosed : BOOL
]]></Declaration>
<Get Name="Get" Id="{ef140b66-3db4-4eb7-87ae-680649452cd3}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsClosed := _xIsClosed;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsInAutomaticMode" Id="{d767111f-e137-4db5-8520-de2e239783bd}">
<Declaration><![CDATA[PROPERTY IsInAutomaticMode : BOOL
]]></Declaration>
<Get Name="Get" Id="{203d88c3-1caf-4d92-8292-06e60d21d256}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsInAutomaticMode := _xAutomaticModeActive AND (NOT _xManualModeActive);
]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsInManualMode" Id="{ddd53775-56a5-419a-a26e-07865c412725}">
<Declaration><![CDATA[PROPERTY IsInManualMode : BOOL
]]></Declaration>
<Get Name="Get" Id="{93872f82-0dd1-4fc7-bfb1-ae593ff57248}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsInManualMode := _xManualModeActive AND (NOT _xAutomaticModeActive);]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsOpen" Id="{6afdd2c7-3934-4136-a18f-ea14a935f5ef}">
<Declaration><![CDATA[PROPERTY IsOpen : BOOL
]]></Declaration>
<Get Name="Get" Id="{3d3206fd-7dcd-4c1d-a91d-4513ff154aa6}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsOpen := _xIsOpen;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="Name" Id="{6475e552-32d7-4dcc-b692-9b5c114c6e55}">
<Declaration><![CDATA[PROPERTY Name : STRING(80)]]></Declaration>
<Get Name="Get" Id="{3c945911-435e-4362-80a0-23de08a8aa01}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[Name := _sName;]]></ST>
</Implementation>
</Get>
<Set Name="Set" Id="{e21a3263-d456-45aa-b72b-78833f59308c}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := Name;
// Recreate alarm messages after name change
CreateAlarmMSG();]]></ST>
</Implementation>
</Set>
</Property>
<Property Name="ProcessInterlocksOK" Id="{5304af5f-5f38-41ac-bae3-41072677ae5b}">
<Declaration><![CDATA[PROPERTY ProcessInterlocksOK : BOOL
]]></Declaration>
<Get Name="Get" Id="{c7a36fe9-16d1-4e6a-840a-95721198ae41}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[ProcessInterlocksOK := _xProcessINTLKOk;]]></ST>
</Implementation>
</Get>
</Property>
<Method Name="ReqAutomaticMode" Id="{8d7ea6da-e5b9-4d22-89bf-8b742e336da8}">
<Declaration><![CDATA[METHOD ReqAutomaticMode
]]></Declaration>
<Implementation>
<ST><![CDATA[_xAutomaticModeActive := TRUE;
_xManualModeActive := FALSE;]]></ST>
</Implementation>
</Method>
<Method Name="ReqManualMode" Id="{3b374714-1bdd-4b5e-a971-5605e320e287}">
<Declaration><![CDATA[METHOD ReqManualMode
]]></Declaration>
<Implementation>
<ST><![CDATA[IF xReleaseManualMode THEN
_xManualModeActive := TRUE;
_xAutomaticModeActive := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Property Name="SafetyInterlocksOK" Id="{cfa4b65a-d169-4f4b-ad4b-6e2d8e24ce3a}">
<Declaration><![CDATA[{attribute 'analysis' := '-31'}
PROPERTY SafetyInterlocksOK : BOOL]]></Declaration>
<Get Name="Get" Id="{fa7d25b7-22e3-4e23-afcd-0ace84239f31}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[SafetyInterlocksOK := _xSafetyINTLKOk;]]></ST>
</Implementation>
</Get>
</Property>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,689 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_ValveAnalog" Id="{54d19eca-c234-46d9-92a2-1587aba4973c}" SpecialFunc="None">
<Declaration><![CDATA[{attribute 'reflection'}
FUNCTION_BLOCK FINAL FB_ValveAnalog
VAR_INPUT
rSPAutomatic : REAL;
iFeedbackValue AT %I* : INT;
xFeedbackUnderrange AT %I* : BOOL;
xFeedbackOverrange AT %I* : BOOL;
xErrorCard AT %I* : BOOL;
xOpenFeedback AT %I* : BOOL;
xCloseFeedback AT %I* : BOOL;
// Open and close the valve
xAutomaticOpen : BOOL;
// Global switch to dissable all errors
xReleaseErrors : BOOL := TRUE;
// Config input
// Valve configuration parameters
stValveConfig : ST_ValveAnalogConfig;
// Release or block change to manual mode
xReleaseManualMode : BOOL;
// Process interlocks
wProcessINTLK : T_INTERLOCK;
wProcessINTLKUsed: T_INTERLOCK;
// Safety interlocks
wSafetyINTLK : T_INTERLOCK;
// Used safety interlocks
wSafetyINTLKUsed: T_INTERLOCK;
// Input to confirm all errors
xConfirmAlarms : BOOL;
// Input to tell the fb thats ist used inside a unit test
// FB will not throw error messages
{attribute 'hide'}
xInUnitTestMode : BOOL := FALSE;
END_VAR
VAR_OUTPUT
// Ouput setpoint
iSetpoint AT %Q* : INT;
// Error in valve active
xError : BOOL;
END_VAR
VAR_IN_OUT
// HMI interface
stHMIInterface : ST_HMI_ANALOG_VALVE_DATA;
END_VAR
VAR
// Internal command for manual mode open request
_xManualOpen : BOOL := FALSE;
// Manual mode active
_xManualModeActive : BOOL := FALSE;
// Automatic mode active
_xAutomaticModeActive : BOOL := TRUE;
// Sum of all activated interlocks
_xProcessINTLKOk : BOOL;
// Sum of all activated process interlocks
_xSafetyINTLKOk : BOOL := TRUE;
// Internal open state of the valve
_xIsOpen : BOOL;
// Internal closed state of the valve
_xIsClosed : BOOL;
// Name of valve
// Will be set in constructor and can be changed with the name property
_sName : STRING;
// Analog input
{attribute 'hide'}
_fbAnalogInput : FB_AnalogInput('');
// analog output
{attribute 'hide'}
_fbAnalogOutput : FB_AnalogOutput('');
// Internal setpoint
_rSetpoint : REAL;
{attribute 'is_connected' := 'xFeedbackUnderrange'}
_xHasUnderrangeFeedback : BOOL;
{attribute 'is_connected' := 'xFeedbackOverrange'}
_xHasOverrangeFeedback : BOOL;
{attribute 'is_connected' := 'xErrorCard'}
_xHasCardError : BOOL;
// Current valve position
_rCurrentValvePosition : REAL;
// Alarm handler for valve did not open
_fbAlarmDidNotOpen : FB_AlarmMessage(stEventEntry := TC_EVENTS.Valve.DidNotOpen, xWithConfirmation := TRUE);
// Alarm handler for valve did not close
_fbAlarmDidNotClose : FB_AlarmMessage(stEventEntry := TC_EVENTS.Valve.DidNotClose, xWithConfirmation := TRUE);
// Alarm handler for not in range
_fbAlarmNotInRange : FB_AlarmMessage(stEventEntry := TC_EVENTS.Valve.NotInRange, xWithConfirmation := TRUE);
// Analog input error
_xAnalogInputError : BOOL;
// analog output error
_xAnalogOutputError : BOOL;
// Analog input warning active
_xWarningAnalogInput : BOOL;
// Calculated allowed window for process value
_rPVTargetMax : REAL;
_rPVTargetMin : REAL;
// Error for not beeing in range
_xNotInRange : BOOL;
// Internal open command
_xOpenValve : BOOL;
// Helper variables used in reset error flag
_xAlarmsActive : BOOL;
_xInputErrorsActive : BOOL;
// Internal error flags
_xError : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// ===============
// Handle not used
// ===============
IF (NOT stValveConfig.xUsed) THEN
// Clear all pending alarms if there are any
_fbAlarmDidNotOpen(xActive := FALSE, xRelease := FALSE);
_fbAlarmDidNotClose(xActive := FALSE, xRelease := FALSE);
_fbAlarmNotInRange(xActive := FALSE, xRelease := FALSE);
_xAnalogOutputError := FALSE;
_xAnalogInputError := FALSE;
_xWarningAnalogInput := FALSE;
_xError := FALSE;
xError := FALSE;
RETURN;
END_IF
// ===========================
// Reset safetyinterlocks flag
// ===========================
IF xConfirmAlarms AND (NOT _xSafetyINTLKOk) THEN
_xSafetyINTLKOk := TRUE;
END_IF
// =========
// Prechecks
// =========
CheckInterlocks();
// =================
// Handle HMI inputs
// =================
HandleHMIInput();
// ==============================
// Check if all interlocks are ok
// ==============================
// Only reset manual command if safetyinterlocks are not ok
IF (NOT _xSafetyINTLKOk) AND _xManualOpen THEN
// Also reset manual open command if safetyinterlocks are set
_xManualOpen := FALSE;
END_IF
// ===================================
// Handle opening and closing of valve
// ===================================
_xOpenValve := _xProcessINTLKOk AND _xSafetyINTLKOk AND ((_xManualOpen AND _xManualModeActive AND (NOT _xAutomaticModeActive)) OR ( xAutomaticOpen AND _xAutomaticModeActive AND (NOT _xManualModeActive)));
IF _xOpenValve THEN
IF _xManualModeActive AND (NOT _xAutomaticModeActive) THEN
_rSetpoint := stHMIInterface.stSetpoint.rValue;
ELSIF _xAutomaticModeActive AND (NOT _xManualModeActive) THEN
_rSetpoint := rSPAutomatic;
ELSE
_rSetpoint := 0.0;
END_IF
ELSE
IF (NOT _xProcessINTLKOk) OR (NOT _xSafetyINTLKOk) THEN
_rSetpoint := stValveConfig.rSetpointWhenInterlocksActive;
ELSE
_rSetpoint := 0.0;
END_IF
END_IF
// ====================
// Handle analog output
// ====================
_fbAnalogOutput(
rSetpoint:= _rSetpoint,
stAnalogIOConfig:= stValveConfig.stAnalogOutputConfig,
xReleaseErrors:= xReleaseErrors,
xConfirmAlarms:= xConfirmAlarms,
xInUnitTestMode := xInUnitTestMode,
iAnalogValue=> iSetpoint,
xError => _xAnalogOutputError,
stHMIInterface:= stHMIInterface.stSetpoint);
IF _fbAnalogOutput.xError THEN
_xError := TRUE;
END_IF
// ===================
// Handle analog input
// ===================
IF stValveConfig.xHasAnalogFeedback THEN
stValveConfig.stAnalogInputConfig.xUsed := TRUE;
ELSE
stValveConfig.stAnalogInputConfig.xUsed := FALSE;
END_IF
_fbAnalogInput(
iAnalogValue:= iFeedbackValue,
xUnderrange:= _xHasUnderrangeFeedback AND xFeedbackUnderrange,
xOverrange:= _xHasOverrangeFeedback AND xFeedbackOverrange,
xErrorCard:= _xHasCardError AND xErrorCard,
stAnalogIOConfig:= stValveConfig.stAnalogInputConfig,
stAnalogEWConfig:= stValveConfig.stAnalogInputEWConfig,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= FALSE,
xReleaseHardwareErrors:= TRUE,
xInUnitTestMode := xInUnitTestMode,
xConfirmAlarms:= xConfirmAlarms,
rScaledValue=> _rCurrentValvePosition,
xError=> _xAnalogInputError,
xWarning=> _xWarningAnalogInput,
xErrorLow=> ,
xWarningLow=> ,
xWarningHigh=> ,
xErrorHigh=> );
IF _fbAnalogInput.xError THEN
_xError := TRUE;
END_IF
IF NOT stValveConfig.xHasAnalogFeedback THEN
_rCurrentValvePosition := _rSetpoint;
END_IF
// Handle open close feedback
_xIsOpen := (stValveConfig.xHasOpenFeedback AND xOpenFeedback AND (NOT xCloseFeedback)) OR ((NOT stValveConfig.xHasOpenFeedback) AND (_rCurrentValvePosition >= stValveConfig.rPVIsOpen));
_xIsClosed := (stValveConfig.xHasClosedFeedback AND xCloseFeedback AND (NOT xOpenFeedback)) OR ((NOT stValveConfig.xHasClosedFeedback) AND (_rCurrentValvePosition <= stValveConfig.rPVIsOpen));
// Calculate target tolerance
_rPVTargetMax := _rSetpoint + stValveConfig.rTargetTolerance;
_rPVTargetMin := _rSetpoint - stValveConfig.rTargetTolerance;
// Check if valve position is in target
_xNotInRange := stValveConfig.xHasAnalogFeedback AND ((_rCurrentValvePosition < _rPVTargetMin) OR (_rCurrentValvePosition > _rPVTargetMax));
// ==================
// Not in range alarm
// ==================
_fbAlarmNotInRange(
xActive:= _xNotInRange,
xRelease:= xReleaseErrors,
xAcknowledge:= xConfirmAlarms,
timOnDelay:= stValveConfig.timNotInRange,
timOffDelay:= T#0S,
xInUnitTestMode:= xInUnitTestMode);
// Latch error signal
IF _fbAlarmNotInRange.Triggered THEN
_xError := TRUE;
END_IF
// ===========================
// Valve did not open in time
// ===========================
_fbAlarmDidNotOpen(
xActive:= _xOpenValve AND (NOT _xIsOpen),
xRelease:= xReleaseErrors,
xAcknowledge:= xConfirmAlarms,
timOnDelay:= stValveConfig.timTimeoutOpen,
timOffDelay:= T#0S,
xInUnitTestMode:= xInUnitTestMode);
// Latch error signal
IF _fbAlarmDidNotOpen.Triggered THEN
_xError := TRUE;
END_IF
// ===========================
// Valve did not close in time
// ===========================
_fbAlarmDidNotClose(
xActive:= (NOT _xOpenValve) AND (NOT _xIsClosed),
xRelease:= xReleaseErrors,
xAcknowledge:= xConfirmAlarms,
timOnDelay:= stValveConfig.timTimeoutClose,
timOffDelay:= T#0S,
xInUnitTestMode:= xInUnitTestMode);
// Latch error signal
IF _fbAlarmDidNotClose.Triggered THEN
_xError := TRUE;
END_IF
// ================
// Reset error flag
// ================
_xAlarmsActive := _fbAlarmDidNotOpen.Active OR _fbAlarmDidNotClose.Active OR _fbAlarmNotInRange.Raised OR _xAnalogInputError OR _xAnalogOutputError;
_xInputErrorsActive := _fbAlarmDidNotOpen.Triggered OR _fbAlarmDidNotClose.Triggered OR _fbAlarmNotInRange.Triggered;
IF xConfirmAlarms AND _xError AND (NOT _xAlarmsActive) AND (NOT _xInputErrorsActive) THEN
_xError := FALSE;
END_IF
// ================================
// Copy internal signals to outputs
// ================================
xError := _xError;
// ==================
// Handle HMI outputs
// ==================
HandleHMIOutput();]]></ST>
</Implementation>
<Method Name="CheckInterlocks" Id="{8b1816d5-ab17-471a-8a0b-10f53aacd0e5}">
<Declaration><![CDATA[METHOD PRIVATE FINAL CheckInterlocks
VAR
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Set interlocks ok
_xProcessINTLKOk := TRUE;
// Check if all active interlocks are ok
IF ((wProcessINTLK AND wProcessINTLKUsed) XOR wProcessINTLKUsed) > 0 THEN
_xProcessINTLKOk := FALSE;
END_IF
// Check safety interlocks
// Safety interlocks will not automatically reset
// They need to be reset via the xConfirmAlarms input
IF ((wSafetyINTLK AND wSafetyINTLKUsed) XOR wSafetyINTLKUsed) > 0 THEN
_xSafetyINTLKOk := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="CreateAlarmMSG" Id="{c81e16aa-3869-4b2a-9f6c-7c975920c18f}">
<Declaration><![CDATA[METHOD PRIVATE CreateAlarmMSG
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Create did not in open alarm
_fbAlarmDidNotOpen.Arguments.Clear().AddString(_sName);
// Create did not close alarm
_fbAlarmDidNotClose.Arguments.Clear().AddString(_sName);
// Create did not in range alarm
_fbAlarmNotInRange.Arguments.Clear().AddString(_sName);]]></ST>
</Implementation>
</Method>
<Method Name="FB_init" Id="{c1ee06ac-eaad-41e8-80a7-e09a4d61cbeb}">
<Declaration><![CDATA[//FB_Init ist immer implizit verfügbar und wird primär für die Initialisierung verwendet.
//Der Rückgabewert wird nicht ausgewertet. Für gezielte Einflussnahme können Sie
//die Methoden explizit deklarieren und darin mit dem Standard-Initialisierungscode
//zusätzlichen Code bereitstellen. Sie können den Rückgabewert auswerten.
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL; // TRUE: Die Retain-Variablen werden initialisiert (Reset warm / Reset kalt)
bInCopyCode: BOOL; // TRUE: Die Instanz wird danach in den Kopiercode kopiert (Online-Change)
sName : STRING(80);
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := sName;
// Name the analog input and output
_fbAnalogInput.Name := CONCAT(_sName, '.AnalogInput');
_fbAnalogOutput.Name := CONCAT(_sName, '.AnalogOutput');
// Create alarm messages
CreateAlarmMSG();]]></ST>
</Implementation>
</Method>
<Method Name="HandleHMIInput" Id="{5eccb8ad-99f3-45d0-9d3c-03af7601217d}">
<Declaration><![CDATA[METHOD PRIVATE HandleHMIInput
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[{warning disable C0371}
// Handle automatic mode request
IF stHMIInterface.stAutomaticButton.xRequest AND (NOT _xAutomaticModeActive) THEN
_xAutomaticModeActive := TRUE;
_xManualModeActive := FALSE;
END_IF
IF stHMIInterface.stAutomaticButton.xRequest THEN
stHMIInterface.stAutomaticButton.xRequest := FALSE;
END_IF
// Handle manual mode request
IF stHMIInterface.stManualButton.xRequest AND (NOT _xManualModeActive) AND xReleaseManualMode THEN
_xAutomaticModeActive := FALSE;
_xManualModeActive := TRUE;
// Copy last requested valve state into manual
_xManualOpen := xAutomaticOpen;
stHMIInterface.stSetpoint.rValue := rSPAutomatic;
END_IF
IF stHMIInterface.stManualButton.xRequest THEN
stHMIInterface.stManualButton.xRequest := FALSE;
END_IF
// Handle open request
IF stHMIInterface.stOpenButton.xRequest AND _xManualModeActive AND _xProcessINTLKOk AND _xSafetyINTLKOk THEN
_xManualOpen := TRUE;
END_IF
IF stHMIInterface.stOpenButton.xRequest THEN
stHMIInterface.stOpenButton.xRequest := FALSE;
END_IF
// Handle close request
IF stHMIInterface.stCloseButton.xRequest AND _xManualModeActive THEN
_xManualOpen := FALSE;
END_IF
IF stHMIInterface.stCloseButton.xRequest THEN
stHMIInterface.stCloseButton.xRequest := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="HandleHMIOutput" Id="{78e41bf5-d7d4-4955-92e5-e51e67c4444d}">
<Declaration><![CDATA[METHOD PRIVATE HandleHMIOutput : BOOL
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[{warning disable C0371}
// Handle open closed feedbacks
IF (NOT _xIsOpen) AND (NOT _xIsClosed) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.TRANSITIONING;
END_IF
IF _xIsOpen AND (NOT _xIsClosed) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.OPENED;
END_IF
IF _xIsClosed AND (NOT _xIsOpen) AND (NOT _xError) THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.CLOSED;
END_IF
IF _xError THEN
stHMIInterface.iStatus := E_HMI_VALVE_STATUS.ERROR;
END_IF
// Copy analog in_out data
stHMIInterface.stProcessValue.sName := 'Actual position';
stHMIInterface.stProcessValue.rValue := _rCurrentValvePosition;
stHMIInterface.stSetpoint.sName := 'Target position';
stHMIInterface.stSetpoint.rMin := stHMIInterface.stProcessValue.rMin;
stHMIInterface.stSetpoint.rMax := stHMIInterface.stProcessValue.rMax;
stHMIInterface.stSetpoint.sUnit := stHMIInterface.stProcessValue.sUnit;
// Copy config data to hmi interface
stHMIInterface.xUsed := stValveConfig.xUsed;
stHMIInterface.sName := _sName;
// Handle mode button feedback
IF _xManualModeActive THEN
stHMIInterface.stManualButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stManualButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
IF _xAutomaticModeActive THEN
stHMIInterface.stAutomaticButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stAutomaticButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
// Handle open close button feedback
IF _xIsOpen THEN
stHMIInterface.stOpenButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stOpenButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
IF _xIsClosed THEN
stHMIInterface.stCloseButton.eFeedback := E_HMI_BUTTON_FEEDBACK.ACTIVE;
ELSE
stHMIInterface.stCloseButton.eFeedback := E_HMI_BUTTON_FEEDBACK.NONE;
END_IF
// Handle release button signals
stHMIInterface.stManualButton.xRelease := xReleaseManualMode AND (NOT _xManualModeActive);
stHMIInterface.stAutomaticButton.xRelease := (NOT _xAutomaticModeActive);
stHMIInterface.stOpenButton.xRelease := _xManualModeActive AND _xProcessINTLKOk AND _xSafetyINTLKOk;
stHMIInterface.stCloseButton.xRelease := _xManualModeActive;
// Handle current mode
IF _xManualModeActive AND (NOT _xAutomaticModeActive) THEN
stHMIInterface.iCurrentMode := E_HMI_MODE.MANUAL;
END_IF
IF _xAutomaticModeActive AND (NOT _xManualModeActive) THEN
stHMIInterface.iCurrentMode := E_HMI_MODE.AUTOMATIC;
END_IF
// Handle interlock status
stHMIInterface.stInterlock.wProcessINTLKStatus := wProcessINTLK;
stHMIInterface.stInterlock.wProcessINTLKUsed := wProcessINTLKUsed;
stHMIInterface.stInterlock.xProcessINTLKOk := _xProcessINTLKOk;
stHMIInterface.stInterlock.wSafetyINTLKStatus := wSafetyINTLK;
stHMIInterface.stInterlock.wSafetyINTLKUsed := wSafetyINTLKUsed;
stHMIInterface.stInterlock.xSafetyINTLKOk := _xSafetyINTLKOk;]]></ST>
</Implementation>
</Method>
<Property Name="IsClosed" Id="{b6c1710c-9aab-4569-b47e-788596401de5}">
<Declaration><![CDATA[PROPERTY IsClosed : BOOL
]]></Declaration>
<Get Name="Get" Id="{6a0b898d-b4dd-42f2-8af5-0cbc0b739a53}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsClosed := _xIsClosed;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsInAutomaticMode" Id="{c23f1e57-d41d-447f-ba1b-ef6a2091e1ee}">
<Declaration><![CDATA[PROPERTY IsInAutomaticMode : BOOL
]]></Declaration>
<Get Name="Get" Id="{7da816e7-e04c-4f70-b8a6-974fe4187b1c}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsInAutomaticMode := _xAutomaticModeActive AND (NOT _xManualModeActive);
]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsInManualMode" Id="{cb02ea4c-34fb-4c1c-bc05-be290f13e9cc}">
<Declaration><![CDATA[PROPERTY IsInManualMode : BOOL
]]></Declaration>
<Get Name="Get" Id="{b9145693-63b3-4bde-b741-a96047fb952a}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsInManualMode := _xManualModeActive AND (NOT _xAutomaticModeActive);]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="IsOpen" Id="{14c8ecb1-93c0-4640-a72a-37068aa931c0}">
<Declaration><![CDATA[PROPERTY IsOpen : BOOL
]]></Declaration>
<Get Name="Get" Id="{9f1a9fc3-9760-4f2d-977c-2b77a4848604}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IsOpen := _xIsOpen;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="Name" Id="{a9acd029-3ab7-4f0b-866d-4e5e120b76d7}">
<Declaration><![CDATA[PROPERTY Name : STRING(80)]]></Declaration>
<Get Name="Get" Id="{2a298056-2f0c-40dd-93f1-0193ef39b177}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[Name := _sName;]]></ST>
</Implementation>
</Get>
<Set Name="Set" Id="{4439378f-18e8-4038-8e1b-e9c4c0e99771}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := Name;
// Rename the analog input and output
_fbAnalogInput.Name := CONCAT(_sName, '.AnalogInput');
_fbAnalogOutput.Name := CONCAT(_sName, '.AnalogOutput');
// Recreate alarm messages after name change
CreateAlarmMSG();]]></ST>
</Implementation>
</Set>
</Property>
<Property Name="ProcessInterlocksOK" Id="{6eca8eb7-6785-41bf-a07d-24fa126ee630}">
<Declaration><![CDATA[PROPERTY ProcessInterlocksOK : BOOL
]]></Declaration>
<Get Name="Get" Id="{063a0fc0-f2e2-4438-82b9-93ba2a524eb0}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[ProcessInterlocksOK := _xProcessINTLKOk;]]></ST>
</Implementation>
</Get>
</Property>
<Method Name="ReqAutomaticMode" Id="{dc5b5371-d320-47c3-9450-5d2ea8274952}">
<Declaration><![CDATA[METHOD ReqAutomaticMode
]]></Declaration>
<Implementation>
<ST><![CDATA[_xAutomaticModeActive := TRUE;
_xManualModeActive := FALSE;]]></ST>
</Implementation>
</Method>
<Method Name="ReqManualMode" Id="{e9adf596-9367-4445-83ca-794c5fdbc5db}">
<Declaration><![CDATA[METHOD ReqManualMode
]]></Declaration>
<Implementation>
<ST><![CDATA[IF xReleaseManualMode THEN
_xManualModeActive := TRUE;
_xAutomaticModeActive := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Property Name="SafetyInterlocksOK" Id="{4a127995-0516-47b5-bd54-a8856404b497}">
<Declaration><![CDATA[{attribute 'analysis' := '-31'}
PROPERTY SafetyInterlocksOK : BOOL]]></Declaration>
<Get Name="Get" Id="{74eb6ffc-1090-4dcc-bb8f-03a5108315f7}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[SafetyInterlocksOK := _xSafetyINTLKOk;]]></ST>
</Implementation>
</Get>
</Property>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_ValveAnalogConfig" Id="{8ae10abc-4856-40f8-b7e0-08933552acd7}">
<Declaration><![CDATA[TYPE ST_ValveAnalogConfig :
STRUCT
// Analog value when valve is considered open
// defaults to >=5%
{attribute 'OPC.UA.DA' := '1'}
rPVIsOpen : REAL := 5.0;
// Maximum allowable difference between setpoint and process value
// Defaults to +-5
{attribute 'OPC.UA.DA' := '1'}
rTargetTolerance : REAL := 5.0;
// Time for the valve to get to the requested setpoint
{attribute 'OPC.UA.DA' := '1'}
timNotInRange : TIME := T#30S;
// Config parameters for the analog input
stAnalogInputConfig : ST_ANALOG_IO_CONFIG := (iAIMax := 32767, iAIMin := 0, rPVMax := 100, rPVMin := 0);
stAnalogInputEWConfig : ST_ANALOG_EW_CONFIG;
// Config parameters for the analog output
stAnalogOutputConfig : ST_ANALOG_IO_CONFIG := (iAIMax := 32767, iAIMin := 0, rPVMax := 100, rPVMin := 0);
// Timeout for the valve to open
// 0 = deactivated
{attribute 'OPC.UA.DA' := '1'}
timTimeoutOpen : TIME := T#0S;
// Timeout for the valve to close
// 0 = deactivated
{attribute 'OPC.UA.DA' := '1'}
timTimeoutClose : TIME := T#0S;
// Valve has open feedback signal
{attribute 'OPC.UA.DA' := '1'}
xHasOpenFeedback : BOOL;
// Valve has close feedback signal
{attribute 'OPC.UA.DA' := '1'}
xHasClosedFeedback : BOOL;
// Valve has analog feedback signal
{attribute 'OPC.UA.DA' := '1'}
xHasAnalogFeedback : BOOL;
// Set setpoint to use if interlocks are active
// defaults to 0
rSetpointWhenInterlocksActive : REAL := 0;
// Valve is used
{attribute 'OPC.UA.DA' := '1'}
xUsed : BOOL := TRUE;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_ValveConfig" Id="{cfeab4f5-40b4-416d-92f9-378624bedcc4}">
<Declaration><![CDATA[TYPE ST_ValveConfig :
STRUCT
// Timeout for the valve to open
// 0 = deactivated
{attribute 'OPC.UA.DA' := '1'}
timTimeoutOpen : TIME := T#0S;
// Timeout for the valve to close
// 0 = deactivated
{attribute 'OPC.UA.DA' := '1'}
timTimeoutClose : TIME := T#0S;
// Valve has open feedback signal
{attribute 'OPC.UA.DA' := '1'}
xHasOpenFeedback : BOOL;
// Valve has close feedback signal
{attribute 'OPC.UA.DA' := '1'}
xHasClosedFeedback : BOOL;
// Set to TRUE if valve should be open instead
// of closed with activated interlocks
xOpenWhenInterlocksActive : BOOL := FALSE;
// Valve is used
{attribute 'OPC.UA.DA' := '1'}
xUsed : BOOL := TRUE;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>