First working implementation

- Currently only with one sided gripper
This commit is contained in:
2026-02-19 15:42:54 +01:00
parent 7126a7dd7e
commit 92276e4f4b
32 changed files with 1041 additions and 378 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<GVL Name="GVL_Product" Id="{cd06b28b-40e0-45d6-96ec-08dcaca45fe3}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL
axProductInStation : ARRAY[0..4] OF BOOL;
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>

View File

@@ -18,22 +18,20 @@
<LibraryReferences>{c5c6b994-f2a1-4a71-b657-f282007c0cd2}</LibraryReferences>
</PropertyGroup>
<ItemGroup>
<Compile Include="GVLs\GVL_Product.TcGVL">
<SubType>Code</SubType>
<LinkAlways>true</LinkAlways>
</Compile>
<Compile Include="PlcTask.TcTTO">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\MAIN.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\E_TransitCond.TcDUT">
<Compile Include="POUs\Scheduler\DUTs\ST_FlowRecipe.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\ST_Recipe.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\ST_RecipeNode.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\ST_Transition.TcDUT">
<Compile Include="POUs\Scheduler\DUTs\ST_FlowRecipeNode.TcDUT">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\DUTs\ST_TransJob.TcDUT">
@@ -43,7 +41,7 @@
<SubType>Code</SubType>
<LinkAlways>true</LinkAlways>
</Compile>
<Compile Include="POUs\Scheduler\ITFs\I_Product.TcIO">
<Compile Include="POUs\Scheduler\ITFs\I_FlowRecHandler.TcIO">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Scheduler\ITFs\I_Station.TcIO">
@@ -54,18 +52,27 @@
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_JobQueue.TcPOU">
<SubType>Code</SubType>
<ExcludeFromBuild>false</ExcludeFromBuild>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_Product.TcPOU">
<Compile Include="POUs\Scheduler\POUs\FB_FlowRecHandler.TcPOU">
<SubType>Code</SubType>
<ExcludeFromBuild>false</ExcludeFromBuild>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_Scheduler.TcPOU">
<SubType>Code</SubType>
<ExcludeFromBuild>false</ExcludeFromBuild>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_StationBase.TcPOU">
<Compile Include="POUs\Scheduler\POUs\FB_BaseStation.TcPOU">
<SubType>Code</SubType>
<ExcludeFromBuild>false</ExcludeFromBuild>
</Compile>
<Compile Include="POUs\Scheduler\POUs\FB_TransportBase.TcPOU">
<Compile Include="POUs\Scheduler\POUs\FB_BaseTransport.TcPOU">
<SubType>Code</SubType>
<ExcludeFromBuild>false</ExcludeFromBuild>
</Compile>
<Compile Include="POUs\Scheduler\POUs\GVL_Test.TcGVL">
<SubType>Code</SubType>
<LinkAlways>true</LinkAlways>
</Compile>
<Compile Include="POUs\Stations\FB_Etcher.TcPOU">
<SubType>Code</SubType>
@@ -82,16 +89,26 @@
<Compile Include="POUs\Stations\FB_Output.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\Stations\FB_Robot.TcPOU">
<SubType>Code</SubType>
</Compile>
<Compile Include="POUs\ST_RobotMoveData.TcDUT">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="DUTs" />
<Folder Include="GVLs" />
<Folder Include="POUs\Scheduler" />
<Folder Include="POUs\Scheduler">
<ExcludeFromBuild>false</ExcludeFromBuild>
</Folder>
<Folder Include="POUs\Scheduler\ITFs" />
<Folder Include="POUs\Scheduler\DUTs" />
<Folder Include="POUs\Scheduler\GVLs" />
<Folder Include="POUs\Scheduler\POUs" />
<Folder Include="POUs\Stations" />
<Folder Include="POUs\Stations">
<ExcludeFromBuild>false</ExcludeFromBuild>
</Folder>
<Folder Include="VISUs" />
<Folder Include="POUs" />
</ItemGroup>
@@ -118,8 +135,8 @@
<ProjectExtensions>
<PlcProjectOptions>
<XmlArchive>
<Data>
<o xml:space="preserve" t="OptionKey">
<Data>
<o xml:space="preserve" t="OptionKey">
<v n="Name">"&lt;ProjectRoot&gt;"</v>
<d n="SubKeys" t="Hashtable" ckt="String" cvt="OptionKey">
<v>{192FAD59-8248-4824-A8DE-9177C94C195A}</v>
@@ -195,14 +212,14 @@
</d>
<d n="Values" t="Hashtable" />
</o>
</Data>
<TypeList>
<Type n="Boolean">System.Boolean</Type>
<Type n="Hashtable">System.Collections.Hashtable</Type>
<Type n="OptionKey">{54dd0eac-a6d8-46f2-8c27-2f43c7e49861}</Type>
<Type n="String">System.String</Type>
</TypeList>
</XmlArchive>
</Data>
<TypeList>
<Type n="Boolean">System.Boolean</Type>
<Type n="Hashtable">System.Collections.Hashtable</Type>
<Type n="OptionKey">{54dd0eac-a6d8-46f2-8c27-2f43c7e49861}</Type>
<Type n="String">System.String</Type>
</TypeList>
</XmlArchive>
</PlcProjectOptions>
</ProjectExtensions>
</Project>

