Merge branch 'release/1.3'

This commit is contained in:
2026-02-16 11:36:56 +01:00
11 changed files with 388 additions and 446 deletions

View File

@@ -116,13 +116,13 @@
</Hides>
</DataType>
</DataTypes>
<Project ProjectGUID="{775BE4FD-89CE-48D5-8E68-5C84AF95981A}" Target64Bit="true" ShowHideConfigurations="#x6">
<Project ProjectGUID="{775BE4FD-89CE-48D5-8E68-5C84AF95981A}" TargetNetId="5.167.199.178.1.1" Target64Bit="true" ShowHideConfigurations="#x6">
<System>
<Settings MaxCpus="2"/>
<Licenses>
<Target>
<ManualSelect>{9FD32FC8-0CF9-4C5B-95FB-F35423496A77}</ManualSelect>
<LicenseDevice DongleHardwareId="2" DongleDevice="#x71010002" DongleLevel="92" DongleSystemId="{F4D452BD-01EA-8CE8-F538-CCB335BD88CC}" DongleSerialNumber="000btjb7" DongleCacheLicense="false"/>
<LicenseDevice Disabled="true" DongleHardwareId="2" DongleDevice="#x71010002" DongleLevel="92" DongleSerialNumber="000btjb7" DongleCacheLicense="false"/>
</Target>
</Licenses>
<Tasks>
@@ -136,7 +136,7 @@
</System>
<Plc>
<Project GUID="{4E62D9E7-436C-457D-8DC4-82D2FEF91C96}" Name="BasicComponents" PrjFilePath="BasicComponents\BasicComponents.plcproj" TmcFilePath="BasicComponents\BasicComponents.tmc" ReloadTmc="true" AmsPort="851" FileArchiveSettings="#x000e" CopyTmcToTarget="true" CopyTpyToTarget="false" SymbolicMapping="true">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcHash="{FB895001-23AB-2B17-4B12-BEB2F42468E3}" TmcPath="BasicComponents\BasicComponents.tmc">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcHash="{B7757519-28FA-424D-AB4A-AF3716E16F4E}" TmcPath="BasicComponents\BasicComponents.tmc">
<Name>BasicComponents Instance</Name>
<CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID>
<Vars VarGrpType="2" AreaNo="1">

Binary file not shown.

View File

@@ -21,7 +21,7 @@
<GlobalVersionStructureIncluded>false</GlobalVersionStructureIncluded>
<Company>Heisig GmbH</Company>
<Title>BaseComponents</Title>
<ProjectVersion>1.2</ProjectVersion>
<ProjectVersion>1.3</ProjectVersion>
<DefaultNamespace>BC</DefaultNamespace>
<Author>M.Heisig</Author>
<Description>Basic components fb's (Valves, AI, AO, Motors, etc.)</Description>
@@ -79,6 +79,12 @@
<Compile Include="POUs\Components\Controller\FB_PI.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Components\Controller\FB_PID.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Components\Controller\FB_RampGenerator.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Components\EventListener\FB_EventListener.TcPOU">
<SubType>Code</SubType>
</Compile>
@@ -101,9 +107,6 @@
<Compile Include="POUs\Components\Utilities\FB_Blinker.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Components\Utilities\FB_RampGenerator.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Components\Utilities\FB_ReleaseSignal.TcPOU">
<SubType>Code</SubType>
</Compile>
@@ -148,9 +151,6 @@
<Compile Include="POUs\HMI\Datentypen\ST_HMI_INTERLOCK.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\HMI\Datentypen\ST_HMI_ORP_SENSOR_DATA.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\HMI\Datentypen\ST_HMI_VALVE_DATA.TcDUT">
<SubType>Code</SubType>
</Compile>
@@ -2666,6 +2666,12 @@
<v>410,5410</v>
</d>
</o>
<v>{eeeeeeee-3909-4298-8022-501ac3238667}</v>
<o>
<v n="Name">"{eeeeeeee-3909-4298-8022-501ac3238667}"</v>
<d n="SubKeys" t="Hashtable" />
<d n="Values" t="Hashtable" />
</o>
<v>{F66C7017-BDD8-4114-926C-81D6D687E35F}</v>
<o>
<v n="Name">"{F66C7017-BDD8-4114-926C-81D6D687E35F}"</v>
@@ -2687,7 +2693,7 @@
<Type n="String">System.String</Type>
<Type n="UInt32">System.UInt32</Type>
</TypeList>
</XmlArchive>
</XmlArchive>
</PlcProjectOptions>
</ProjectExtensions>
</Project>

View File

@@ -13,6 +13,7 @@ TYPE E_AXIS_PTP_STATE :
MOVING_ABSOLUTE,
MOVING_RELATIVE,
MOVING_VELOCITY,
MOVING_MODULO,
HALTING,
WAIT_FOR_DISABLE,
ERROR,

View File

