nope/modules/net-analyzer/registry/beckhoff.petri-net.templates.ts

685 lines
29 KiB
TypeScript
Raw Normal View History

2020-11-05 17:01:47 +00:00
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-07-16 10:52:32
2020-12-01 12:05:35 +00:00
* @modify date 2020-11-25 07:37:04
2020-11-05 17:01:47 +00:00
* @desc [description]
*/
export const LOGIC_TEMPLATES: {
[index: string]: string;
} = {
// Template to define the Places.
//
// Requires the following Input:
// {
// places: [ {id: string} ]
// options.nameOfStatesType: string
// }
statesStructTemplate: `<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.2">
<DUT Name="{{options.nameOfStatesType}}" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[TYPE {{options.nameOfStatesType}} :
STRUCT
{{#each places}}
{{id}} : INT; (* Element containing the Tokens of the Place {{id}} *)
{{/each}}
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>
`,
transitionStructTemplate: `<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.2">
<DUT Name="{{options.nameOfTransitionType}}" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[TYPE {{options.nameOfTransitionType}} :
STRUCT
iMaxTime: LWORD; (* Configured Max Time *)
iCurrentMaxTime: LWORD; (* Determined Max Time during the Runtime. This will be adapted automatically *)
iMinTime: LWORD; (* Configured Min Time *)
iCurrentMinTime: LWORD; (* Determined Min Time during the Runtime. This will be adapted automatically *)
bTimeParamsValid: BOOL; (* Flag indicating whether the Time Parameters are Valid or not *)
bUseMaxTime: BOOL; (* Flag to toggle on / off the Time Constraints for this Transition *)
bRelease: BOOL; (* Flag to toggle on Enable the Release of the Transition *)
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>`,
transitionVariableTemplate: `<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.2">
<DUT Name="{{options.nameOfUsedTransitionsVariable}}" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[TYPE {{options.nameOfUsedTransitionsVariable}} :
STRUCT
{{#each transitions}}
{{id}} : {{options.nameOfTransitionType}}; (* Element containing the Parameters for the Trantision {{id}} *)
{{/each}}
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>`,
2020-12-01 12:05:35 +00:00
ifHeaderTemplate: "IF ({{> testConsumedPlaces}}{{> testRequiredPlaces}}{{> testAvoidedPlaces}}{{> testPostPlaces}}{{> testGuard}} {{>testTime}} {{#if isAction}}aTransitions.{{id}}.bRelease AND{{/if}} iRecursionCounter < {{options.maxRecursion}}) THEN",
testConsumedPlaces: "{{#each consumed}} ((aTokens.{{placeId}}{{#if minTokens}} - {{minTokens}}{{/if}}) >= {{tokensToRemove}}) AND{{/each}}",
testRequiredPlaces: "{{#each required}} (aTokens.{{placeId}} > 0) AND{{/each}}",
testAvoidedPlaces: "{{#each avoided}} (aTokens.{{placeId}} = 0) AND{{/each}}",
testPostPlaces: "{{#if hasTestOutputs}}{{#each testOutputs}} ((aTokens.{{placeId}} + {{tokensToAdd}}) <= {{maxTokens}}) AND{{/each}}{{/if}}",
testGuard: " {{{ externalGuard }}}",
testTime: "AND (aTransitions.{{id}}.bTimeParamsValid AND iDeltaTime >= aTransitions.{{id}}.iCurrentMinTime AND (NOT(aTransitions.{{id}}.bUseMaxTime) OR iDeltaTime <= aTransitions.{{id}}.iCurrentMinTime)) AND",
removeTokens: "{{#each consumed}}aTokens.{{placeId}} := aTokens.{{placeId}} - {{tokensToRemove}};{{/each}}",
addTokens: "{{#each produced}}aTokens.{{placeId}} := aTokens.{{placeId}} + {{tokensToAdd}};{{/each}}",
ifLogicalHeaderTemplate: "IF ({{> testConsumedPlaces}}{{> testRequiredPlaces}}{{> testAvoidedPlaces}}{{> testPostPlaces}} TRUE) THEN",
2020-11-05 17:01:47 +00:00
// Template to define a Method on a Function-Block.
//
// Requires the following Input:
// {
// functionName: string,
// // Contains the Returnvalue.
// functionReturnValue: string,
// // Containing the Inputs
// functionInputs: string,
// // Containing the Implementation
// functionImplementation: string
// }
2020-12-01 12:05:35 +00:00
fbTransitionFunctionTemplate: "" + `<Method Name="{{methodName}}" Id="{{#uuid}}{{/uuid}}">
2020-11-05 17:01:47 +00:00
<Declaration><![CDATA[METHOD {{methodName}} : BOOL
VAR_INPUT
iRecursionCounter: UINT; (* Counter preventing endless recursions *)
bTriggered: BOOL; (* Flag, indicating whether a check should be performed or not *)
iCurrentTime: LWORD; (* Current Time Pointer *)
END_VAR
VAR
sId: STRING; (* Variable containing the MQTT-Content string. *)
bTempTriggered: BOOL; (* Flag, indicating whether a check should be performed or not *)
iTempRecursionCounter: UINT; (* Counter preventing endless recursions. Internally used *)
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[bTempTriggered := FALSE;
// Test if the Transition is able to Fire.
{{> ifHeaderTemplate}}
{{#if hasInputs}}
// Removing Tokens from Pre-Places.
{{> removeTokens}}
{{#each consumed}}
{{#timeAdaption this.placeId}}{{/timeAdaption}}
{{/each}}
{{/if}}
{{#if hasOutputs}}
// Adding Tokens to the Post-Places.
{{> addTokens}}
{{#each produced}}
{{#timeAdaption this.placeId}}{{/timeAdaption}}
{{/each}}
{{/if}}
{{#if functionName}}
// Execute the desired Function
{{#getFunctionTriggerCode this}}{{/getFunctionTriggerCode}}
{{/if}}
IF bEnableMQTT THEN
// If MQTT is enabled just forward the fired Transition.
sId:= '{{id}}';
fbMqttClient.Publish('{{options.nameOfPlc}}/fired',ADR(sID),LEN(sID),1,FALSE,FALSE);
END_IF
{{#if isAction}}
// Remove the Release
aTransitions.{{id}}.bRelease := FALSE;
{{/if}}
// Mark the Transition as Triggered
bTempTriggered := TRUE;
{{#if hasTriggers}}
// Prepare Triggering Elements.
iTempRecursionCounter := iRecursionCounter + 1;
{{#each triggered}}
// Trigger Transition {{{label}}}
{{methodName}}(iTempRecursionCounter, TRUE, iCurrentTime);
{{/each}}
{{/if}}
{{#if hasReleases}}
{{#each releases}}
// Set a Release to Transition {{{label}}}
aTransitions.{{this}}.bRelease := TRUE;
{{/each}}
{{/if}}
{{#if hasLocks}}
{{#each locks}}
// Lock the Transition {{{label}}}
aTransitions.{{this}}.bRelease := FALSE;
{{/each}}
{{/if}}
END_IF
// Transmit the Result.
{{methodName}} := bTempTriggered;]]></ST>
</Implementation>
</Method>
`,
2020-12-01 12:05:35 +00:00
fbTransitionAdaptTimesFunctionTemplate: "" + `<Method Name="{{methodName}}_ADAPT_TIMES" Id="{{#uuid}}{{/uuid}}">
2020-11-05 17:01:47 +00:00
<Declaration><![CDATA[METHOD {{methodName}}_ADAPT_TIMES : BOOL
VAR_INPUT
iCurrentTime: LWORD; (* Current Time Pointer *)
END_VAR
VAR
bTempEnabled: BOOL; (* Flag, indicating whether the Transition is logically enabled or not *)
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Toggle the Flag, that the Transition isnt able to fire.
bTempEnabled:= False;
// Test, if the Transition is logically able to Fire.
{{> ifLogicalHeaderTemplate}}
// The Transtion is logically able to Fire.
bTempEnabled := TRUE;
// Determine the Valid-Timestamp.
aTransitions.{{id}}.iCurrentMinTime := aTransitions.{{id}}.iMinTime + iCurrentTime;
aTransitions.{{id}}.iCurrentMaxTime := aTransitions.{{id}}.iMaxTime + iCurrentTime;
// Mark the Element as Enabled.
aTransitions.{{id}}.bTimeParamsValid := TRUE;
ELSE
// Mark the Element as Enabled.
aTransitions.{{id}}.bTimeParamsValid := FALSE;
END_IF
// Transmit the Result.
{{methodName}}_ADAPT_TIMES := bTempEnabled;]]></ST>
</Implementation>
</Method>
`,
// Template to define a logic based on a Petri-net.
//
// Requires the following Input:
// {
// uuid: '{uuid}',
// fbName: string,
// // Contains the Returnvalue.
// functionReturnValue: string,
// // Containing the Inputs
// functionInputs: string,
// // Containing the Implementation
// functionImplementation: string
// }
mainTemplate: `<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.2">
<POU Name="{{options.nameOfMainFb}}" Id="{{#uuid}}{{/uuid}}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK {{options.nameOfMainFb}}
VAR_INPUT
bEnableLogic : BOOL := FALSE; (* Flag to Enable / Disable the Main-Logic *)
bEnableMQTT : BOOL := FALSE; (* Flag to Enable / Disable publishing Fired Transitions *)
sHostName : STRING(255) := '127.0.0.1'; (* Hostname of the MQTT-Broker *)
nHostPort : UINT := 1883; (* Port of the MQTT-Broker *)
END_VAR
VAR_IN_OUT
bResetLogic : BOOL; (* Set to True to Reset the Logic. *)
END_VAR
VAR
bRunTestLoop : BOOL := FALSE;
iIterationCounter: INT := 0;
iTimeLoPart : UDINT; (* Low Bytes of the Current Time *)
iTimeHiPart : UDINT; (* High Bytes of the Current Time *)
iTimeLo64 : LWORD; (* Low Bytes of the Current Time *)
iTimeHi64 : LWORD; (* Low Bytes of the Current Time *)
iCurrentTime : LWORD; (* Flag containing the current Time. Should be LWORD, which isnt supported on the PLC *)
iDeltaTime : LWORD; (* Time Containing the Delta, between start and now. *)
// MQTT Related Stuff
fbMqttClient : FB_IotMqttClient; (* An MQTT-Client *)
fbMessageQueue : FB_IotMqttMessageQueue; (* MQTT-Message-Queue *)
fbMessage : FB_IotMqttMessage; (* MQTT-Message *)
bSubscribedRelease: BOOL;
sMQTTTopicRelease : STRING(255) := '{{options.nameOfPlc}}/release'; (* MQTT-Topic on which messages are expected to Release an Action *)
bSubscribedLock : BOOL;
sMQTTTopicLock : STRING(255) := '{{options.nameOfPlc}}/locks'; (* MQTT-Topic on which messages are expected to Lock an Action *)
bSubscribedTrigger: BOOL;
sMQTTTopicTrigger : STRING(255) := '{{options.nameOfPlc}}/trigger'; (* MQTT-Topic on which messages are expected to Trigger an Action *)
{attribute 'TcEncoding':='UTF-8'}
sTopicRcv : STRING(255); (* Received Topic *)
{attribute 'TcEncoding':='UTF-8'}
sPayloadRcv : STRING(255); (* Received Payload *)
// MQTT Stuff to Send the State of the Net.
fbTONUpdateStates: TON;
fbJson : FB_JsonSaxWriter; (* JSON-Writer *)
fbJsonDataType: FB_JsonReadWriteDataType; (* JSON-Object-Parser *)
{attribute 'TcEncoding':='UTF-8'}
sState : STRING(8192); (* String containing the JSON *)
iStateLength: UDINT; (* Length of the Parsed Content *)
END_VAR
VAR_STAT
fbTime : GETSYSTEMTIME; (* FB for getting the time *)
bFirstRun : BOOL := TRUE; (* Flag, used to mark the initial Run of the Petri-Net *)
bLogicEnabled : BOOL := FALSE; (* Internal Flag to Enable / Disable the Logic *)
iStartTime : LWORD; (* Start Timestamp *)
aTokens: {{options.nameOfStatesType}}; (* Struct containing the Tokens of the Net *)
aTransitions: {{options.nameOfUsedTransitionsVariable}}; (* Array containing all Time Constrains of the Transitions in the Net *)
{{! List all Variables below}}
{{#each variables}}
{{name}} : {{type}} := {{value}}; (* Accessor for Variable {{name}}. This Variable is used in the Logic *)
{{/each}}
{{! List all Subscribed Variables below}}
{{#each subscribedVariables}}
{{adaptedName}} : {{type}}; (* Accessor for Variable {{name}}. This Variable is used in the Logic *)
{{/each}}
// Section for Creating Instances:
{{#each instances}}
{{instanceName}}: {{fBName}}; (* Accessor for the required Instance for the Transition {{instanceName}} *)
{{#each inputs}}
{{name}}: {{type}}; (* Accessor for the Input {{name}} of {{fBName}} *)
{{/each}}
{{#each outputs}}
{{name}}: {{type}}; (* Accessor for the Output {{name}} of {{fBName}} *)
{{/each}}
{{/each}}
END_VAR
VAR_OUTPUT
bError : BOOL;
hrErrorCode : HRESULT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[
// If the Programm runs the First time, and MQTT Should be used.
// Make shure the Client is using the correct Connection-Params
IF bEnableMQTT AND bFirstRun THEN
fbMqttClient.sHostName := sHostName;
fbMqttClient.nHostPort := nHostPort;
fbMqttClient.sTopicPrefix := '';
fbMqttClient.nKeepAlive := 60;
fbMqttClient.ipMessageQueue := fbMessageQueue;
END_IF
// Enable the MQTT Connection
IF bEnableMQTT THEN
fbMqttClient.Execute(bEnableMQTT);
handle_mqtt_messages(iDeltaTime);
IF fbMqttClient.bError THEN
// add your error logging here
hrErrorCode := fbMqttClient.hrErrorCode;
END_IF
// If MQTT is connected Subscribe to the Topics
IF fbMqttClient.bConnected THEN
// Subscribe to Messages
IF NOT bSubscribedRelease THEN
bSubscribedRelease := fbMqttClient.Subscribe(sTopic:=sMQTTTopicRelease, eQoS:=TcIotMqttQos.AtMostOnceDelivery);
END_IF
IF NOT bSubscribedRelease THEN
bSubscribedRelease := fbMqttClient.Subscribe(sTopic:=sMQTTTopicLock, eQoS:=TcIotMqttQos.AtMostOnceDelivery);
END_IF
IF NOT bSubscribedRelease THEN
bSubscribedRelease := fbMqttClient.Subscribe(sTopic:=sMQTTTopicTrigger, eQoS:=TcIotMqttQos.AtMostOnceDelivery);
END_IF
// Test if the Timer is elapsed
IF fbTONUpdateStates.Q THEN
// Reset Timer
fbTONUpdateStates(IN:=FALSE,PT:=T#1000MS);
// Create the JSON Document
fbJson.ResetDocument();
fbJson.StartObject();
fbJsonDataType.AddJsonKeyValueFromSymbol(fbJson, 'marking', 'STATES_DUT', SIZEOF(aTokens), ADR(aTokens));
fbJson.EndObject();
// Parse the Object => Returns the Length
iStateLength := fbJson.CopyDocument(sState, SIZEOF(sState));
// Publish the State
IF iStateLength > 0 THEN
fbMqttClient.Publish('plc-0003/logic',ADR(sState),iStateLength,1,FALSE,FALSE);
END_IF
ELSE
// Call the Timer
fbTONUpdateStates(IN:=TRUE,PT:=T#1000MS);
END_IF
END_IF
END_IF
// Determine if the Logic is allowed to execute
IF bEnableMQTT AND bEnableLogic THEN
// Only allow the Logic to go one, if the MQTT-Client is connected
bLogicEnabled := fbMqttClient.bConnected;
ELSE
// MQTT is disabled => Use the Enable Flag.
bLogicEnabled := bEnableLogic;
END_IF
// The Main Loop. This Loop contains the compiled Petri-Net
IF bLogicEnabled THEN
// Update the Inputs
read_inputs();
// Get the Current Time
fbTime(timeLoDW=>iTimeLoPart, timeHiDW=>iTimeHiPart);
// Convert The Time to an LWORD Value.
iTimeLo64 := UDINT_TO_LWORD(iTimeLoPart);
iTimeHi64 := UDINT_TO_LWORD(iTimeHiPart);
// Assemble the current Time. (Shift the Higher one by 2**32)
iCurrentTime := (iTimeHi64 * 4294967296 + iTimeLo64);
// The Resolution is 1 ms (Originally 100ns => Resolution)
iCurrentTime := iCurrentTime / (1000 * 1000 / 100);
// If the Programm runs the First time or the Logic should be
// resetted => Reset the Tokens of the Petri-Net.
IF bFirstRun OR bResetLogic THEN
// Resetting the State of the Petri-Net
reset_tokens();
// Turn Off logic Reset.
bResetLogic := FALSE;
bFirstRun := FALSE;
// During the First Run Assing the Start-Time
iStartTime := iCurrentTime;
// Upadte the Time-Parameters.
reset_transtions();
END_IF
// Determine the Delta of the time.
iDeltaTime := iCurrentTime - iStartTime;
// Flag, indicating whether the System should test for other Transitions or not.
bRunTestLoop := TRUE;
// Reset the Iteration iIterationCounter
iIterationCounter := 0;
WHILE bRunTestLoop AND iIterationCounter < {{options.maxIterations}} DO
// Mark the Loop as tested.
bRunTestLoop := FALSE;
// Update and Call the Instances
updateInstances();
callInstances();
// Rise the Iteration Counter.
iIterationCounter := iIterationCounter + 1;
{{#each transitionsSortedByPriority}}
{{#if isFirst}}IF{{/if}}{{#unless isFirst}}ELSIF{{/unless}} {{methodName}}(0, FALSE, iDeltaTime) THEN
// Transition "{{{label}}}" fired!
bRunTestLoop := TRUE;
{{#if isLast}}
END_IF
{{/if}}
{{/each}}
END_WHILE
END_IF
]]></ST>
</Implementation>
{{#each transitions}}
{{> fbTransitionFunctionTemplate}}
{{> fbTransitionAdaptTimesFunctionTemplate}}
{{/each}}
<Method Name="updateInstances" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD updateInstances : BOOL]]></Declaration>
<Implementation>
<ST>
<![CDATA[{{#each instances}}// Call Signature for {{instanceName}} (related to {{transitionName}} and using {{fbName}})
{{{callCode}}}
{{/each}}]]>
</ST>
</Implementation>
</Method>
<Method Name="callInstances" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD callInstances : BOOL]]></Declaration>
<Implementation>
<ST><![CDATA[callInstances:= TRUE;]]>
</ST>
</Implementation>
</Method>
<Method Name="reset_transtions" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD reset_transtions : BOOL]]></Declaration>
<Implementation>
<ST><![CDATA[// Function to Reset the Times of the Net.
reset_transtions:= TRUE;
{{#each transitions}}
// Set the Initial Static Parameters for Transition {{id}}
// Time is provided in [ms]. Add Additional {{options.additionalDelay}} ms. This will be based on the Implementation
aTransitions.{{id}}.iMaxTime := {{#if useMaxTime}}{{maxTime}} + {{options.additionalDelay}}{{else}}0{{/if}};
aTransitions.{{id}}.iMinTime := {{minTime}};
// aTransitions.{{id}}.bUseMaxTime:= {{#if useMaxTime}}TRUE{{else}}FALSE{{/if}};
aTransitions.{{id}}.bUseMaxTime := FALSE;
// Test if Transition {{id}} is enabled and adapt the dynamic Times-Parameters used in the Logic.
{{methodName}}_ADAPT_TIMES(0);
{{/each}}
]]>
</ST>
</Implementation>
</Method>
<Method Name="reset_tokens" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD reset_tokens : BOOL]]></Declaration>
<Implementation>
<ST><![CDATA[// Function to Reset the Tokens of the Net.
reset_tokens:= TRUE;
{{#each places}}
aTokens.{{id}} := {{tokens.length}};
{{/each}}]]>
</ST>
</Implementation>
</Method>
<Method Name="read_inputs" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD read_inputs : BOOL]]></Declaration>
<Implementation>
<ST><![CDATA[// Function to Update the Variables Used in the Logic.
read_inputs:= TRUE;
{{#each subscribedVariables}}
{{adaptedName}} := {{#getAccessorOfGuardVariable this}}{{/getAccessorOfGuardVariable}};
{{/each}}
]]>
</ST>
</Implementation>
</Method>
<Method Name="handle_mqtt_messages" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[METHOD handle_mqtt_messages : BOOL
VAR_INPUT
iCurrentTime: LWORD; (* Current Time Pointer *)
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[// Function to Update the Variables Used in the Logic.
handle_mqtt_messages:= TRUE;
// If MQTT is connected Subscribe to the Topics
IF fbMessageQueue.nQueuedMessages > 0 THEN
IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
// Assign the Message handling based on the Topic:
fbMessage.GetTopic(pTopic:=ADR(sTopicRcv), nTopicSize:=SIZEOF(sTopicRcv) );
sPayloadRcv := '';
fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
IF sTopicRcv=sMQTTTopicRelease THEN
{{#each actions}}
{{#if isFirst}}IF{{/if}}{{#unless isFirst}}ELSIF{{/unless}} bEnableLogic AND sPayloadRcv = '"{{id}}"' THEN
// Release the Transition {{label}}
aTransitions.{{id}}.bRelease := TRUE;
{{#if isLast}}
END_IF
{{/if}}{{/each}}
ELSIF sTopicRcv=sMQTTTopicLocK THEN
{{#each actions}}
{{#if isFirst}}IF{{/if}}{{#unless isFirst}}ELSIF{{/unless}} bEnableLogic AND sPayloadRcv = '"{{id}}"' THEN
// Lock the Transition {{label}}
aTransitions.{{id}}.bRelease := FALSE;
{{#if isLast}}
END_IF
{{/if}}{{/each}}
ELSIF sTopicRcv=sMQTTTopicTrigger THEN
{{#each actions}}
{{#if isFirst}}IF{{/if}}{{#unless isFirst}}ELSIF{{/unless}} bEnableLogic AND sPayloadRcv = '"{{id}}"' THEN
// Trigger Transition {{label}}
{{methodName}}(0, FALSE, iCurrentTime);
{{#if isLast}}
END_IF
{{/if}}{{/each}}
END_IF
END_IF
END_IF
]]>
</ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>`,
plcProject: `<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<FileVersion>1.0.0.0</FileVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{d052bc74-6748-44e9-ab97-c76a5465eacb}</ProjectGuid>
<SubObjectsSortedByName>True</SubObjectsSortedByName>
<DownloadApplicationInfo>true</DownloadApplicationInfo>
<WriteProductVersion>true</WriteProductVersion>
<GenerateTpy>false</GenerateTpy>
<Name>Testcode</Name>
<ProgramVersion>3.1.4023.0</ProgramVersion>
<Application>{bbff2287-a113-4d28-8f6d-efcabdf5d264}</Application>
<TypeSystem>{b24aea9d-6e67-48f8-a831-3bc6f2342ea4}</TypeSystem>
<Implicit_Task_Info>{da805644-4748-448f-b5b2-6ca842e9030d}</Implicit_Task_Info>
<Implicit_KindOfTask>{56ede67f-e61e-4c59-92de-6e963a28d9bb}</Implicit_KindOfTask>
<Implicit_Jitter_Distribution>{cd90ab99-2020-4094-a370-afc280347629}</Implicit_Jitter_Distribution>
<LibraryReferences>{114fa4ae-03f9-4200-addb-a4bf60a385a9}</LibraryReferences>
</PropertyGroup>
<ItemGroup>
{{#each codeFiles}}
<Compile Include="{{path}}">
<SubType>Code</SubType>
</Compile>
{{/each}}
</ItemGroup>
<ItemGroup>
<Folder Include="DUTs" />
<Folder Include="GVLs" />
<Folder Include="VISUs" />
<Folder Include="POUs" />
</ItemGroup>
<ItemGroup>
<PlaceholderReference Include="Tc2_Standard">
<DefaultResolution>Tc2_Standard, * (Beckhoff Automation GmbH)</DefaultResolution>
<Namespace>Tc2_Standard</Namespace>
</PlaceholderReference>
<PlaceholderReference Include="Tc2_System">
<DefaultResolution>Tc2_System, * (Beckhoff Automation GmbH)</DefaultResolution>
<Namespace>Tc2_System</Namespace>
</PlaceholderReference>
<PlaceholderReference Include="Tc2_Utilities">
<DefaultResolution>Tc2_Utilities, * (Beckhoff Automation GmbH)</DefaultResolution>
<Namespace>Tc2_Utilities</Namespace>
</PlaceholderReference>
<PlaceholderReference Include="Tc3_IotBase">
<DefaultResolution>Tc3_IotBase, * (Beckhoff Automation GmbH)</DefaultResolution>
<Namespace>Tc3_IotBase</Namespace>
</PlaceholderReference>
<PlaceholderReference Include="Tc3_Module">
<DefaultResolution>Tc3_Module, * (Beckhoff Automation GmbH)</DefaultResolution>
<Namespace>Tc3_Module</Namespace>
</PlaceholderReference>
</ItemGroup>
<ProjectExtensions>
<PlcProjectOptions>
<XmlArchive>
<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>
<o>
<v n="Name">"{192FAD59-8248-4824-A8DE-9177C94C195A}"</v>
<d n="SubKeys" t="Hashtable" />
<d n="Values" t="Hashtable" />
</o>
<v>{8F99A816-E488-41E4-9FA3-846536012284}</v>
<o>
<v n="Name">"{8F99A816-E488-41E4-9FA3-846536012284}"</v>
<d n="SubKeys" t="Hashtable" />
<d n="Values" t="Hashtable" />
</o>
<v>{40450F57-0AA3-4216-96F3-5444ECB29763}</v>
<o>
<v n="Name">"{40450F57-0AA3-4216-96F3-5444ECB29763}"</v>
<d n="SubKeys" t="Hashtable" />
<d n="Values" t="Hashtable" ckt="String" cvt="String">
<v>ActiveVisuProfile</v>
<v>IR0whWr8bwfwBwAAhiaVXgAAAABVAgAAWdTSaQAAAAABAAAAAAAAAAEaUwB5AHMAdABlAG0ALgBTAHQAcgBpAG4AZwACTHsAZgA5ADUAYgBiADQAMgA2AC0ANQA1ADIANAAtADQAYgA0ADUALQA5ADQAMAAwAC0AZgBiADAAZgAyAGUANwA3AGUANQAxAGIAfQADCE4AYQBtAGUABDBUAHcAaQBuAEMAQQBUACAAMwAuADEAIABCAHUAaQBsAGQAIAA0ADAAMgA0AC4AMAAFFlAAcgBvAGYAaQBsAGUARABhAHQAYQAGTHsAMQA2AGUANQA1AGIANgAwAC0ANwAwADQAMwAtADQAYQA2ADMALQBiADYANQBiAC0ANgAxADQANwAxADMAOAA3ADgAZAA0ADIAfQAHEkwAaQBiAHIAYQByAGkAZQBzAAhMewAzAGIAZgBkADUANAA1ADkALQBiADAANwBmAC0ANABkADYAZQAtAGEAZQAxAGEALQBhADgAMwAzADUANgBhADUANQAxADQAMgB9AAlMewA5AGMAOQA1ADgAOQA2ADgALQAyAGMAOAA1AC0ANAAxAGIAYgAtADgAOAA3ADEALQA4ADkANQBmAGYAMQBmAGUAZABlADEAYQB9AAoOVgBlAHIAcwBpAG8AbgALBmkAbgB0AAwKVQBzAGEAZwBlAA0KVABpAHQAbABlAA4aVgBpAHMAdQBFAGwAZQBtAE0AZQB0AGUAcgAPDkMAbwBtAHAAYQBuAHkAEAxTAHkAcwB0AGUAbQARElYAaQBzAHUARQBsAGUAbQBzABIwVgBpAHMAdQBFAGwAZQBtAHMAUwBwAGUAYwBpAGEAbABDAG8AbgB0AHIAbwBsAHMAEyhWAGkAcwB1AEUAbABlAG0AcwBXAGkAbgBDAG8AbgB0AHIAbwBsAHMAFCRWAGkAcwB1AEUAbABlAG0AVABlAHgAdABFAGQAaQB0AG8AcgAVIlYAaQBzAHUATgBhAHQAaQB2AGUAQwBvAG4AdAByAG8AbAAWFHYAaQBzAHUAaQBuAHAAdQB0AHMAFwxzAHkAcwB0AGUAbQAYGFYAaQBzAHUARQBsAGUAbQBCAGEAcwBlABkmRABlAHYAUABsAGEAYwBlAGgAbwBsAGQAZQByAHMAVQBzAGUAZAAaCGIAbwBvAGwAGyJQAGwAdQBnAGkAbgBDAG8AbgBzAHQAcgBhAGkAbgB0AHMAHEx7ADQAMwBkADUAMgBiAGMAZQAtADkANAAyAGMALQA0ADQAZAA3AC0AOQBlADkANAAtADEAYgBmAGQAZgAzADEAMABlADYAMwBjAH0AHRxBAHQATABlAGEAcwB0AFYAZQByAHMAaQBvAG4AHhRQAGwAdQBnAGkAbgBHAHUAaQBkAB8WUwB5AHMAdABlAG0ALgBHAHUAaQBkACBIYQBmAGMAZAA1ADQANAA2AC0ANAA5ADEANAAtADQAZgBlADcALQBiAGIANwA4AC0AOQBiAGYAZgBlAGIANwAwAGYAZAAxADcAIRRVAHAAZABhAHQAZQBJAG4AZgBvACJMewBiADAAMwAzADYANgBhADgALQBiADUAYwAwAC0ANABiADkAYQAtAGEAMAAwAGUALQBlAGIAOAA2ADAAMQAxADEAMAA0AGMAMwB9ACMOVQBwAGQAYQB0AGUAcwAkTHsAMQA4ADYAOABmAGYAYwA5AC0AZQA0AGYAYwAtADQANQAzADIALQBhAGMAMAA2AC0AMQBlADMAOQBiAGIANQA1ADcAYgA2ADkAfQAlTHsAYQA1AGIAZAA0ADgAYwAzAC0AMABkADEANwAtADQAMQBiADUALQBiADEANgA0AC0ANQBmAGMANgBhAGQAMgBiADkANgBiADcAfQAmFk8AYgBqAGUAYwB0AHMAVAB5AHAAZQAnVFUAcABkAGEAdABlAEwAYQBuAGcAdQBhAGcAZQBNAG8AZABlAGwARgBvAHIAQwBvAG4AdgBlAHIAdABpAGIAbABlAEwAaQBiAHIAYQByAGkAZQBzACgQTABpAGIAVABpAHQAbABlACkUTABpAGIAQwBvAG0AcABhAG4AeQAqHlUAcABkAGEAdABlAFAAcgBvAHYAaQBkAGUAcgBzACs4UwB5AHMAdABlAG0ALgBDAG8AbABsAGUAYwB0AGkAbwBuAHMALgBIAGEAcwBoAHQAYQBiAGwAZQAsEnYAaQBzAHUAZQBsAGUAbQBzAC1INgBjAGIAMQBjAGQAZQAxAC0AZAA1AGQAYwAtADQAYQAzAGIALQA5ADAANQA0AC0AMgAxAGYAYQA3ADUANgBhADMAZgBhADQALihJAG4AdABlAHIAZgBhAGMAZQBWAGUAcgBzAGkAbwBuAEkAbgBmAG8AL0x7AGMANgAxADEAZQA0ADAAMAAtADcAZgBiADkALQA0AGMAMwA1AC0AYgA5AGEAYwAtADQAZQAzADEANABiADUAOQA5ADYANAAzAH0AMBhNAGEAagBvAHIAVgBlAHIAcwBpAG8AbgAxGE0AaQBuAG8AcgBWAGUAcgBzAGkAbwBuADIMTABlAGcAYQBjAHkAMzBMAGEAbgBnAHUAYQBnAGUATQBvAGQAZQBsAFYAZQByAHMAaQBvAG4ASQBuAGYAbwA0MEwAbwBhAGQATABpAGIAcgBhAHIAaQBlAHMASQBuAHQAbwBQAHIAbwBqAGUAYwB0ADUaQwBvAG0AcABhAHQAaQBiAGkAbABpAHQAeQDQAAIaA9ADAS0E0AUGGgfQBwgaAUUHCQjQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtDtAPAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAFQAAANAMC60BAAAA0A0BLRHQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0S0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAUAAAA0AwLrQIAAADQDQEtE9APAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAAAAAANAMC60CAAAA0A0BLRTQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0V0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtFtAPAS0X0AAJGgRFCgsEAwAAAAUAAAANAAAAFQAAANAMC60EAAAA0A0BLRjQDwEtENAZGq0BRRscAdAAHBoCRR0LBAMAAAAFAAAADQAAAAAAAADQHh8tINAhIhoCRSMkAtAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAAAAANADAS0n0CgBLRHQKQEtENAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAQAAANADAS0n0CgBLRHQKQEtEJoqKwFFAAEC0AABLSzQAAEtF9AAHy0t0C4vGgPQMAutAQAAANAxC60XAAAA0DIarQDQMy8aA9AwC60CAAAA0DELrQMAAADQMhqtANA0Gq0A0DUarQA=</v>
</d>
</o>
</d>
<d n="Values" t="Hashtable" />
</o>
</Data>
<TypeList>
<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>
`,
plcTask: `
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<Task Name="PlcTask" Id="{8b15b34f-602d-4143-a3e1-d8fe7e6a9a07}">
<!--CycleTime in micro seconds.-->
<CycleTime>10000</CycleTime>
<Priority>20</Priority>
<PouCall>
<Name>{{nameOfMainFb}}</Name>
</PouCall>
<TaskFBGuid>{40b78878-0487-47d4-ba7d-2f596fd0f0fe}</TaskFBGuid>
<Fb_init>{bf297dd4-78e5-43f2-b98d-3899ae75bf54}</Fb_init>
<Fb_exit>{9e24ead9-19c2-4f25-b930-d7c9fbcaf678}</Fb_exit>
<CycleUpdate>{94c51f5d-cf3e-4304-857a-330e87fa21ab}</CycleUpdate>
<PostCycleUpdate>{a9235a07-fbb3-4457-81e0-1ff81b29917f}</PostCycleUpdate>
<ObjectProperties />
</Task>
</TcPlcObject>
`,
gvlLogic: `
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.2">
<GVL Name="GVL_Logic" Id="{{#uuid}}{{/uuid}}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL
fbLogic01: FB_MAIN;
END_VAR]]></Declaration>
</GVL>
</TcPlcObject>`
};