File diff suppressed because one or more lines are too long

View File

@@ -14,8 +14,18 @@ VAR
_fbOutput : FB_Output;
_fbNIO : FB_NIO;
// Transport
_fbRobot : FB_Robot;
// Job queue
_fbJobQueue : FB_JobQueue;
// Example recipe
_stRecipeSimple : ST_Recipe;
_stRecipeSimple : ST_FlowRecipe;
_fbFlowRecHandler : FB_FlowRecHandler;
_xStart : BOOL;
END_VAR
]]></Declaration>
<Implementation>
@@ -25,18 +35,31 @@ END_VAR
END_IF
// Call all stations
_fbInput();
_fbEtcher();
_fbHVTest();
_fbOutput();
_fbNIO();
_fbInput(fbFlowRecHandler := _fbFlowRecHandler);
GVL_Product.axProductInStation[0] := _fbInput.P_HasProduct;
_fbEtcher(fbFlowRecHandler := _fbFlowRecHandler);
GVL_Product.axProductInStation[1] := _fbEtcher.P_HasProduct;
_fbHVTest(fbFlowRecHandler := _fbFlowRecHandler);
GVL_Product.axProductInStation[2] := _fbHVTest.P_HasProduct;
_fbOutput(fbFlowRecHandler := _fbFlowRecHandler);
GVL_Product.axProductInStation[3] := _fbOutput.P_HasProduct;
_fbNIO(fbFlowRecHandler := _fbFlowRecHandler);
GVL_Product.axProductInStation[4] := _fbNIO.P_HasProduct;
// Call scheduler
_fbScheduler();]]></ST>
_fbScheduler(fbFlowRecHandler := _fbFlowRecHandler, fbJobQueue := _fbJobQueue);
// Call robot transport
_fbRobot(fbJobQueue := _fbJobQueue, fbScheduler := _fbScheduler);]]></ST>
</Implementation>
<Action Name="A_Init" Id="{203d4406-b6da-4e3d-8378-ac96a0e18ff2}">
<Implementation>
<ST><![CDATA[// Register stations to scheduler
<ST><![CDATA[// // Register stations to scheduler
_fbScheduler.M_ClearStations();
_fbScheduler.M_Register(_fbInput);
@@ -45,26 +68,35 @@ _fbScheduler.M_Register(_fbHVTest);
_fbScheduler.M_Register(_fbOutput);
_fbScheduler.M_Register(_fbNIO);
// Register transport
_fbScheduler.M_RegisterTransport(_fbRobot);
// Create simple recipe
_stRecipeSimple.uiRecipeID := 1;
_stRecipeSimple.uiStartNode := 0;
_stRecipeSimple.uiNodeCnt := 3;
_stRecipeSimple.uiNodeCnt := 4;
// Process nodes
_stRecipeSimple.astNodes[0].uiNodeID := 1;
_stRecipeSimple.astNodes[0].dwReqCap := 16#0001;
_stRecipeSimple.astNodes[0].uiTransCnt := 1;
_stRecipeSimple.astNodes[0].astTransitions[0].eCondition := E_TransitCond.ALWAYS;
_stRecipeSimple.astNodes[0].astTransitions[0].uiNextNodeID := 1;
_stRecipeSimple.astNodes[1].uiNodeID := 1;
_stRecipeSimple.astNodes[1].dwReqCap := 16#0001;
_stRecipeSimple.astNodes[1].uiTransCnt := 2;
_stRecipeSimple.astNodes[1].astTransitions[0].eCondition := E_TransitCond.RESULT_OK;
_stRecipeSimple.astNodes[1].astTransitions[0].uiNextNodeID := 3;
_stRecipeSimple.astNodes[1].astTransitions[1].eCondition := E_TransitCond.RESULT_NIO;
_stRecipeSimple.astNodes[1].astTransitions[1].uiNextNodeID := 4;]]></ST>
// Etching
_stRecipeSimple.astNodes[0].dwReqCap := 16#0001;
_stRecipeSimple.astNodes[0].iNextNodeSuccess := 1;
_stRecipeSimple.astNodes[0].iNextNodeFail := 3;
// HV Testing
_stRecipeSimple.astNodes[1].dwReqCap := 16#0002;
_stRecipeSimple.astNodes[1].iNextNodeSuccess := 2;
_stRecipeSimple.astNodes[1].uiMaxRetries := 1;
_stRecipeSimple.astNodes[1].iNextNodeRetry := 0;
_stRecipeSimple.astNodes[1].iNextNodeFail := 3;
// Output
_stRecipeSimple.astNodes[2].dwReqCap := 16#0004;
// NIO
_stRecipeSimple.astNodes[3].dwReqCap := 16#0008;
// Set flow recipe in input station
_fbInput.M_SetRecipe(_stRecipeSimple);]]></ST>
</Implementation>
</Action>
</POU>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_RobotMoveData" Id="{94123d39-0745-4fc4-b9ca-f3094a525f9a}">
<Declaration><![CDATA[TYPE ST_RobotMoveData :
STRUCT
rWaitTime : REAL;
rX : REAL;
rY : REAL;
rZ : REAL;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="E_TransitCond" Id="{0c8e8042-17f8-4b76-ad0c-96d650245098}">
<Declaration><![CDATA[{attribute 'qualified_only'}
{attribute 'strict'}
{attribute 'to_string'}
TYPE E_TransitCond :
(
ALWAYS := 0,
RESULT_OK,
RESULT_NIO
);
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

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Recipe" Id="{3e2c2867-3147-4627-bf49-6255dbaf5310}">
<Declaration><![CDATA[TYPE ST_Recipe :
STRUCT
uiRecipeID : UINT;
uiStartNode : UINT := 0;
uiNodeCnt : UINT := 0;
astNodes : ARRAY[0..(GVL_Scheduler.MAX_RECIPE_NODES - 1)] OF ST_RecipeNode;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_RecipeNode" Id="{ceb8b626-8a65-4e92-9344-14e330b6c8bd}">
<Declaration><![CDATA[TYPE ST_RecipeNode :
STRUCT
uiNodeID : UINT;
// Required capabilities
dwReqCap : DWORD;
// Transition count
uiTransCnt : UINT;
// Transitions
astTransitions : ARRAY[0..(GVL_Scheduler.MAX_STATIONS - 1)] OF ST_Transition;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -4,21 +4,19 @@
<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;
uiFromStation : UINT;
// Is this job in the queue still valid
xValid : BOOL := TRUE;
// 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>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Transition" Id="{7b8c5232-d3a6-4d0b-b8ad-e54400018d53}">
<Declaration><![CDATA[TYPE ST_Transition :
STRUCT
eCondition : E_TransitCond;
uiNextNodeID : UINT;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -10,8 +10,10 @@ 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;
// 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;

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

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<Itf Name="I_Product" Id="{78dffc1b-aed6-4622-8b64-e060477179c6}">
<Declaration><![CDATA[{attribute 'no_explicit_call' := 'do not call this POU directly'}
INTERFACE I_Product
]]></Declaration>
<Method Name="M_SetCurrProcRes" Id="{afd48187-6008-4813-8fe5-04c632483cf0}">
<Declaration><![CDATA[METHOD M_SetCurrProcRes
VAR_INPUT
xSuccess : BOOL;
END_VAR
]]></Declaration>
</Method>
<Property Name="P_CurrNodeID" Id="{2408303a-b81e-4623-9557-4af29ff08776}">
<Declaration><![CDATA[PROPERTY P_CurrNodeID : UINT]]></Declaration>
<Get Name="Get" Id="{c7773936-77b0-42da-86b5-ef706272183a}">
<Declaration><![CDATA[]]></Declaration>
</Get>
<Set Name="Set" Id="{1ecab2de-a6ae-4a48-a0d3-c65c8bfe8a22}">
<Declaration><![CDATA[]]></Declaration>
</Set>
</Property>
<Property Name="P_NextReqCap" Id="{ea007dca-197b-4284-b65b-0f7c1a14e1fa}">
<Declaration><![CDATA[PROPERTY P_NextReqCap : DWORD]]></Declaration>
<Get Name="Get" Id="{0abf4eb3-351d-4342-a687-7722bc63fdaa}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
</Itf>
</TcPlcObject>

View File

@@ -10,26 +10,26 @@ VAR_INPUT
END_VAR
]]></Declaration>
</Method>
<Method Name="M_InsertProduct" Id="{6d808715-3e3b-47f8-9bce-8110ddf0fadc}">
<Method Name="M_InsertProduct" Id="{73df88e2-075f-45f5-90de-892fc737e8cc}">
<Declaration><![CDATA[METHOD M_InsertProduct : BOOL;
VAR_INPUT
fbProduct : I_Product;
iFlowRecIdx : INT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_Release" Id="{8a898aac-0cc9-477d-9998-62f5f31391c7}">
<Declaration><![CDATA[METHOD M_Release : BOOL
VAR_INPUT
udiHandle : UDINT;
uiHandle : UINT;
END_VAR
]]></Declaration>
</Method>
<Method Name="M_RemoveProduct" Id="{90af1fde-9aed-4c2f-990d-d0ad1af5a705}">
<Declaration><![CDATA[METHOD M_RemoveProduct : I_Product
<Declaration><![CDATA[METHOD M_RemoveProduct : INT
]]></Declaration>
</Method>
<Method Name="M_Reserve" Id="{6c535852-3b02-41fc-a4fc-9b0284412ec7}">
<Declaration><![CDATA[METHOD M_Reserve : UDINT
<Declaration><![CDATA[METHOD M_Reserve : UINT
]]></Declaration>
</Method>
<Property Name="P_Available" Id="{3075cb0b-1920-4207-a4f7-c06c6ad91a05}">
@@ -50,6 +50,12 @@ END_VAR
<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}">
@@ -62,23 +68,16 @@ END_VAR
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_Product" Id="{20f38cff-89b3-4ebd-9f5f-171721cc7f4c}">
<Declaration><![CDATA[PROPERTY P_Product : I_Product]]></Declaration>
<Get Name="Get" Id="{54f46645-3bb5-477e-a037-282c55914608}">
<Declaration><![CDATA[]]></Declaration>
</Get>
</Property>
<Property Name="P_Result" Id="{703e039e-b718-4d79-86bd-e01bd8c2d16b}">
<Declaration><![CDATA[PROPERTY P_Result : UINT]]></Declaration>
<Get Name="Get" Id="{5a6206de-d50c-4c20-be99-7f2f851fee69}">
<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

@@ -1,31 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_StationBase" Id="{e987477c-14c3-4a7d-9e57-317d0837328f}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_StationBase IMPLEMENTS I_Station
<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;
// Station available for new product
_xAvailable : 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;
_uiResult : UINT;
// Reserve token handle
_xReserved : BOOL;
_udiCurrHandle : UDINT;
_udiHandleCounter : UDINT := 1;
_fbProduct : I_Product;
_uiCurrHandle : UINT;
_uiHandleCounter : UINT := 1;
END_VAR
]]></Declaration>
<Implementation>
@@ -38,33 +52,47 @@ VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[M_HasCapabilty := (dwReqCap AND _dwCapabilities) = dwReqCap;]]></ST>
<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
fbProduct : I_Product;
iFlowRecIdx : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF fbProduct <> 0 AND _fbProduct = 0 THEN
_fbProduct := fbProduct;
M_InsertProduct := TRUE;
ELSE
<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]]></ST>
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
udiHandle : UDINT;
uiHandle : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF _xReserved AND (udiHandle = _udiCurrHandle) THEN
<ST><![CDATA[// Check if we are reserved and the handle to release is corrcet
IF _xReserved AND (uiHandle = _uiCurrHandle) THEN
_xReserved := FALSE;
_udiCurrHandle := 0;
_uiCurrHandle := 0;
M_Release := TRUE;
ELSE
M_Release := FALSE;
@@ -72,33 +100,40 @@ END_IF]]></ST>
</Implementation>
</Method>
<Method Name="M_RemoveProduct" Id="{e5d01ee0-a885-4496-b522-46d093416622}">
<Declaration><![CDATA[METHOD M_RemoveProduct : I_Product
<Declaration><![CDATA[METHOD M_RemoveProduct : INT
]]></Declaration>
<Implementation>
<ST><![CDATA[M_RemoveProduct := _fbProduct;
_fbProduct := 0;]]></ST>
<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 : UDINT
<Declaration><![CDATA[METHOD M_Reserve : UINT
]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _xReserved) THEN
<ST><![CDATA[// Check if we are not already reserved
IF (NOT _xReserved) THEN
_xReserved := TRUE;
_xAvailable := FALSE;
_udiCurrHandle := _udiHandleCounter;
_uiCurrHandle := _uiHandleCounter;
// Increment handle
_udiHandleCounter := _udiHandleCounter + 1;
_uiHandleCounter := _uiHandleCounter + 1;
// Prevent invalid token when overflowing
IF _udiHandleCounter = 0 THEN
_udiHandleCounter := 1;
IF _uiHandleCounter = 0 THEN
_uiHandleCounter := 1;
END_IF
// Return handle
M_Reserve := _udiCurrHandle;
M_Reserve := _uiCurrHandle;
ELSE
M_Reserve := 0;
END_IF]]></ST>
@@ -112,7 +147,10 @@ END_IF]]></ST>
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Available := _xAvailable;]]></ST>
<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>
@@ -140,6 +178,18 @@ END_VAR
</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>
@@ -152,6 +202,17 @@ END_VAR
</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>
@@ -164,30 +225,6 @@ END_VAR
</Implementation>
</Get>
</Property>
<Property Name="P_Product" Id="{78d9c0d5-87fc-4940-8596-ddfb9f581df8}">
<Declaration><![CDATA[PROPERTY P_Product : I_Product
]]></Declaration>
<Get Name="Get" Id="{16260053-c4bc-45d7-9a2b-60c60dd338ad}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Product := _fbProduct;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_Result" Id="{84eed0af-ed7f-4d3e-964c-fa5fda4093f1}">
<Declaration><![CDATA[PROPERTY P_Result : UINT
]]></Declaration>
<Get Name="Get" Id="{72d03065-7320-43e6-8295-5250a7641abc}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_Result := _uiResult;]]></ST>
</Implementation>
</Get>
</Property>
<Property Name="P_StationID" Id="{6655f302-05b0-4a57-b6f3-aa70ddd6a9f3}">
<Declaration><![CDATA[PROPERTY P_StationID : UINT
]]></Declaration>
@@ -199,6 +236,14 @@ END_VAR
<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

@@ -1,13 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_TransportBase" Id="{27acb860-a7d2-4ad3-adc1-3999bf7b2d0f}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_TransportBase IMPLEMENTS I_Transport
<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
_xAvailable : BOOL;
_stTransJon : ST_TransJob;
_xAvailable : BOOL := TRUE;
_xError : BOOL;
END_VAR
]]></Declaration>

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

@@ -3,6 +3,7 @@
<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
@@ -12,7 +13,11 @@ VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<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.
@@ -28,12 +33,7 @@ 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
<ST><![CDATA[// No jobs in queue
_uiJobCount := 0;]]></ST>
</Implementation>
</Method>
@@ -50,22 +50,36 @@ IF _uiJobCount >= GVL_Scheduler.MAX_JOBS_IN_QUEUE THEN
RETURN;
END_IF
_uiJobCount := _uiJobCount + 1;
_astJobQueue[_uiJobCount] := stJob;
_astJobQueue[_uiJobCount].xValid := TRUE;
_astJobQueue[_uiJobCount].uliTimeCreated := F_GetSystemTime();
_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
_iMaxPrio : INT := -32768;
_uiIndex : UINT;
_ulTimestamp : ULINT;
_uiIndex : UINT := 0;
_rDynPrio : REAL;
_uiCnt : UINT;
@@ -77,26 +91,25 @@ IF _uiJobCount = 0 THEN
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);
FOR _uiCnt := 0 TO (_uiJobCount-1) DO
// Check for highest priority
IF (_astJobQueue[_uiCnt].iStatPrio + _astJobQueue[_uiCnt].iDynPrio) > _iMaxPrio THEN
_iMaxPrio :=
IF _astJobQueue[_uiCnt].uiPrio > _astJobQueue[_uiIndex].uiPrio THEN
_uiIndex := _uiCnt;
END_IF
END_IF
END_FOR]]></ST>
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>

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Product" Id="{aeaa85d2-80b5-4274-aa7e-dadad9f0dcfb}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Product IMPLEMENTS I_Product
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
// Product recipe
_stRecipe : ST_Recipe;
_uiCurrNodeID : UINT := 0;
_uiNextNodeID : UINT := 0;
_xResult : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
<Method Name="M_SetCurrProcRes" Id="{478e39cf-00d0-4253-8c4e-ed30567e421d}">
<Declaration><![CDATA[METHOD M_SetCurrProcRes
VAR_INPUT
xSuccess : BOOL;
END_VAR
VAR
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Check all possible transition
FOR _uiCnt := 0 TO (_stRecipe.astNodes[_uiCurrNodeID].uiTransCnt - 1) DO
CASE _stRecipe.astNodes[_uiCurrNodeID].astTransitions[_uiCnt].eCondition OF
E_TransitCond.ALWAYS:
_uiNextNodeID := _stRecipe.astNodes[_uiCurrNodeID].astTransitions[_uiCnt].uiNextNodeID;
EXIT;
E_TransitCond.RESULT_OK:
IF xSuccess THEN
_uiNextNodeID := _stRecipe.astNodes[_uiCurrNodeID].astTransitions[_uiCnt].uiNextNodeID;
EXIT;
END_IF
E_TransitCond.RESULT_NIO:
IF (NOT xSuccess) THEN
_uiNextNodeID := _stRecipe.astNodes[_uiCurrNodeID].astTransitions[_uiCnt].uiNextNodeID;
EXIT;
END_IF
END_CASE
END_FOR
]]></ST>
</Implementation>
</Method>
<Property Name="P_CurrNodeID" Id="{896d8e88-c324-42f7-8ec5-28782223405a}">
<Declaration><![CDATA[PROPERTY P_CurrNodeID : UINT
]]></Declaration>
<Get Name="Get" Id="{e1aeb000-7306-4b12-ab29-6078214076ea}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_CurrNodeID := _uiCurrNodeID;]]></ST>
</Implementation>
</Get>
<Set Name="Set" Id="{6b281f43-823b-4830-a917-2539de62e216}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_uiCurrNodeID := P_CurrNodeID;]]></ST>
</Implementation>
</Set>
</Property>
<Property Name="P_NextReqCap" Id="{4c4a03ed-4074-46c1-8f2a-b964a788faf6}">
<Declaration><![CDATA[PROPERTY P_NextReqCap : DWORD
]]></Declaration>
<Get Name="Get" Id="{a7e4e08c-166a-4234-b8b1-9d5ae2650ea7}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[P_NextReqCap := _stRecipe.astNodes[_uiNextNodeID].dwReqCap;]]></ST>
</Implementation>
</Get>
</Property>
</POU>
</TcPlcObject>

View File

@@ -6,35 +6,77 @@ 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_Station;
_uiTransportCnt : UINT := 0;
_fbTransport : I_Transport;
_uiCnt : UINT;
_uiNextAvailStation : UINT;
_fbProduct : I_Product;
_iFlowRecIdx : INT;
_dwNextProcReq : DWORD;
_stTmpJob : ST_TransJob;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[IF (NOT _fbTransport.P_Available) THEN
// No need to check the station if we don't have a transport ready
<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(_aiStations[_uiCnt].P_Product);
_uiNextAvailStation := M_FindNextAvailStation(_dwNextProcReq);
// Check if there is a station available
IF _uiNextAvailStation <> 0 THEN
// Skip reservation for now
// _aiStations[_uiNextAvailStation].M_Reserve();
// Create transport job
// 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>
@@ -55,22 +97,37 @@ _uiStationCount := 0;]]></ST>
<Method Name="M_FindNextAvailStation" Id="{75382296-32be-4724-b020-39273aa8358c}">
<Declaration><![CDATA[METHOD PRIVATE M_FindNextAvailStation : UINT
VAR_INPUT
_fbProduct : I_Product;
dwProcReq : DWORD;
END_VAR
VAR
_uiCnt : UINT;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[FOR _uiCnt := 1 TO _uiStationCount DO
IF _aiStations[_uiCnt].M_HasCapabilty(_fbProduct.P_NextReqCap) AND _aiStations[_uiCnt].P_Available THEN
IF _aiStations[_uiCnt].M_HasCapabilty(dwProcReq) AND _aiStations[_uiCnt].P_Available THEN
M_FindNextAvailStation := _uiCnt;
EXIT;
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
@@ -85,8 +142,9 @@ END_IF
// Check if we have free slots
IF _uiStationCount < GVL_Scheduler.MAX_STATIONS THEN
_aiStations[_uiStationCount] := fbStation;
_uiStationCount := _uiStationCount + 1;
_aiStations[_uiStationCount] := fbStation;
_aiStations[_uiStationCount].P_StationID := _uiStationCount;
END_IF]]></ST>
</Implementation>
</Method>
@@ -102,11 +160,10 @@ IF fbTransport = 0 THEN
RETURN;
END_IF
// Check if we have free slots
IF _uiTransportCnt < GVL_Scheduler.MAX_STATIONS THEN
_aiStations[_uiTransportCnt] := fbTransport;
_uiTransportCnt := _uiTransportCnt + 1;
END_IF]]></ST>
// Set transport interface
_fbTransport := fbTransport;
]]></ST>
</Implementation>
</Method>
</POU>

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>

