Added JobScheduler and recipe data for stations

This commit is contained in:
2026-02-24 18:28:00 +01:00
parent c4044be7bd
commit 46e294d991
33 changed files with 1454 additions and 2837 deletions

View File

@@ -6,8 +6,26 @@ STRUCT
// Acknowledge alarm button
stConfirmAlarmsBtn : ST_HMI_CONTROL_BUTTON := (xRelease := TRUE);
// Main machine
stMachineCmds : ST_HMI_PackML;
// =======
// Recipes
// =======
// Master flow recipe
stMasterFlowRecipe : ST_FlowRecipe;
// Master station recipes
stMasterRecipeHotplate : ST_Recipe_Hotplate;
stMasterRecipeCoolplate : ST_Recipe_Coolplate;
stMasterRecipeHVTest : ST_Recipe_HVTester;
// ========
// Stations
// ========
stKukaRobot : ST_HMI_Kuka;
stTrayFeederIn : ST_HMI_TrayFeeder;

View File

@@ -10,6 +10,9 @@ STRUCT
// Set setpoint
stSetSetpointBtn : ST_HMI_CONTROL_BUTTON;
// Status of slots (true = product in in this slot)
axSlotStatus : ARRAY[0..8] OF BOOL;
END_STRUCT
END_TYPE
]]></Declaration>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe_Coolplate" Id="{5bc4f5e4-441e-43c6-ba49-51587e72fb75}">
<Declaration><![CDATA[TYPE ST_Recipe_Coolplate :
STRUCT
// Resting time in seconds
rRestingTime : REAL;
// Temperature in °C
rTemp : REAL;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<GVL Name="GVL_ETCHER" Id="{ef1ba925-5452-40f1-b847-57ee34065909}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL
MAX_ROBOT_POS : UINT := 10;
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe_Etcher" Id="{a2719587-4d71-43a5-9547-91935943e20c}">
<Declaration><![CDATA[TYPE ST_Recipe_Etcher :
STRUCT
// Number of robot positions
uiNumRobotPos : UINT;
// Roboter position and setting data
stRobotStepData : ARRAY[0..(GVL_ETCHER.MAX_ROBOT_POS - 1)] OF ST_Recipe_EtherRobotStepData;
// Radial position of water jet under the blank in mm
rRadialPosLowerWaterJet : REAL;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe_EtherRobotStepData" Id="{21cfad24-000a-40fe-ab34-469629d5b7b9}">
<Declaration><![CDATA[TYPE ST_Recipe_EtherRobotStepData :
STRUCT
// Position in x in mm
rPosX : REAL;
// Positionin y in mm
rPosY : REAL;
// Position in z in mm
rPosZ : REAL;
// Angle alpha in deg
rAngleAlpha : REAL;
// Move speed in mm/s
rMoveSpeed : REAL;
// Wait time after move in seconds
rDelay : REAL;
// Tepe of medium
uiMedium : UINT;
// With waterjet from below
xWaterFromBelow : BOOL;
// With water from above
xWaterFromAbove : BOOL;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe_HVTester" Id="{f4954ef2-f38f-4cda-8590-43b9782aba39}">
<Declaration><![CDATA[TYPE ST_Recipe_HVTester :
STRUCT
// Test voltage in V
rTestVoltage : REAL;
// Maximum test current
rMaxTestCurrent : REAL;
// Ramp time in ms
rRampTime : REAL;
// Test frequency in HZ
rTestFrequency : REAL;
// Polarity 1 = POS, 2 = NEG
uiPolarity : UINT := 1;
// Overpressure N2 in mbar
rTestPresN2 : REAL;
// N2 pre purging time in seconds
rN2PrePurgeTime : REAL;
// Test retries
uiNumRetries : UINT := 0;
// Temperature for testing
// (only used in hv station which has a heater)
rTestTemp : REAL := 20.0;
// Test ok point
rTestOkVoltage : REAL;
rTestOkCurrent : REAL;
// Temperature in °C
rTemp : REAL := 20.0;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_HeatCoolPlates" Id="{a2be063c-19d7-4ca2-8121-529d61cc7bc2}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_HeatCoolPlates
VAR_INPUT
xReleaseAlarms : BOOL;
xConfirmAlarms : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
stHMIInterface : ST_HCStationHMIInterface;
END_VAR
VAR
_fbHeatingPlate : FB_HotPlate;
_xEnableHotplate : BOOL;
_rTargetTemp : REAL := 30.0;
// Cool plate
_fbCoolPlate : FB_CoolPlate;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_fbHeatingPlate(
xEnable:= _xEnableHotplate,
rTargetTemp:= _rTargetTemp,
stHMIInterface := stHMIInterface.stHotplateHMIInterface,
xReleaseAlarms := xReleaseAlarms,
xConfirmAlarms:= xConfirmAlarms,
uiNextFreeSlot=> ,
uiNextReadySlot=> );
_fbCoolPlate(stHMIInterface := stHMIInterface.stCoolplateHMIInterface);]]></ST>
</Implementation>
</POU>
</TcPlcObject>

View File

@@ -16,6 +16,9 @@ STRUCT
// Disable button
stDisableBtn : ST_HMI_CONTROL_BUTTON;
// Status of slots (true = product in in this slot)
axSlotStatus : ARRAY[0..8] OF BOOL;
END_STRUCT
END_TYPE
]]></Declaration>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe_Hotplate" Id="{c622bf0d-8040-4151-b720-bcd2c14eb3b4}">
<Declaration><![CDATA[TYPE ST_Recipe_Hotplate :
STRUCT
// Resting time in seconds
rRestingTime : REAL;
// Temperature in °C
rTemp : REAL;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_FlowRecipe" Id="{3e2c2867-3147-4627-bf49-6255dbaf5310}">
<Declaration><![CDATA[TYPE ST_FlowRecipe :
STRUCT
// Index in station recipe arrays
// -1 is used to determin if the slot
// in the product array is free.
// The index number will automatically be
// assigned by the FB_ProductHandler
iProdIdx : INT := -1;
// Current node in flow graph
iCurrNode : INT := 0;
// Next node in flow graph
// will be set by the station after completed process
// 0 means that there is no next node and the
// flow recipe is finished
iNextNode : INT := 0;
// Number of flow nodes
uiNodeCnt : INT := 0;
// Flow nodes array
astNodes : ARRAY[0..(GVL_Scheduler.MAX_RECIPE_NODES - 1)] OF ST_FlowRecipeNode;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_FlowRecipeNode" Id="{ceb8b626-8a65-4e92-9344-14e330b6c8bd}">
<Declaration><![CDATA[TYPE ST_FlowRecipeNode :
STRUCT
// Priority for transportation after completed node
uiPriority : UINT := 100;
// Required capabilities
dwReqCap : DWORD := 0;
// How many retries are allowed of this node
uiMaxRetries : UINT := 0;
// Current numbers of retries of this node
uiCurrRetries : UINT := 0;
// Was the current process successfull
xSuccess : BOOL := FALSE;
// Transition to node when operation ok
iNextNodeSuccess : INT := -1;
// Transition to node when retrying
iNextNodeRetry : INT := -1;
// Transition to node when operation failed
// or max retries reached
iNextNodeFail : INT := -1;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_TransJob" Id="{e6905112-b0c5-4499-9e9b-aaa153fe4e4d}">
<Declaration><![CDATA[TYPE ST_TransJob :
STRUCT
// Station to pick from
uiFromStation : UINT;
// Station to put into
uiToStation : UINT;
// Handle from reserved source station
uiFromStationHandle : UINT;
// Handle from reserved target station
uiToStationHandle : UINT;
// Static priority from recipe
uiPrio : UINT;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<GVL Name="GVL_Scheduler" Id="{768bef46-feba-4d2e-bc5d-8385420ff7b5}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
// Recipe constants
MAX_RECIPE_TRANSITIONS : UINT := 10;
MAX_RECIPE_NODES : UINT := 10;
// Scheduler constants
MAX_STATIONS : UINT := 10;
// Factor to calc dynamic priority from age of job in prio/s
AGING_STEP : UINT := 1;
MAX_PRIORITY : UINT := 65535;
// Job queue constants
MAX_JOBS_IN_QUEUE : UINT := 10;
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<Itf Name="I_FlowRecHandler" Id="{6edbf032-1748-4bdd-83cb-a3d71babaa22}">
<Declaration><![CDATA[INTERFACE I_FlowRecHandler
]]></Declaration>
<Method Name="M_AddFlowRec" Id="{99764a4e-e4e8-4559-8137-ead050289c2f}">
<Declaration><![CDATA[METHOD M_AddFlowRec : INT
VAR_INPUT
stFlowRecipe : ST_FlowRecipe;
END_VAR]]></Declaration>
</Method>
<Method Name="M_GetFlowRec" Id="{acfd7fa8-d012-4f06-a247-ab699d1b3413}">
<Declaration><![CDATA[METHOD M_GetFlowRec : REFERENCE TO ST_FlowRecipe
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_GetNextProcReq" Id="{b0952230-f0ad-4f72-b4ae-f75c72b5379f}">
<Declaration><![CDATA[METHOD M_GetNextProcReq : BOOL
VAR_INPUT
// Flow recipe pool index
iIdx : INT;
END_VAR
VAR_OUTPUT
// Process requirements bitmask
dwProcReq : DWORD;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_RemFlowRec" Id="{24769a0f-680b-4bee-aa98-9f548ec7f78c}">
<Declaration><![CDATA[METHOD M_RemFlowRec : BOOL
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_ReportResult" Id="{4c4a5c8e-bf65-4842-9e43-a2f8387979e2}">
<Declaration><![CDATA[METHOD M_ReportResult : BOOL
VAR_INPUT
iIdx : INT;
xResult : BOOL;
END_VAR
]]></Declaration>
</Method>
</Itf>
</TcPlcObject>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<Itf Name="I_Station" Id="{f863fe97-4162-4b2e-b60e-3fee73b74d3a}">
<Declaration><![CDATA[INTERFACE I_Station
]]></Declaration>
<Method Name="M_HasCapabilty" Id="{b116dc84-553d-4994-acaf-ce410825ba36}">
<Declaration><![CDATA[METHOD M_HasCapabilty : BOOL
VAR_INPUT
dwReqCap : DWORD;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_InsertProduct" Id="{73df88e2-075f-45f5-90de-892fc737e8cc}">
<Declaration><![CDATA[METHOD M_InsertProduct : BOOL;
VAR_INPUT
iFlowRecIdx : INT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_Release" Id="{8a898aac-0cc9-477d-9998-62f5f31391c7}">
<Declaration><![CDATA[METHOD M_Release : BOOL
VAR_INPUT
uiHandle : UINT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_RemoveProduct" Id="{90af1fde-9aed-4c2f-990d-d0ad1af5a705}">
<Declaration><![CDATA[METHOD M_RemoveProduct : INT
]]></Declaration>
</Method>
<Method Name="M_Reserve" Id="{6c535852-3b02-41fc-a4fc-9b0284412ec7}">
<Declaration><![CDATA[METHOD M_Reserve : UINT
]]></Declaration>
</Method>
<Property Name="P_Available" Id="{3075cb0b-1920-4207-a4f7-c06c6ad91a05}">
<Declaration><![CDATA[PROPERTY P_Available : BOOL]]></Declaration>
<Get Name="Get" Id="{8936bdb5-2331-44ac-804b-848836e7e78f}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_Busy" Id="{c7a782a6-d9d8-4f37-ab05-aa446f3c2ade}">
<Declaration><![CDATA[PROPERTY P_Busy : BOOL]]></Declaration>
<Get Name="Get" Id="{0bee0f8b-b5f7-4a72-b050-a4b75337d9bb}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_Capabilities" Id="{3066b824-0e0c-4a74-9959-559c483d2106}">
<Declaration><![CDATA[PROPERTY P_Capabilities : DWORD]]></Declaration>
<Get Name="Get" Id="{e550ce47-de92-4f19-a7f9-98ddde2d0ceb}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_CurrFlowRecIdx" Id="{5ed2ff40-a91e-4a0c-a811-d777c4479d01}">
<Declaration><![CDATA[PROPERTY P_CurrFlowRecIdx : INT]]></Declaration>
<Get Name="Get" Id="{231435f7-ad8b-4f53-a923-60b6c4b48be9}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_HasError" Id="{9d27caa3-2913-4127-9dc4-3c0085468476}">
<Declaration><![CDATA[PROPERTY P_HasError : BOOL]]></Declaration>
<Get Name="Get" Id="{ce1554ec-8c79-4cec-ba5a-7f52f7858b24}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_ProdAvail" Id="{8be4500d-7028-4fcc-a7fe-1999aec0ad49}">
<Declaration><![CDATA[PROPERTY P_ProdAvail : BOOL]]></Declaration>
<Get Name="Get" Id="{f64416d4-c6fe-4557-a6a6-a815d611ad92}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_StationID" Id="{9004f009-99e0-41c2-a1a1-57af78c8e0c6}">
<Declaration><![CDATA[PROPERTY P_StationID : UINT]]></Declaration>
<Get Name="Get" Id="{060c4b1d-c578-4e27-95fc-86c34f0eedd4}">
<Declaration><![CDATA[]]></Declaration>
</Get>
<Set Name="Set" Id="{30ef11e6-3d79-4cf4-bd0d-ecf371cdb56f}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
</Set>
</Property>
</Itf>
</TcPlcObject>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<Itf Name="I_Transport" Id="{ab102a71-b12c-47a4-86f6-0ac81eba046a}">
<Declaration><![CDATA[INTERFACE I_Transport
]]></Declaration>
<Property Name="P_Available" Id="{3d35d7bc-8757-4198-a19d-30d68f948ed9}">
<Declaration><![CDATA[PROPERTY P_Available : BOOL]]></Declaration>
<Get Name="Get" Id="{4a84e659-1971-4dc0-87d2-0fe5edfa2e18}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_HasError" Id="{57c92e5a-3721-42db-811d-3913f54f35ee}">
<Declaration><![CDATA[PROPERTY P_HasError : BOOL]]></Declaration>
<Get Name="Get" Id="{ce549bea-4b32-46cf-b739-cdadf8dfa005}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
</Itf>
</TcPlcObject>

View File

@@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_BaseStation" Id="{e987477c-14c3-4a7d-9e57-317d0837328f}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_BaseStation IMPLEMENTS I_Station
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
fbFlowRecHandler : FB_FlowRecHandler;
END_VAR
VAR
// Current loaded product flow recipe
// Index should also be used to access
// additional recipe parameters in designated pools
_iFlowRecIdx : INT := -1;
// Product available for pickup
_xProdAvail : BOOL;
// No product in station
_xEmpty : BOOL;
// Station busy
_xBusy : BOOL;
// Station done
_xDone : BOOL;
// Station has error
_xError : BOOL;
// Station id which was assigned by the scheduler
_uiStationID : UINT := 0;
// Station capabilities
_dwCapabilities : DWORD;
// Reserve token handle
_xReserved : BOOL;
_uiCurrHandle : UINT;
_uiHandleCounter : UINT := 1;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
<Method Name="M_HasCapabilty" Id="{d05e7240-ab9b-469b-8867-87a70dbef84e}">
<Declaration><![CDATA[METHOD M_HasCapabilty : BOOL
VAR_INPUT
dwReqCap : DWORD;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF (dwReqCap AND _dwCapabilities) = dwReqCap THEN
M_HasCapabilty := TRUE;
ELSE
M_HasCapabilty := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_InsertProduct" Id="{d323f806-9a97-49f2-9986-a997415883bf}">
<Declaration><![CDATA[METHOD M_InsertProduct : BOOL;
VAR_INPUT
iFlowRecIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Cant insert product if there is still one
// in the station, also dont insert invalid product index
IF (_iFlowRecIdx <> -1) OR (iFlowRecIdx = -1) THEN
M_InsertProduct := FALSE;
END_IF
// Save product index
_iFlowRecIdx := iFlowRecIdx;
// Advance one step in the node control flow
fbFlowRecHandler.M_AdvJob(iIdx := iFlowRecIdx);
// Report success
M_InsertProduct := TRUE;]]></ST>
</Implementation>
</Method>
<Method Name="M_Release" Id="{924bffc5-44cc-4d02-a67d-fb0f3ab51cc2}">
<Declaration><![CDATA[METHOD M_Release : BOOL
VAR_INPUT
uiHandle : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Check if we are reserved and the handle to release is corrcet
IF _xReserved AND (uiHandle = _uiCurrHandle) THEN
_xReserved := FALSE;
_uiCurrHandle := 0;
M_Release := TRUE;
ELSE
M_Release := FALSE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_RemoveProduct" Id="{e5d01ee0-a885-4496-b522-46d093416622}">
<Declaration><![CDATA[METHOD M_RemoveProduct : INT
]]></Declaration>
<Implementation>
<ST><![CDATA[// Return index of product in machine
M_RemoveProduct := _iFlowRecIdx;
// Set product in machine invalid
_iFlowRecIdx := -1;
// There is no more a product to be ready for pickup
_xProdAvail := FALSE;
]]></ST>
</Implementation>
</Method>
<Method Name="M_Reserve" Id="{42a69458-4b78-4d35-bd61-3bb1ee3cafec}">
<Declaration><![CDATA[METHOD M_Reserve : UINT
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check if we are not already reserved
IF (NOT _xReserved) THEN
_xReserved := TRUE;
_uiCurrHandle := _uiHandleCounter;
// Increment handle
_uiHandleCounter := _uiHandleCounter + 1;
// Prevent invalid token when overflowing
IF _uiHandleCounter = 0 THEN
_uiHandleCounter := 1;
END_IF
// Return handle
M_Reserve := _uiCurrHandle;
ELSE
M_Reserve := 0;
END_IF]]></ST>
</Implementation>
</Method>
<Property Name="P_Available" Id="{44abe6d9-f47e-47fa-8e00-c1822708d7bb}">
<Declaration><![CDATA[PROPERTY P_Available : BOOL
]]></Declaration>
<Get Name="Get" Id="{3bc760b0-1ae6-43b4-920e-8dd9987fe3f7}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Available := (NOT _xReserved) // Station not reserver
AND (_iFlowRecIdx = -1) // No product in station
AND (NOT _xBusy) // Station not busy
AND (NOT _xError); // Station no error]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_Busy" Id="{5b025893-318c-4480-ab72-6ffb8bedb715}">
<Declaration><![CDATA[PROPERTY P_Busy : BOOL
]]></Declaration>
<Get Name="Get" Id="{596ebd24-6d36-4141-9b46-0ba6929a7256}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Busy := _xBusy;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_Capabilities" Id="{3e87d0bb-3b77-4aa6-bc89-587ee89028c2}">
<Declaration><![CDATA[PROPERTY P_Capabilities : DWORD
]]></Declaration>
<Get Name="Get" Id="{4acbb119-eda4-4b57-8a60-16f1e982c21f}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Capabilities := _dwCapabilities;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_CurrFlowRecIdx" Id="{d0f37c45-4a5c-4efc-a8ff-f73e6ce76964}">
<Declaration><![CDATA[PROPERTY P_CurrFlowRecIdx : INT
]]></Declaration>
<Get Name="Get" Id="{d05b9f0f-e7a4-4e88-83d5-c67bbca6f978}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_CurrFlowRecIdx := _iFlowRecIdx;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_HasError" Id="{1126bae7-5dd1-4592-8f95-62ea6e252ed0}">
<Declaration><![CDATA[PROPERTY P_HasError : BOOL
]]></Declaration>
<Get Name="Get" Id="{3b3e65f8-9d92-48aa-9006-6a688edebb28}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_HasError := _xError;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_HasProduct" Id="{8b974b49-9c67-4d5f-9d0f-defc53b608cd}">
<Declaration><![CDATA[PROPERTY P_HasProduct : BOOL]]></Declaration>
<Get Name="Get" Id="{b6ba2262-99e3-4626-8d27-178b09d53a84}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_HasProduct := (_iFlowRecIdx <> -1);]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_ProdAvail" Id="{47526379-0732-4031-8d2b-4380310ef64c}">
<Declaration><![CDATA[PROPERTY P_ProdAvail : BOOL
]]></Declaration>
<Get Name="Get" Id="{c498f522-3394-4950-acf2-2e6ab02869cd}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_ProdAvail := _xProdAvail;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_StationID" Id="{6655f302-05b0-4a57-b6f3-aa70ddd6a9f3}">
<Declaration><![CDATA[PROPERTY P_StationID : UINT
]]></Declaration>
<Get Name="Get" Id="{06281909-79f6-4e9b-a8d9-9b210420e132}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_StationID := _uiStationID;]]></ST>
</Implementation>
</Get>
<Set Name="Set" Id="{2aae20dc-be6d-4d77-adda-bb622f240eb6}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_uiStationID := P_StationID;]]></ST>
</Implementation>
</Set>
</Property>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_BaseTransport" Id="{27acb860-a7d2-4ad3-adc1-3999bf7b2d0f}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_BaseTransport IMPLEMENTS I_Transport
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
fbJobQueue : FB_JobQueue;
END_VAR
VAR
_stTransJon : ST_TransJob;
_xAvailable : BOOL := TRUE;
_xError : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
<Property Name="P_Available" Id="{b86daa99-acd7-4a83-9de5-8155146759bd}">
<Declaration><![CDATA[PROPERTY P_Available : BOOL
]]></Declaration>
<Get Name="Get" Id="{b5275c3b-e101-4786-a7c8-b2f25babf8a7}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Available := _xAvailable;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_HasError" Id="{fc4055b6-6553-4a3a-9b04-c5bb792578a6}">
<Declaration><![CDATA[PROPERTY P_HasError : BOOL
]]></Declaration>
<Get Name="Get" Id="{da490431-bb00-4006-9c7f-3c5d4dab3bd2}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_HasError := _xError;]]></ST>
</Implementation>
</Get>
</Property>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_FlowRecHandler" Id="{3b3f8fad-c882-4122-b8a4-83e042a5706a}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_FlowRecHandler IMPLEMENTS I_FlowRecHandler
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_astFlowRecPool : ARRAY[0..(POOL_SIZE - 1)] OF ST_FlowRecipe;
_iFlowRecCnt : INT := 0;
// Variable used to safely delete an entry in the pool
_stDefaultRecipe : ST_FlowRecipe;
END_VAR
VAR CONSTANT
POOL_SIZE : INT := 100;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
<Method Name="M_AddFlowRec" Id="{f9093d26-eb70-4f53-b224-c0a4e1b5746e}">
<Declaration><![CDATA[METHOD M_AddFlowRec : INT
VAR_INPUT
stFlowRecipe : ST_FlowRecipe;
END_VAR
VAR
i : INT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Check size
IF _iFlowRecCnt >= POOL_SIZE THEN
M_AddFlowRec := -1;
RETURN;
END_IF
// Find a free slot in the pool
FOR i := 0 TO (POOL_SIZE - 1) DO
// If slot is free, set the flow recipe data
IF (_astFlowRecPool[i].iProdIdx = -1) THEN
_astFlowRecPool[i] := stFlowRecipe;
_astFlowRecPool[i].iProdIdx := i;
// Increment number of recipes in the pool
_iFlowRecCnt := _iFlowRecCnt + 1;
// Return index of the added flow recipe
M_AddFlowRec := i;
RETURN;
END_IF
END_FOR]]></ST>
</Implementation>
</Method>
<Method Name="M_AdvJob" Id="{896adff3-f1cf-4fbe-80a0-e0d546958fea}">
<Declaration><![CDATA[METHOD M_AdvJob : BOOL
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT M_CheckPoolBounds(iIdx := iIdx)) THEN
M_AdvJob := FALSE;
RETURN;
END_IF
// Advance one step throught the flow recipe
IF (NOT M_CheckNextNodeIdx(iCurrNodeIdx := iIdx, iNextNodeIdx := _astFlowRecPool[iIdx].iNextNode)) THEN
M_AdvJob := FALSE;
ELSE
_astFlowRecPool[iIdx].iCurrNode := _astFlowRecPool[iIdx].iNextNode;
END_IF
]]></ST>
</Implementation>
</Method>
<Method Name="M_CheckNextNodeIdx" Id="{01c36d2f-aaf6-49a5-816f-6f12c6f68b2a}">
<Declaration><![CDATA[METHOD PRIVATE M_CheckNextNodeIdx : BOOL
VAR_INPUT
iCurrNodeIdx : INT;
iNextNodeIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT M_CheckPoolBounds(iIdx := iCurrNodeIdx)) THEN
M_CheckNextNodeIdx := FALSE;
RETURN;
END_IF
IF (iNextNodeIdx) < 0 OR (iNextNodeIdx > (_astFlowRecPool[iCurrNodeIdx].uiNodeCnt - 1)) THEN
M_CheckNextNodeIdx := FALSE;
ELSE
M_CheckNextNodeIdx := TRUE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_CheckPoolBounds" Id="{fce02a57-832f-4eaf-9235-5af81ab0f23b}">
<Declaration><![CDATA[METHOD PRIVATE M_CheckPoolBounds : BOOL
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check index boundaries
IF (iIdx < 0) OR (iIdx > (POOL_SIZE - 1)) THEN
M_CheckPoolBounds := FALSE;
ELSE
M_CheckPoolBounds := TRUE;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_GetFlowRec" Id="{6c6a6f91-e451-4cac-88ce-a7a62b58d715}">
<Declaration><![CDATA[METHOD M_GetFlowRec : REFERENCE TO ST_FlowRecipe
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check index boundaries
IF (NOT M_CheckPoolBounds(iIdx)) THEN
RETURN;
END_IF
// Return product data from pool
M_GetFlowRec REF= _astFlowRecPool[iIdx];]]></ST>
</Implementation>
</Method>
<Method Name="M_GetNextPrio" Id="{11bba164-4c45-4988-8c5e-3a922df5613f}">
<Declaration><![CDATA[METHOD M_GetNextPrio : UINT
VAR_INPUT
(* Flow recipe pool index*)
iIdx : INT;
END_VAR
VAR
_iNextNodeIdx : INT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT M_CheckPoolBounds(iIdx)) THEN
M_GetNextPrio := 0;
RETURN;
END_IF
// Get next node index
_iNextNodeIdx := _astFlowRecPool[iIdx].iNextNode;
// Check for next node bounds
IF (NOT M_CheckNextNodeIdx(iCurrNodeIdx := iIdx, iNextNodeIdx := _iNextNodeIdx)) THEN
M_GetNextPrio := 0;
RETURN;
END_IF
// Return next priority
M_GetNextPrio := _astFlowRecPool[iIdx].astNodes[_iNextNodeIdx].uiPriority;]]></ST>
</Implementation>
</Method>
<Method Name="M_GetNextProcReq" Id="{c0f8e1bf-d696-4712-b3aa-63621a5789cf}">
<Declaration><![CDATA[METHOD M_GetNextProcReq : BOOL
VAR_INPUT
(* Flow recipe pool index*)
iIdx : INT;
END_VAR
VAR_OUTPUT
(* Process requirements bitmask*)
dwProcReq : DWORD;
END_VAR
VAR
_iNextNodeIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check index boundaries
IF (NOT M_CheckPoolBounds(iIdx)) THEN
dwProcReq := 0;
M_GetNextProcReq := FALSE;
RETURN;
END_IF
// Return product data from pool
_iNextNodeIdx := _astFlowRecPool[iIdx].iNextNode;
// Check for valid next node
IF (NOT M_CheckNextNodeIdx(iCurrNodeIdx := iIdx, iNextNodeIdx := _iNextNodeIdx)) THEN
dwProcReq := 0;
M_GetNextProcReq := FALSE;
RETURN;
END_IF
dwProcReq := _astFlowRecPool[iIdx].astNodes[_iNextNodeIdx].dwReqCap;
M_GetNextProcReq := TRUE;]]></ST>
</Implementation>
</Method>
<Method Name="M_RemFlowRec" Id="{4ca29998-c702-42b0-a307-733174741ab8}">
<Declaration><![CDATA[METHOD M_RemFlowRec : BOOL
VAR_INPUT
iIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check index boundaries
IF (NOT M_CheckPoolBounds(iIdx)) THEN
M_RemFlowRec := FALSE;
END_IF
// Reset flow recipe in pool slot to default recipe
_astFlowRecPool[iIdx] := _stDefaultRecipe;
// Return success
M_RemFlowRec := TRUE;]]></ST>
</Implementation>
</Method>
<Method Name="M_ReportResult" Id="{16053eca-a7a6-4800-b521-1a15f3dfce4f}">
<Declaration><![CDATA[// Sets the result of the current node operation
// and updates the reference to the next node id
// depending on the reported result
METHOD M_ReportResult : BOOL
VAR_INPUT
iIdx : INT;
xResult : BOOL;
END_VAR
VAR
_iCurrNodeIdx : INT;
_iNextNodeIdx : INT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Check index boundaries
IF (NOT M_CheckPoolBounds(iIdx)) THEN
M_ReportResult := FALSE;
RETURN;
END_IF
// Set current flow node result flag
_iCurrNodeIdx := _astFlowRecPool[iIdx].iCurrNode;
_astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].xSuccess := xResult;
// Update ref for the next node
IF xResult THEN
_iNextNodeIdx := _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].iNextNodeSuccess;
ELSE
// Check if we have retries left
IF _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].uiCurrRetries < _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].uiMaxRetries THEN
// Increment retry counter
_astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].uiCurrRetries := _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].uiCurrRetries + 1;
// Get next retry node
_iNextNodeIdx := _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].iNextNodeRetry;
ELSE
// Get next fail node
_iNextNodeIdx := _astFlowRecPool[iIdx].astNodes[_iCurrNodeIdx].iNextNodeFail;
END_IF
END_IF
// Check index boundaries
IF (_iNextNodeIdx < 0) OR (_iNextNodeIdx > (_astFlowRecPool[iIdx].uiNodeCnt - 1)) THEN
// If there is no next node set to -1
// to indicate, that this was the last node in the
// flow recipe
_astFlowRecPool[iIdx].iNextNode := -1;
ELSE
_astFlowRecPool[iIdx].iNextNode := _iNextNodeIdx;
END_IF
// Return success
M_ReportResult := TRUE;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_JobQueue" Id="{b05c10aa-a132-426c-9adb-9c3d92e348cd}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_JobQueue
VAR_INPUT
xEnableAging : BOOL := FALSE;
END_VAR
VAR_OUTPUT
END_VAR
VAR
_astJobQueue : ARRAY[0..(GVL_Scheduler.MAX_JOBS_IN_QUEUE - 1)] OF ST_TransJob;
_uiJobCount : UINT := 0;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// If aging is enable recalculate priorities
IF xEnableAging THEN
M_CalcAgingPrio();
END_IF
]]></ST>
</Implementation>
<Method Name="FB_Init" Id="{59b2fc0f-4c7e-4fd5-97a2-d54f83d37be4}">
<Declaration><![CDATA[//FB_Init is always available implicitly and it is used primarily for initialization.
//The return value is not evaluated. For a specific influence, you can also declare the
//methods explicitly and provide additional code there with the standard initialization
//code. You can evaluate the return value.
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
bInCopyCode: BOOL; // TRUE: the instance will be copied to the copy code afterward (online change)
END_VAR
VAR
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// No jobs in queue
_uiJobCount := 0;]]></ST>
</Implementation>
</Method>
<Method Name="M_AddJob" Id="{c7959fb9-7c57-4429-b989-c0befdd4ac38}">
<Declaration><![CDATA[METHOD M_AddJob : BOOL
VAR_INPUT
stJob : ST_TransJob;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check if queue is full
IF _uiJobCount >= GVL_Scheduler.MAX_JOBS_IN_QUEUE THEN
M_AddJob := FALSE;
RETURN;
END_IF
_astJobQueue[_uiJobCount] := stJob;
_uiJobCount := _uiJobCount + 1;
M_AddJob := TRUE;]]></ST>
</Implementation>
</Method>
<Method Name="M_CalcAgingPrio" Id="{7ee1cc23-b7ac-4255-862a-74b0b696f2cb}">
<Declaration><![CDATA[METHOD PRIVATE M_CalcAgingPrio
VAR
i : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Calculate ageing priority with overflow check
FOR i := 0 TO _uiJobCount DO
IF _astJobQueue[i].uiPrio < GVL_Scheduler.MAX_PRIORITY THEN
IF _astJobQueue[i].uiPrio > (GVL_Scheduler.MAX_PRIORITY - GVL_Scheduler.AGING_STEP) THEN
_astJobQueue[i].uiPrio := GVL_Scheduler.MAX_PRIORITY;
ELSE
_astJobQueue[i].uiPrio := _astJobQueue[i].uiPrio + GVL_Scheduler.AGING_STEP;
END_IF
END_IF
END_FOR]]></ST>
</Implementation>
</Method>
<Method Name="M_GetHighest" Id="{26fa0379-d921-41a3-a15c-b3ff597c1d37}">
<Declaration><![CDATA[METHOD M_GetHighest : BOOL
VAR_OUTPUT
stJob : ST_TransJob;
END_VAR
VAR
_uiIndex : UINT := 0;
_rDynPrio : REAL;
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// No jobs to return
IF _uiJobCount = 0 THEN
M_GetHighest := FALSE;
RETURN;
END_IF
// Find job with highest priority also update dyn prio
FOR _uiCnt := 0 TO (_uiJobCount-1) DO
// Check for highest priority
IF _astJobQueue[_uiCnt].uiPrio > _astJobQueue[_uiIndex].uiPrio THEN
_uiIndex := _uiCnt;
END_IF
END_FOR
// Output job
stJob := _astJobQueue[_uiIndex];
// Close gap with last element
_astJobQueue[_uiIndex] := _astJobQueue[_uiJobCount - 1];
// Adjust job number in queue
_uiJobCount := _uiJobCount - 1;
// Repost success
M_GetHighest := TRUE;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Scheduler" Id="{af4eba28-c46c-4c30-8885-00e91057da4d}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Scheduler
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
fbFlowRecHandler : FB_FlowRecHandler;
fbJobQueue : FB_JobQueue;
END_VAR
VAR
// Array needs to start at 1 so that 0 can be an invalid station
// Maybe later change this to int ant -1
_aiStations : ARRAY[1..(GVL_Scheduler.MAX_STATIONS)] OF I_Station;
_uiStationCount : UINT := 0;
_fbTransport : I_Transport;
_uiCnt : UINT;
_uiNextAvailStation : UINT;
_iFlowRecIdx : INT;
_dwNextProcReq : DWORD;
_stTmpJob : ST_TransJob;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Call job queue
fbJobQueue(xEnableAging := FALSE);
// Avoid invalid interface refs
IF (_uiStationCount = 0) OR (_fbTransport = 0) THEN
RETURN;
END_IF
IF (NOT _fbTransport.P_Available) THEN
// No need to check the stations if we don't have a transport ready
RETURN;
END_IF
FOR _uiCnt := 1 TO _uiStationCount DO
IF _aiStations[_uiCnt].P_ProdAvail THEN
// Get flow recipe index
_iFlowRecIdx := _aiStations[_uiCnt].P_CurrFlowRecIdx;
// Get next process requirements
IF (NOT fbFlowRecHandler.M_GetNextProcReq(iIdx := _iFlowRecIdx, dwProcReq => _dwNextProcReq)) THEN
CONTINUE;
END_IF
// Find next available station according to recipe and available station
_uiNextAvailStation := M_FindNextAvailStation(_dwNextProcReq);
// Check if there is a station available
IF _uiNextAvailStation <> 0 THEN
// Reserve source station
_stTmpJob.uiFromStationHandle := _aiStations[_uiCnt].M_Reserve();
// Reserve target station
_stTmpJob.uiToStationHandle := _aiStations[_uiNextAvailStation].M_Reserve();
// Check if we could reserve the stations
IF (_stTmpJob.uiToStationHandle <> 0) AND (_stTmpJob.uiFromStationHandle <> 0) THEN
// Create transport job
_stTmpJob.uiFromStation := _uiCnt;
_stTmpJob.uiToStation := _uiNextAvailStation;
_stTmpJob.uiPrio := fbFlowRecHandler.M_GetNextPrio(iIdx := _iFlowRecIdx);
// Add job to job queue
fbJobQueue.M_AddJob(stJob := _stTmpJob);
ELSE
// Remove registrations from stations
_aiStations[_uiCnt].M_Release(_stTmpJob.uiFromStationHandle);
_aiStations[_uiNextAvailStation].M_Release(_stTmpJob.uiToStationHandle);
END_IF
END_IF
END_IF
END_FOR]]></ST>
</Implementation>
<Method Name="M_ClearStations" Id="{7c667e8a-38f2-48a9-afff-834e87e4d3f6}">
<Declaration><![CDATA[METHOD M_ClearStations
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Remove all registered stations from array
MEMSET(destAddr := ADR(_aiStations), fillByte := 0, n := SIZEOF(_aiStations));
// Reset number of registered stations
_uiStationCount := 0;]]></ST>
</Implementation>
</Method>
<Method Name="M_FindNextAvailStation" Id="{75382296-32be-4724-b020-39273aa8358c}">
<Declaration><![CDATA[METHOD PRIVATE M_FindNextAvailStation : UINT
VAR_INPUT
dwProcReq : DWORD;
END_VAR
VAR
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[FOR _uiCnt := 1 TO _uiStationCount DO
IF _aiStations[_uiCnt].M_HasCapabilty(dwProcReq) AND _aiStations[_uiCnt].P_Available THEN
M_FindNextAvailStation := _uiCnt;
RETURN;
END_IF
END_FOR
M_FindNextAvailStation := 0;]]></ST>
</Implementation>
</Method>
<Method Name="M_GetStation" Id="{e8451683-3cd1-4d6f-99dd-76f33ecd008d}">
<Declaration><![CDATA[METHOD M_GetStation : I_Station
VAR_INPUT
iStationIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF iStationIdx < 1 OR iStationIdx > (GVL_Scheduler.MAX_STATIONS - 1) THEN
M_GetStation := 0;
RETURN;
END_IF
M_GetStation := _aiStations[iStationIdx];]]></ST>
</Implementation>
</Method>
<Method Name="M_Register" Id="{6a14034a-d5c0-46c1-a13b-418885a02563}">
<Declaration><![CDATA[METHOD M_Register
VAR_INPUT
fbStation : I_Station;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check if station is valid interface
IF fbStation = 0 THEN
RETURN;
END_IF
// Check if we have free slots
IF _uiStationCount < GVL_Scheduler.MAX_STATIONS THEN
_uiStationCount := _uiStationCount + 1;
_aiStations[_uiStationCount] := fbStation;
_aiStations[_uiStationCount].P_StationID := _uiStationCount;
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_RegisterTransport" Id="{a85ebd03-27a5-40c5-bd8f-ade9f2eac962}">
<Declaration><![CDATA[METHOD M_RegisterTransport
VAR_INPUT
fbTransport : I_Transport;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Check if transport is valid interface
IF fbTransport = 0 THEN
RETURN;
END_IF
// Set transport interface
_fbTransport := fbTransport;
]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<GVL Name="GVL_Test" Id="{795c0f41-8233-4565-bfb5-a9d7fd82a697}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long