Started transportation job queue

This commit is contained in:
2026-02-17 11:37:34 +01:00
parent b35746eab0
commit 7126a7dd7e
5 changed files with 158 additions and 12 deletions

View File

@@ -36,6 +36,9 @@
<Compile Include="POUs\Scheduler\DUTs\ST_Transition.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\ST_TransJob.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\GVLs\GVL_Scheduler.TcGVL">
<SubType>Code</SubType>
<LinkAlways>true</LinkAlways>
@@ -49,6 +52,9 @@
<Compile Include="POUs\Scheduler\ITFs\I_Transport.TcIO">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_JobQueue.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_Product.TcPOU">
<SubType>Code</SubType>
</Compile>
@@ -196,7 +202,7 @@
<Type n="OptionKey">{54dd0eac-a6d8-46f2-8c27-2f43c7e49861}</Type>
<Type n="String">System.String</Type>
</TypeList>
</XmlArchive>
</XmlArchive>
</PlcProjectOptions>
</ProjectExtensions>
</Project>

View File

@@ -0,0 +1,26 @@
<?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
uiFromNodeID : UINT;
// Station to put into
uiToNodeID : UINT;
// Static priority from recipe
iStatPrio : INT;
// Dynamic priority from process
iDynPrio : INT;
// Prio sum
iSumPrio : INT;
// Time this job was created
// The older the job the higher the priority
uliTimeCreated : ULINT;
// Is this job in the queue still valid
xValid : BOOL := TRUE;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -9,6 +9,12 @@ VAR_GLOBAL CONSTANT
// Scheduler constants
MAX_STATIONS : UINT := 10;
// Factor to calc dynmaic priority from age of job in prio/s
AGE_FACTOR : REAL := 1.0;
// Job queue constants
MAX_JOBS_IN_QUEUE : UINT := 10;
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>

View File

@@ -0,0 +1,103 @@
<?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
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[]]></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[// Set all jobs to false
FOR _uiCnt := 0 TO (GVL_Scheduler.MAX_JOBS_IN_QUEUE - 1) DO
_astJobQueue[_uiCnt].xValid := FALSE;
END_FOR
// 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
_uiJobCount := _uiJobCount + 1;
_astJobQueue[_uiJobCount] := stJob;
_astJobQueue[_uiJobCount].xValid := TRUE;
_astJobQueue[_uiJobCount].uliTimeCreated := F_GetSystemTime();
M_AddJob := TRUE;]]></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
_iMaxPrio : INT := -32768;
_uiIndex : UINT;
_ulTimestamp : ULINT;
_rDynPrio : REAL;
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// No jobs to return
IF _uiJobCount = 0 THEN
M_GetHighest := FALSE;
RETURN;
END_IF
_ulTimestamp := F_GetSystemTime();
// Find job with highest priority also update dyn prio
FOR _uiCnt := 0 TO _uiJobCount DO
// Only recalc and check if job is valid
IF _astJobQueue[_uiCnt].xValid THEN
// Recalculate dynamic priority (100ns intervalls to seconds)
_rDynPrio := ULINT_TO_REAL(_ulTimestamp - _astJobQueue[_uiCnt].uliTimeCreated) *1E-7 * GVL_Scheduler.AGE_FACTOR;
IF _rDynPrio > 32767.0 THEN
_rDynPrio := 32767.0;
END_IF
_astJobQueue[_uiCnt].iDynPrio := REAL_TO_INT(_rDynPrio);
// Check for highest priority
IF (_astJobQueue[_uiCnt].iStatPrio + _astJobQueue[_uiCnt].iDynPrio) > _iMaxPrio THEN
_iMaxPrio :=
END_IF
END_IF
END_FOR]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -10,7 +10,7 @@ VAR
_aiStations : ARRAY[1..(GVL_Scheduler.MAX_STATIONS)] OF I_Station;
_uiStationCount : UINT := 0;
_fbTransport : ARRAY[1..(GVL_Scheduler.MAX_STATIONS)] OF I_Station;
_fbTransport : I_Station;
_uiTransportCnt : UINT := 0;
_uiCnt : UINT;
@@ -19,7 +19,10 @@ VAR
_fbProduct : I_Product;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[FOR _
<ST><![CDATA[IF (NOT _fbTransport.P_Available) THEN
// No need to check the station if we don't have a transport ready
RETURN;
END_IF
FOR _uiCnt := 1 TO _uiStationCount DO
@@ -29,7 +32,9 @@ FOR _uiCnt := 1 TO _uiStationCount DO
// Check if there is a station available
IF _uiNextAvailStation <> 0 THEN
// Skip reservation for now
// _aiStations[_uiNextAvailStation].M_Reserve();
// Create transport job
END_IF
END_IF
END_FOR]]></ST>