View File

@@ -1,16 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Etcher" Id="{b81a122a-e5f5-475c-8ee0-2d97c1df7eb4}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Etcher EXTENDS FB_StationBase
<Declaration><![CDATA[FUNCTION_BLOCK FB_Etcher EXTENDS FB_BaseStation
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_iState : INT;
_tonProcessDuration : TON;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<ST><![CDATA[CASE _iState OF
// Wait for product
0:
IF _iFlowRecIdx <> -1 THEN
_xDone := FALSE;
_xBusy := TRUE;
_iState := 10;
END_IF
// Simulate process
10:
_tonProcessDuration(IN := TRUE, PT := T#10S);
IF _tonProcessDuration.Q THEN
_tonProcessDuration(IN := FALSE);
fbFlowRecHandler.M_ReportResult(iIdx := _iFlowRecIdx, xResult := TRUE);
_xProdAvail := TRUE;
_xBusy := FALSE;
_xDone := TRUE;
_iState := 20;
END_IF
// Wait for station to be empty again
20:
IF _iFlowRecIdx = -1 THEN
_iState := 0;
END_IF
END_CASE]]></ST>
</Implementation>
<Method Name="FB_Init" Id="{0669fde2-f64d-4e8f-b76d-71a71bfb6cae}">
<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
]]></Declaration>
<Implementation>
<ST><![CDATA[// Set station capabilities
_dwCapabilities := 16#0001;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,16 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_HVTest" Id="{b3445d38-3c32-4973-bf63-429552f0da50}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_HVTest EXTENDS FB_StationBase
<Declaration><![CDATA[FUNCTION_BLOCK FB_HVTest EXTENDS FB_BaseStation
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_iState : INT;
_tonProcessDuration : TON;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<ST><![CDATA[CASE _iState OF
// Wait for product
0:
IF _iFlowRecIdx <> -1 THEN
_xDone := FALSE;
_xBusy := TRUE;
_iState := 10;
END_IF
// Simulate process
10:
_tonProcessDuration(IN := TRUE, PT := T#10S);
IF _tonProcessDuration.Q THEN
_tonProcessDuration(IN := FALSE);
fbFlowRecHandler.M_ReportResult(iIdx := _iFlowRecIdx, xResult := TRUE);
_xProdAvail := TRUE;
_xBusy := FALSE;
_xDone := TRUE;
_iState := 20;
END_IF
// Wait for station to be empty again
20:
IF _iFlowRecIdx = -1 THEN
_iState := 0;
END_IF
END_CASE]]></ST>
</Implementation>
<Method Name="FB_Init" Id="{c40e1a0c-0b45-4ca1-a647-10e90c1b54ce}">
<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
]]></Declaration>
<Implementation>
<ST><![CDATA[// Set station capabilities
_dwCapabilities := 16#0002;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,16 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Input" Id="{d51b3f46-877e-4234-99b6-53eb690c6bdc}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Input EXTENDS FB_StationBase
<Declaration><![CDATA[FUNCTION_BLOCK FB_Input EXTENDS FB_BaseStation
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_tonNewProductReady : TON;
_stFlowRecipe : ST_FlowRecipe;
_xRecipeSet : BOOL;
_xStart : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<ST><![CDATA[_tonNewProductReady(IN := ((NOT _xProdAvail) AND _xRecipeSet AND _xStart), PT := T#5S);
IF _tonNewProductReady.Q THEN
_iFlowRecIdx := fbFlowRecHandler.M_AddFlowRec(stFlowRecipe := _stFlowRecipe);
// Todo create additional recipe data in pool array where the index
// is the same as _iFlowRecIdx
_xProdAvail := TRUE;
END_IF]]></ST>
</Implementation>
<Method Name="M_SetRecipe" Id="{58f2ee2a-65a7-4788-a8f6-1e5a7ab222c9}">
<Declaration><![CDATA[METHOD M_SetRecipe
VAR_INPUT
stRecipe : ST_FlowRecipe;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_stFlowRecipe := stRecipe;
_xRecipeSet := TRUE;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,16 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_NIO" Id="{2cf6f855-1738-40a7-bc96-4518cbb192c2}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_NIO EXTENDS FB_StationBase
<Declaration><![CDATA[FUNCTION_BLOCK FB_NIO EXTENDS FB_BaseStation
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_iState : INT;
_tonAutoRemove : TON;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<ST><![CDATA[CASE _iState OF
// Wait for product
0:
IF _iFlowRecIdx <> -1 THEN
_iState := 10;
END_IF
// Remove product from line
10:
_tonAutoRemove(IN := TRUE, PT := T#5S);
IF _tonAutoRemove.Q THEN
_tonAutoRemove(IN := FALSE);
fbFlowRecHandler.M_RemFlowRec(iIdx := _iFlowRecIdx);
_iFlowRecIdx := -1;
_iState := 0;
END_IF
END_CASE]]></ST>
</Implementation>
<Method Name="FB_Init" Id="{a2216f45-001c-4771-bc7d-1f63f2637a1e}">
<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
]]></Declaration>
<Implementation>
<ST><![CDATA[// Set station capabilities
_dwCapabilities := 16#0008;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -1,16 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Output" Id="{9711b552-d6a0-4f78-8a70-9ea977b18390}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Output EXTENDS FB_StationBase
<Declaration><![CDATA[FUNCTION_BLOCK FB_Output EXTENDS FB_BaseStation
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
_iState : INT;
_tonAutoRemove : TON;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
<ST><![CDATA[CASE _iState OF
// Wait for product
0:
IF _iFlowRecIdx <> -1 THEN
_iState := 10;
END_IF
// Remove product from line
10:
_tonAutoRemove(IN := TRUE, PT := T#5S);
IF _tonAutoRemove.Q THEN
_tonAutoRemove(IN := FALSE);
fbFlowRecHandler.M_RemFlowRec(iIdx := _iFlowRecIdx);
_iFlowRecIdx := -1;
_iState := 0;
END_IF
END_CASE]]></ST>
</Implementation>
<Method Name="FB_Init" Id="{ae701a9f-9b93-4e2d-8380-8e14731a8c09}">
<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
]]></Declaration>
<Implementation>
<ST><![CDATA[// Set station capabilities
_dwCapabilities := 16#0004;]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Robot" Id="{dc28987f-6f20-437a-aca7-b51d3eb98f94}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Robot EXTENDS FB_BaseTransport
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
fbScheduler : FB_Scheduler;
END_VAR
VAR
_tonTransportDone : TON;
_timTransportTime : TIME := T#5S;
_itfSourceStation : I_Station;
_itfTargetStation : I_Station;
_iFlowRecIdx : INT;
_iState : INT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[CASE _iState OF
// Idle, check for new transport job
0:
IF fbJobQueue.M_GetHighest(stJob => _stTransJon) THEN
_xAvailable := FALSE;
// Get station interfaces
_itfSourceStation := fbScheduler.M_GetStation(_stTransJon.uiFromStation);
_itfTargetStation := fbScheduler.M_GetStation(_stTransJon.uiToStation);
_iState := 10;
END_IF
// Move to pickup station
10:
_tonTransportDone(IN := TRUE, PT := _timTransportTime);
IF _tonTransportDone.Q THEN
_tonTransportDone(IN := FALSE);
// Get product from station
_iFlowRecIdx := _itfSourceStation.M_RemoveProduct();
_iState := 20;
END_IF
// Move to drop station
20:
_tonTransportDone(IN := TRUE, PT := _timTransportTime);
IF _tonTransportDone.Q THEN
_tonTransportDone(IN := FALSE);
// Put product into station
_itfTargetStation.M_InsertProduct(_iFlowRecIdx);
_iState := 30;
END_IF
// Release all station reservations
30:
_itfSourceStation.M_Release(uiHandle := _stTransJon.uiFromStationHandle);
_itfTargetStation.M_Release(uiHandle := _stTransJon.uiToStationHandle);
_xAvailable := TRUE;
_iState := 0;
END_CASE]]></ST>
</Implementation>
</POU>
</TcPlcObject>

View File

@@ -12,6 +12,5 @@
<Fb_exit>{baf80adf-b0f3-4c1e-958c-6b1cb1610e8e}</Fb_exit>
<CycleUpdate>{ff4c396c-9481-47ed-a237-b2f9054f4e68}</CycleUpdate>
<PostCycleUpdate>{e06145cf-bc12-4403-9e2e-7beeb337c603}</PostCycleUpdate>
<ObjectProperties />
</Task>
</TcPlcObject>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<TcSmProject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.beckhoff.com/schemas/2012/07/TcSmProject" TcSmVersion="1.0" TcVersion="3.1.4026.20">
<Project ProjectGUID="{499C0D3C-2952-40F2-B5CC-1C48D904BF27}" Target64Bit="true" ShowHideConfigurations="#x6">
<Project ProjectGUID="{499C0D3C-2952-40F2-B5CC-1C48D904BF27}" TargetNetId="199.4.42.250.1.1" Target64Bit="true" ShowHideConfigurations="#x6">
<System>
<Tasks>
<Task Id="3" Priority="20" CycleTime="100000" AmsPort="350" AdtTasks="true">

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<TcSmItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.beckhoff.com/schemas/2012/07/TcSmProject" TcSmVersion="1.0" TcVersion="3.1.4026.20" ClassName="CTComPlcObjDef">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" XtvPath="PLC\PLC Instance.xtv">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" XtvPath="PLC\PLC Instance.xtv" TmcPath="PLC\PLC.tmc">
<Name>__FILENAME__</Name>
<CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID>
<Contexts>