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

@@ -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>