@@ -7,7 +7,7 @@ VAR_INPUT
xCalibrationCam AT %I* : BOOL;
xInvertCalibrationCam : BOOL := FALSE;
xEnablePositive : BOOL := TRUE;
xEnableNegative : BOOL := TRUE;;
xEnableNegative : BOOL := TRUE;
rOverride : REAL := 100.0;
lrVelocity : LREAL;
@@ -23,6 +23,7 @@ VAR_OUTPUT
lrActPosition : LREAL;
xIsStopped : BOOL;
xBusy : BOOL;
xDone : BOOL;
xError : BOOL;
END_VAR
VAR
@@ -36,6 +37,7 @@ VAR
_fbMoveAbsolute : MC_MoveAbsolute;
_fbMoveRelative : MC_MoveRelative;
_fbMoveVelocity : MC_MoveVelocity;
_fbMoveModulo : MC_MoveModulo;
_fbHalt : MC_Halt;
_fbReset : MC_Reset;
@@ -49,6 +51,7 @@ VAR
_xStartMoveAbsolute : BOOL;
_xStartMoveRelative : BOOL;
_xStartMoveVelocity : BOOL;
_xStartMoveModulo : BOOL;
_xHalt : BOOL;
_xReset : BOOL;
@@ -61,6 +64,7 @@ VAR
_xExecuteMoveAbs : BOOL;
_xExecuteMoveRel : BOOL;
_xExecuteMoveVelocity : BOOL;
_xExecuteMoveModulo : BOOL;
_xExecuteHalt : BOOL;
@@ -74,7 +78,9 @@ VAR
_eHomingMode : MC_HomingMode := MC_HomingMode.MC_DefaultHoming;
_xCalibrationCam : BOOL;
_eMoveVelDirection : MC_Direction;
_eMoveDirection : MC_Direction;
_xCanExecNewCmd : BOOL;
_eState : E_AXIS_PTP_STATE;
@@ -83,6 +89,7 @@ VAR
// Ouput buffers
// =============
_xDone : BOOL;
_xBusy : BOOL;
_xError : BOOL;
END_VAR
@@ -91,6 +98,10 @@ END_VAR
<ST><![CDATA[// Call axis interface
_fbAxis.ReadStatus();
IF _fbAxis.Status.Error THEN
_xError := TRUE;
END_IF
IF xInvertCalibrationCam THEN
_xCalibrationCam := NOT xCalibrationCam;
ELSE
@@ -169,12 +180,26 @@ _fbMoveVelocity(
Acceleration:= 0,
Deceleration:= 0,
Jerk:= 0,
Direction:= _eMoveVelDirection);
Direction:= _eMoveDirection);
IF _fbMoveVelocity.Error THEN
_xError := TRUE;
END_IF
_fbMoveModulo(
Axis:= _fbAxis,
Execute:= _xStartMoveModulo,
Position:= _lrTargetPosition,
Velocity:= lrVelocity,
Acceleration:= 0,
Deceleration:= 0,
Jerk:= 0,
Direction:= _eMoveDirection);
IF _fbMoveModulo.Error THEN
_xError := TRUE;
END_IF
_fbHalt(
Axis:= _fbAxis,
@@ -187,6 +212,8 @@ IF _fbHalt.Error THEN
_xError := TRUE;
END_IF
// Can the axis perform a new move command
_xCanExecNewCmd := (NOT _xBusy) AND _fbPower.Status AND (NOT _xError);
// ====================
// Handle state machine
@@ -209,6 +236,7 @@ CASE _eState OF
E_AXIS_PTP_STATE.WAIT_FOR_ENABLE:
IF _fbPower.Status AND (NOT _xError) THEN
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
@@ -220,6 +248,8 @@ CASE _eState OF
E_AXIS_PTP_STATE.ENABLED:
_xDone := FALSE;
IF _xExecuteMoveAbs THEN
_xExecuteMoveAbs := FALSE;
_xBusy := TRUE;
@@ -248,6 +278,13 @@ CASE _eState OF
_eState := E_AXIS_PTP_STATE.MOVING_VELOCITY;
END_IF
IF _xExecuteMoveModulo THEN
_xExecuteMoveModulo := FALSE;
_xStartMoveModulo := TRUE;
_xBusy := TRUE;
_eState := E_AXIS_PTP_STATE.MOVING_MODULO;
END_IF
IF (NOT xEnable) AND (NOT _xError) THEN
_xEnable := FALSE;
_xBusy := TRUE;
@@ -265,6 +302,7 @@ CASE _eState OF
IF (NOT _fbHome.Busy) AND (NOT _xError) THEN
_xStartHomeing := FALSE;
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
@@ -286,6 +324,7 @@ CASE _eState OF
IF (NOT _fbMoveAbsolute.Busy) AND (NOT _xError) THEN
_xStartMoveAbsolute := FALSE;
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
@@ -307,6 +346,7 @@ CASE _eState OF
IF (NOT _fbMoveRelative.Busy) AND (NOT _xError) THEN
_xStartMoveRelative := FALSE;
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
@@ -331,11 +371,41 @@ CASE _eState OF
_eState := E_AXIS_PTP_STATE.ERROR;
END_IF
IF _xExecuteHalt THEN
_xExecuteHalt := FALSE;
_xStartMoveVelocity := FALSE;
_xHalt := TRUE;
_eState := E_AXIS_PTP_STATE.HALTING;
END_IF
E_AXIS_PTP_STATE.MOVING_MODULO:
IF _fbMoveModulo.Done THEN
_xStartMoveModulo := FALSE;
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
IF _xExecuteHalt THEN
_xExecuteHalt := FALSE;
_xStartMoveModulo := FALSE;
_xHalt := TRUE;
_eState := E_AXIS_PTP_STATE.HALTING;
END_IF
IF _xError THEN
_xStartMoveModulo := FALSE;
_xBusy := FALSE;
_eState := E_AXIS_PTP_STATE.ERROR;
END_IF
E_AXIS_PTP_STATE.HALTING:
IF (NOT _fbHalt.Busy) AND (NOT _xError) THEN
_xHalt := FALSE;
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.ENABLED;
END_IF
@@ -349,6 +419,7 @@ CASE _eState OF
E_AXIS_PTP_STATE.WAIT_FOR_DISABLE:
IF (NOT _fbPower.Status) AND (NOT _xError) THEN
_xBusy := FALSE;
_xDone := TRUE;
_eState := E_AXIS_PTP_STATE.OFF;
END_IF
@@ -387,6 +458,7 @@ xEnabled := _fbPower.Status;
xHomed := _fbAxis.Status.Homed;
lrActPosition := _fbAxis.NcToPlc.ActPos;
xIsStopped := _fbAxis.Status.StandStill OR _fbAxis.Status.Disabled;
xDone := _xDone;
xBusy := _xBusy;
xError := _xError;]]></ST>
</Implementation>
@@ -403,11 +475,11 @@ END_IF]]></ST>
<Method Name="M_Homing" Id="{816a1a43-38da-49f0-b902-c1027a6ba127}">
<Declaration><![CDATA[METHOD M_Homing : BOOL
VAR_INPUT
lrHomingPosition : LREAL;
lrHomingPosition : LREAL := Tc2_MC2.DEFAULT_HOME_POSITION;
eHomingMode : MC_HomingMode := MC_HomingMode.MC_DefaultHoming;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _xBusy) AND _fbPower.Status THEN
<ST><![CDATA[IF _xCanExecNewCmd THEN
_xBusy := TRUE;
_lrHomingPos := lrHomingPosition;
_eHomingMode := eHomingMode;
@@ -424,7 +496,7 @@ VAR_INPUT
lrTargetPos : LREAL;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _xBusy) AND _fbPower.Status THEN
<ST><![CDATA[IF _xCanExecNewCmd THEN
_xBusy := TRUE;
_lrTargetPosition := lrTargetPos;
_xExecuteMoveAbs := TRUE;
@@ -435,13 +507,31 @@ END_IF
]]></ST>
</Implementation>
</Method>
<Method Name="M_MoveModulo" Id="{1a5eaefa-5bb1-4438-b3e0-80c1a34fc0ee}">
<Declaration><![CDATA[METHOD M_MoveModulo : BOOL
VAR_INPUT
lrTargetPos : LREAL;
eMoveDirection : MC_Direction;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF _xCanExecNewCmd THEN
_xBusy := TRUE;
_lrTargetPosition := lrTargetPos;
_eMoveDirection := eMoveDirection;
_xExecuteMoveModulo := TRUE;
M_MoveModulo := TRUE;
ELSE
M_MoveModulo := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_MoveRel" Id="{51e5fe82-5e25-4de0-84c7-b4d6560c312f}">
<Declaration><![CDATA[METHOD M_MoveRel : BOOL;
VAR_INPUT
lrRelDist : LREAL;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _xBusy) AND _fbPower.Status THEN
<ST><![CDATA[IF _xCanExecNewCmd THEN
_xBusy := TRUE;
_lrRelativeDistance := lrRelDist;
_xExecuteMoveRel:= TRUE;
@@ -458,10 +548,10 @@ VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _xBusy) AND _fbPower.Status THEN
<ST><![CDATA[IF _xCanExecNewCmd THEN
_xBusy := TRUE;
_xExecuteMoveVelocity := TRUE;
_eMoveVelDirection := eDirection;
_eMoveDirection := eDirection;
M_MoveVelocity := TRUE;
ELSE
M_MoveVelocity := FALSE;

View File

@@ -6,7 +6,7 @@ VAR_INPUT
rSP : REAL;
rPV : REAL;
rKp : REAL;
rTn : REAL;
rTn : REAL; // In seconds
xEnable : BOOL;
xSaturatedUpper : BOOL := FALSE;
@@ -19,23 +19,21 @@ VAR
_rError : REAL := 0.0;
_rIntegral : REAL := 0.0;
_rProportinal : REAL := 0.0;
_rProportional : REAL := 0.0;
_rDeltaIntegral : REAL := 0.0;
_fbGetCurTaskIdx : GETCURTASKINDEX;
_rT : REAL;
_xFirstCylce : BOOL := TRUE;
_xFirstCycle : BOOL := TRUE;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF _xFirstCylce THEN
_xFirstCylce := FALSE;
<ST><![CDATA[IF _xFirstCycle THEN
_xFirstCycle := FALSE;
// Get current task time
_fbGetCurTaskIdx();
_rT := LREAL_TO_REAL(UDINT_TO_LREAL(TwinCAT_SystemInfoVarList._TASKInfo[_fbGetCurTaskIdx.index].CycleTime) * 10E-5);
_rT := LREAL_TO_REAL(UDINT_TO_LREAL(_TaskInfo[GETCURTASKINDEXEX()].CycleTime) * 1E-7);
END_IF
@@ -49,13 +47,10 @@ ELSE
END_IF
// Calculate proportinal part
_rProportinal := rKp * _rError;
// Calculate controller output
rMV := _rProportinal + _rIntegral;
_rProportional := rKp * _rError;
// Calculate integral for this step
IF rTn <> 0 THEN
IF rTn > 0 THEN
_rDeltaIntegral := (rKp * _rT / rTn) * _rError;
ELSE
_rDeltaIntegral := 0;
@@ -74,9 +69,12 @@ END_IF
_rIntegral := _rIntegral + _rDeltaIntegral;
// Reset integral with deactivated integral time
IF (rTn = 0.0) AND (_rIntegral <> 0) THEN
IF (rTn <= 0.0) THEN
_rIntegral := 0.0;
END_IF]]></ST>
END_IF
// Calculate controller output
rMV := _rProportional + _rIntegral;]]></ST>
</Implementation>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_PID" Id="{7822f674-b0e7-48ea-8d27-164e15dbed0c}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_PID
VAR_INPUT
rSP : REAL;
rPV : REAL;
rKp : REAL;
rTn : REAL; // In seconds
rTv : REAL; // In seconds
rTdFilter : REAL; // Filter time constant
xEnable : BOOL;
xSaturatedUpper : BOOL := FALSE;
xSaturatedLower : BOOL := FALSE;
END_VAR
VAR_OUTPUT
rMV : REAL;
END_VAR
VAR
_rError : REAL := 0.0;
_rIntegral : REAL := 0.0;
_rProportional : REAL := 0.0;
_rDerivative : REAL := 0.0;
_rDeltaPV : REAL := 0.0;
_rPVLast : REAL := 0.0;
_rDRaw : REAL := 0.0;
_rDeltaIntegral : REAL := 0.0;
_rT : REAL;
_xFirstCycle : BOOL := TRUE;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF _xFirstCycle THEN
_xFirstCycle := FALSE;
// Get current task time
_rT := LREAL_TO_REAL(UDINT_TO_LREAL(_TaskInfo[GETCURTASKINDEXEX()].CycleTime) * 1E-7);
_rPVLast := rPV;
END_IF
IF xEnable THEN
_rError := rSP - rPV;
ELSE
_rError := 0.0;
_rIntegral := 0.0;
_rDerivative := 0.0;
_rPVLast := rPv;
rMV := 0.0;
RETURN;
END_IF
// Calculate proportinal part
_rProportional := rKp * _rError;
// Calculate integral for this step
IF rTn > 0 THEN
_rDeltaIntegral := (rKp * _rT / rTn) * _rError;
ELSE
_rDeltaIntegral := 0;
END_IF
// Only add new integral part if we are going away from the upper or lower bound
IF (xSaturatedUpper AND (_rDeltaIntegral > 0.0)) THEN
_rDeltaIntegral := 0.0;
END_IF
IF (xSaturatedLower AND (_rDeltaIntegral < 0.0)) THEN
_rDeltaIntegral := 0.0;
END_IF
// Calculate integral part
_rIntegral := _rIntegral + _rDeltaIntegral;
// Reset integral with deactivated integral time
IF (rTn <= 0.0) THEN
_rIntegral := 0.0;
END_IF
IF rTv > 0.0 THEN
_rDeltaPV := (rPV - _rPVLast) / _rT;
// Unfiltered derivative
_rDRaw := -rKp * rTv * _rDeltaPV;
// Filter with PT1
IF rTdFilter > 0.0 THEN
_rDerivative := _rDerivative + (_rT / (rTdFilter + _rT)) * (_rDRaw - _rDerivative);
ELSE
_rDerivative := _rDRaw;
END_IF
ELSE
_rDerivative := 0.0;
END_IF
_rPVLast := rPV;
// Calculate controller output
rMV := _rProportional + _rIntegral + _rDerivative;]]></ST>
</Implementation>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_RampGenerator" Id="{61136989-8ee2-49fa-af2b-1b579e85353f}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_RampGenerator
VAR_INPUT
xEnable : BOOL; // Output equals input
xHold : BOOL := FALSE; // Freeze current value
rTarget : REAL; // Targetvalue
rRiseRate : REAL; // Positive rise rate in units/s
rFallRate : REAL; // Negative rise rate in units/s (only positive values)
END_VAR
VAR_OUTPUT
rOut : REAL; // Generated ramp output
xBusy : BOOL; // TRUE until target value reached
END_VAR
VAR
_xFirstCycle : BOOL := TRUE;
_rT : REAL := 0.0; // Task cycle time
_rDiff : REAL := 0.0;
_rStep : REAL := 0.0;
// Output buffer
_rOut : REAL;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF _xFirstCycle THEN
_xFirstCycle := FALSE;
// Get cycle time
_rT := LREAL_TO_REAL(UDINT_TO_LREAL(_TaskInfo[GETCURTASKINDEXEX()].CycleTime) * 1E-7);
END_IF
// If not enabled output equals input
IF (NOT xEnable) THEN
_rOut := rTarget;
rOut := _rOut;
xBusy := FALSE;
RETURN;
END_IF
// Freezes the ramp at the current value
IF xHold THEN
xBusy := (ABS(rTarget - _rOut) > 0.0);
RETURN;
END_IF
// Calculate diff to target
_rDiff := rTarget - _rOut;
// Check if we need to ramp up or down
IF _rDiff > 0 THEN
// Ramp up
_rStep := ABS(rRiseRate) * _rT;
// Clamp to target value
IF _rStep > _rDiff THEN
_rOut := rTarget;
ELSE
_rOut := _rOut + _rStep;
END_IF
ELSIF _rDiff < 0 THEN
// Ramp down
_rStep := ABS(rFallRate) * _rT;
// Clamp to target value
IF _rStep > ABS(_rDiff) THEN
_rOut := rTarget;
ELSE
_rOut := _rOut - _rStep;
END_IF
END_IF
// Handle busy flag
xBusy := (ABS(rTarget - _rOut) > 0.0);
// Copy internals to outputs
rOut := _rOut;]]></ST>
</Implementation>
<Method Name="M_SetStart" Id="{f7326067-d944-49e6-a19b-12711f9b3f19}">
<Declaration><![CDATA[METHOD M_SetStart
VAR_INPUT
rStartValue : REAL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_rOut := rStartValue;
rOut := _rOut;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_RampGenerator" Id="{eab75824-fb3c-460a-af65-da2b006c5dc1}" SpecialFunc="None">
<Declaration><![CDATA[// Must only be called once per cycle!
// Otherwise the Interpolation is wrong
{attribute 'analysis' := '-56'}
FUNCTION_BLOCK FB_RampGenerator
VAR_INPUT
// Current target value
rTarget : REAL;
// Minimum target value
rTargetMin : REAL;
// Maximum target value
rTargetMax : REAL;
// Ramp up time (min to max)
timRampUp : TIME;
// Ramp down time (max to min)
timRampDown : TIME;
END_VAR
VAR_OUTPUT
rSetpoint : REAL := 0;
// Indicates that the target value has been reached
xInTarget : BOOL;
END_VAR
VAR
// Cycle time in ms
_rCycleTime : REAL;
// Ramp up speed per cycle
// Units per ms
_rRampUpSpeed : REAL;
// Ramp down speed per cycle
// Units per ms
_rRampDownSpeed : REAL;
// Distance left to go
_rDistanceToGo : REAL;
// First cycle
_xFirstCycle : BOOL := TRUE;
_fbGetCurTaskIdx : GETCURTASKINDEX;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Get task cycle time during first cycle
// Does not work in FB_init and also not with {attribute 'call_after_init'}
IF _xFirstCycle THEN
_xFirstCycle := FALSE;
// Get current task cycle time
_fbGetCurTaskIdx();
// Convert 100ns to 1ms
_rCycleTime := UDINT_TO_REAL(TwinCAT_SystemInfoVarList._TaskInfo[_fbGetCurTaskIdx.index].CycleTime) * 10E-5;
END_IF
// Clamp setpoint to min max values
{analysis -37}
rTarget := MAX(rTarget, rTargetMin);
rTarget := MIN(rTarget, rTargetMax);
{analysis +37}
// Calculate change rates
// TIME datatype is handled internally like a UDINT (32-bit). This leads to a resolution in milliseconds.
// [_rRampUpSpeed] = units per cycle
IF timRampUp <> T#0S THEN
_rRampUpSpeed := (rTargetMax - rTargetMin) * (_rCycleTime / TIME_TO_REAL(timRampUp));
ELSE
_rRampUpSpeed := rTargetMax;
END_IF
IF timRampDown <> T#0S THEN
_rRampDownSpeed := -(rTargetMax - rTargetMin) * (_rCycleTime / TIME_TO_REAL(timRampDown));
ELSE
_rRampDownSpeed := -rTargetMax;
END_IF
// Calculate distance left to go
_rDistanceToGo := rTarget - rSetpoint;
// Calculate new setpoint
IF (_rDistanceToGo > 0.0) THEN
IF (_rDistanceToGo > _rRampUpSpeed) THEN
rSetpoint := rSetpoint + _rRampUpSpeed;
ELSE
rSetpoint := rTarget;
END_IF
ELSIF (_rDistanceToGo < 0.0) THEN
IF (_rDistanceToGo < _rRampDownSpeed) THEN
rSetpoint := rSetpoint + _rRampDownSpeed;
ELSE
rSetpoint := rTarget;
END_IF
ELSE
rSetpoint := rTarget;
END_IF
// Check if we are in range of target range
IF ABS(rSetpoint-rTarget) <= 0.001 THEN
xInTarget := TRUE;
ELSE
xInTarget := FALSE;
END_IF ]]></ST>
</Implementation>
<Property Name="CycleTime" Id="{12c00f80-a9cf-4d1b-ac68-3c3e59228015}">
<Declaration><![CDATA[PROPERTY CycleTime : REAL]]></Declaration>
<Get Name="Get" Id="{06853e5f-48e3-4643-ae4f-e23a07d71695}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[CycleTime := _rCycleTime;]]></ST>
</Implementation>
</Get>
</Property>
<Method Name="SetStart" Id="{06f2b416-7cfb-4f46-94e3-4002d92fc703}">
<Declaration><![CDATA[METHOD SetStart
VAR_INPUT
rStartpoint : REAL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[rSetpoint := rStartpoint;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_HMI_ORP_SENSOR_DATA" Id="{5591496f-c48a-4039-866c-f9454b697632}">
<Declaration><![CDATA[TYPE ST_HMI_ORP_SENSOR_DATA :
STRUCT
// PH sensor value
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
rValuePH : REAL;
// Temperature sensor value
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
rValueTemp : REAL;
// Redox potential sensor data
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
rValueORP : REAL;
// Sensor lifetime left
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
rValueDLI : REAL;
// Reflects the current status of the ORP input data (read only)
// 1 = Ok; 2 = Error
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
iStatus : INT := 0;
// Instance name (read only)
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
sName : STRING(80);
// true = Object ist used by the program and can be actuated by the HMI
// false = Object is NOT used by the program and should NOT be used by the HMI
// (read only)
{attribute 'OPC.UA.DA' := '1'}
{attribute 'OPC.UA.DA.Access' := '1'}
xUsed : BOOL := TRUE;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -3,17 +3,6 @@
<POU Name="FB_RampGeneratorTest" Id="{883a6789-24bc-434e-bdcd-11047d9142bc}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_RampGeneratorTest EXTENDS TcUnit.FB_TestSuite
VAR
// variables for max clamp test
_fbRMPMaxClamp : FB_RampGenerator;
_fbDelayMaxClamp : TON;
_fbDelayMaxClampBuffer : TON;
// variables for min clamp test
_fbRMPMinClamp : FB_RampGenerator;
_fbDelayMinClamp : TON;
_fbDelayMinClampBuffer : TON;
_xInitMinClamp : BOOL := FALSE;
// variables for ramp down timing test
_fbRMPRampDownTime : FB_RampGenerator;
_fbDelayRampDownTime : TON;
@@ -43,11 +32,7 @@ END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// test for correct cycle time and it's readout
TestCycleTimeReadout();
// test clamping
TestMaxClamping();
TestMinClamping();
// TestCycleTimeReadout();
// test ramp up/down
TestRampUpTime();
@@ -59,55 +44,17 @@ TestRampDownContinuity();
TestInTargetResultUp();
TestInTargetResultDown();]]></ST>
</Implementation>
<Method Name="TestCycleTimeReadout" Id="{adf8238e-e5ed-478c-ac22-37930b46e697}">
<Declaration><![CDATA[{warning disable C0394}
METHOD TestCycleTimeReadout
VAR
// ramp generator instance
_fbRampGen : FB_RampGenerator;
// task index instance
_fbGetCurTaskIdx : GETCURTASKINDEX;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestCycleTimeReadout');
// read current cycle time and abort test in case of cycletime not 10 ms.
_fbGetCurTaskIdx();
IF ((UDINT_TO_REAL(TwinCAT_SystemInfoVarList._TASKInfo[_fbGetCurTaskIdx.index].CycleTime) * 10E-5) - 10.0) > 0.001 THEN
AssertTrue(FALSE, 'Project cycle time not set to 10ms!');
ELSE
_fbRampGen(
rTarget:= 0.0,
rTargetMin:= 0.0,
rTargetMax:= 100.0,
timRampUp:= T#5S,
timRampDown:= T#5S,
rSetpoint=> ,
xInTarget=> );
// Project should be set to 10ms cycle time for this test to work
AssertEquals_REAL(Expected := 10.0, Actual := _fbRampGen.CycleTime, Delta := 0.01, 'Cycle time is not equal to project cycle time (10ms)');
END_IF
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
<Method Name="TestInTargetResultDown" Id="{d659dd70-b87c-48f4-9d7b-df95aaef8540}">
<Declaration><![CDATA[{warning disable C0394}
METHOD TestInTargetResultDown
VAR
// in target result
_xInTarget : BOOL;
// current ramp setpoint
_rSetpoint : REAL;
END_VAR
VAR CONSTANT
// delay until in target should be reached
timDelay : TIME := T#990MS;
timDelay : TIME := T#500MS;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestInTargetResultDown');
@@ -117,19 +64,18 @@ _fbDelayInTargetResultDown(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPInTargetResultDown(
xEnable := TRUE,
rTarget := -1,
rTargetMin := -1,
rTargetMax := 1,
timRampUp := T#2S,
timRampDown := T#2S,
rSetpoint => _rSetpoint,
xInTarget => _xInTarget);
rRiseRate := 2,
rFallRate := 2,
rOut => _rSetpoint,
xBusy =>);
// check for whether InTarget is reached at the specified time
IF NOT _fbDelayInTargetResultDown.Q THEN
AssertFalse(_xInTarget, 'InTarget reached earlier then expected.');
AssertTrue(_fbRMPInTargetResultDown.xBusy, 'InTarget reached earlier then expected.');
ELSE
AssertTrue(_xInTarget, 'InTarget not reached in time.');
AssertFalse(_fbRMPInTargetResultDown.xBusy, 'InTarget not reached in time.');
TEST_FINISHED();
END_IF]]></ST>
</Implementation>
@@ -138,16 +84,13 @@ END_IF]]></ST>
<Declaration><![CDATA[{warning disable C0394}
METHOD TestInTargetResultUp
VAR
// in target result
_xInTarget : BOOL;
// current ramp setpoint
_rSetpoint : REAL;
END_VAR
VAR CONSTANT
// delay until in target should be reached
timDelay : TIME := T#740MS;
timDelay : TIME := T#500MS;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestInTargetResultUp');
@@ -157,130 +100,19 @@ _fbDelayInTargetResultUp(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPInTargetResultUp(
xEnable := TRUE,
rTarget := 1,
rTargetMin := -1,
rTargetMax := 1,
timRampUp := T#1S500MS,
timRampDown := T#1S500MS,
rSetpoint => _rSetpoint,
xInTarget => _xInTarget);
rRiseRate := 2,
rFallRate := 2,
rOut => _rSetpoint,
xBusy => );
// check for whether InTarget is reached at the specified time
IF NOT _fbDelayInTargetResultUp.Q THEN
AssertFalse(_xInTarget, 'InTarget reached earlier then expected.');
IF (NOT _fbDelayInTargetResultUp.Q) THEN
AssertTrue(_fbRMPInTargetResultUp.xBusy, 'InTarget reached earlier then expected.');
ELSE
AssertTrue(_xInTarget, 'InTarget not reached in time.');
AssertFalse(_fbRMPInTargetResultUp.xBusy, 'InTarget not reached in time.');
TEST_FINISHED();
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="TestMaxClamping" Id="{65cd0699-a59f-4537-9fbd-e53ee8fb2124}">
<Declaration><![CDATA[{warning disable C0394}
METHOD TestMaxClamping
VAR
// current ramp setpoint
_rSetpoint : REAL;
END_VAR
VAR CONSTANT
// expected clamped value
rExpected : REAL := 5;
// delta for assertion
rDelta : REAL := 0.0001;
// delay until clamping is reached
timDelayRamp : TIME := T#500MS;
// delay to check for value changes after clamping
timDelay : TIME := T#1S;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestMaxClamping');
// timer until until clamping is reach + timeBuffer afterwards to check whether is stays clamped or not
_fbDelayMaxClamp(IN := TRUE, PT := timDelayRamp);
_fbDelayMaxClampBuffer(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPMaxClamp(
rTarget := 10,
rTargetMin := 0,
rTargetMax := 5,
timRampUp := T#500MS,
timRampDown := T#500MS,
rSetpoint => _rSetpoint,
xInTarget =>);
// check for clamping
IF NOT _fbDelayMaxClamp.Q THEN
// too early
AssertTrue(_rSetpoint < rExpected,'Clamped value reached before expected time');
ELSE
// after expected rampTime
IF NOT _fbDelayMaxClampBuffer.Q THEN
AssertEquals_REAL(Expected := rExpected, Actual := _rSetpoint, Delta := rDelta, 'Value did not stay on or did not reach MaxTarget');
ELSE
TEST_FINISHED();
END_IF
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="TestMinClamping" Id="{e7c98271-509c-4227-870c-3cb3245da266}">
<Declaration><![CDATA[{warning disable C0394}
METHOD TestMinClamping
VAR
// current ramp setpoint
_rSetpoint : REAL;
END_VAR
VAR CONSTANT
// expected clamped value
rExpected : REAL := 5;
// delta for assertion
rDelta : REAL := 0.0001;
// delay until clamping is reached
timDelayRamp : TIME := T#500MS;
// delay to check for value changes after clamping
timDelay : TIME := T#1S;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestMinClamping');
// init start at top cap
IF NOT _xInitMinClamp THEN
_fbRMPMinClamp.SetStart(10);
_xInitMinClamp := TRUE;
END_IF
// timer until until clamping is reach + timeBuffer afterwards to check whether is stays clamped or not
_fbDelayMinClamp(IN := TRUE, PT := timDelayRamp);
_fbDelayMinClampBuffer(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPMinClamp(
rTarget := 0,
rTargetMin := 5,
rTargetMax := 10,
timRampUp := T#500MS,
timRampDown := T#500MS,
rSetpoint => _rSetpoint,
xInTarget =>);
// check for clamping
IF NOT _fbDelayMinClamp.Q THEN
// too early
AssertTrue(_rSetpoint >= (rExpected - rDelta), 'Clamped value reached before expected time');
ELSE
// after expected rampTime
IF NOT _fbDelayMinClampBuffer.Q THEN
AssertEquals_REAL(Expected := rExpected, Actual := _rSetpoint, Delta := rDelta, 'Value did not stay on or did not reach MinTarget');
ELSE
TEST_FINISHED();
END_IF
END_IF]]></ST>
</Implementation>
</Method>
@@ -297,31 +129,30 @@ END_VAR
VAR CONSTANT
// expected final value
rExpected : REAL := -9.4564;
rExpected : REAL := -0.8;
// delta for assertion
rDelta : REAL := 0.0001;
// delay until final value is reached
timDelay : TIME := T#470MS;
timDelay : TIME := T#790MS;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestRampDownContinuity');
// ramp timer, calc speed & calc next expected ramp step
_fbDelayRampDownContinuity(IN := TRUE, PT := timDelay);
_rMyRampSpeed := 20 * (10 / TIME_TO_REAL(T#1S));
_rMyRampSpeed := 0.01;
_rNextExpectedDown := _rNextExpectedDown - _rMyRampSpeed;
// run RampGenerator
_fbRMPRampDownContinuity(
rTarget := -9.4564,
rTargetMin := -10,
rTargetMax := 10,
timRampUp := T#1S,
timRampDown := T#1S,
rSetpoint => _rSetpoint,
xInTarget =>);
xEnable := TRUE,
rTarget := -1,
rRiseRate := 1,
rFallRate := 1,
rOut => _rSetpoint,
xBusy =>);
// check for current expected value
IF NOT _fbDelayRampDownContinuity.Q THEN
@@ -360,12 +191,10 @@ _fbDelayRampDownTime(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPRampDownTime(
rTarget := -7.4367,
rTargetMin := -10,
rTargetMax := 10,
timRampUp := T#500MS,
timRampDown := T#100MS,
rSetpoint => _rSetpoint,
xInTarget =>);
rRiseRate := 20,
rFallRate := 100,
rOut => _rSetpoint,
xBusy =>);
// check whether final value is reach on time or before
IF NOT _fbDelayRampDownTime.Q THEN
@@ -390,31 +219,30 @@ END_VAR
VAR CONSTANT
// expected final value
rExpected : REAL := 8.673;
rExpected : REAL := 0.8;
// delta for assertions
rDelta : REAL := 0.0001;
// delay until final value is reached
timDelay : TIME := T#860MS;
timDelay : TIME := T#790MS;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestRampUpContinuity');
// ramp timer, calc speed & calc next expected ramp step
_fbDelayRampUpContinuity(IN := TRUE, PT := timDelay);
_rMyRampSpeed := 16.567 * (10 / TIME_TO_REAL(T#1S650MS));
_rMyRampSpeed := 0.01;
_rNextExpectedUp := _rNextExpectedUp + _rMyRampSpeed;
// run RampGenerator
_fbRMPRampUpContinuity(
rTarget := 8.673,
rTargetMin := -7,
rTargetMax := 9.567,
timRampUp := T#1S650MS,
timRampDown := T#1S,
rSetpoint => _rSetpoint,
xInTarget =>);
xEnable := TRUE,
rTarget := 2,
rRiseRate := 1,
rFallRate := 1,
rOut => _rSetpoint,
xBusy =>);
// check for current expected value
IF NOT _fbDelayRampUpContinuity.Q THEN
@@ -436,13 +264,13 @@ END_VAR
VAR CONSTANT
// expected final value
rExpected : REAL := 8.3456;
rExpected : REAL := 0.55;
// delta for assertions
rDelta : REAL := 0.0001;
// delay until final value is reached
timDelay : TIME := T#440MS;
timDelay : TIME := T#550MS;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('TestRampUpTime');
@@ -452,13 +280,12 @@ _fbDelayRampUpTime(IN := TRUE, PT := timDelay);
// run RampGenerator
_fbRMPRampUpTime(
rTarget := 8.3456,
rTargetMin := -10,
rTargetMax := 10,
timRampUp := T#1S70MS,
timRampDown := T#100MS,
rSetpoint => _rSetpoint,
xInTarget =>);
rTarget := 0.55,
xEnable := TRUE,
rRiseRate := 1,
rFallRate := 100,
rOut => _rSetpoint,
xBusy =>);
// check whether final value is reach on time or before
IF NOT _fbDelayRampUpTime.Q THEN