Compare commits

...

10 Commits

24 changed files with 24006 additions and 858 deletions

View File

@ -0,0 +1 @@
<WorkspaceLanguage>en-US</WorkspaceLanguage>

View File

@ -0,0 +1,477 @@
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V18" />
<SW.Blocks.FC ID="0">
<AttributeList>
<AutoNumber>false</AutoNumber>
<HeaderFamily>TASK2</HeaderFamily>
<Interface><Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v5">
<Section Name="Input" />
<Section Name="Output" />
<Section Name="InOut" />
<Section Name="Temp" />
<Section Name="Constant" />
<Section Name="Return">
<Member Name="Ret_Val" Datatype="Void" />
</Section>
</Sections></Interface>
<MemoryLayout>Standard</MemoryLayout>
<Name>BlenderCtrl_ProdModeInit</Name>
<Namespace />
<Number>2012</Number>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
<SetENOAutomatically>false</SetENOAutomatically>
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="2" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="4" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="5" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="6" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="7" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="8" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<SW.Blocks.CompileUnit ID="9" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Call UId="21">
<CallInfo Name="BlenderPID_PIDResInteg" BlockType="FC" />
</Call>
</Parts>
<Wires>
<Wire UId="22">
<Powerrail />
<NameCon UId="21" Name="en" />
</Wire>
</Wires>
</FlgNet></NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="A" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="B" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="C" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="D" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="E" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="F" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="10" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="11" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="12" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="13" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>PID Reset Integral</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="14" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="15" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="16" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="17" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="18" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="19" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<SW.Blocks.CompileUnit ID="1A" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Call UId="21">
<CallInfo Name="BlenderCtrl_InitErrors" BlockType="FC" />
</Call>
</Parts>
<Wires>
<Wire UId="22">
<Powerrail />
<NameCon UId="21" Name="en" />
</Wire>
</Wires>
</FlgNet></NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="1B" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="1C" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1D" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1E" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1F" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="20" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="21" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="22" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="23" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="24" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>Ctrl Init Errors</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="25" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="26" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="27" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="28" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="29" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2A" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<SW.Blocks.CompileUnit ID="2B" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Access Scope="LiteralConstant" UId="21">
<Constant>
<ConstantType>Real</ConstantType>
<ConstantValue>0.0</ConstantValue>
</Constant>
</Access>
<Access Scope="GlobalVariable" UId="22">
<Symbol>
<Component Name="HMI_Variables_Status" />
<Component Name="Analog_Values" />
<Component Name="TP301RunOutCount" />
</Symbol>
</Access>
<Part Name="Move" UId="23" DisabledENO="false">
<TemplateValue Name="Card" Type="Cardinality">1</TemplateValue>
</Part>
</Parts>
<Wires>
<Wire UId="24">
<IdentCon UId="21" />
<NameCon UId="23" Name="in" />
</Wire>
<Wire UId="25">
<NameCon UId="23" Name="out1" />
<IdentCon UId="22" />
</Wire>
<Wire UId="26">
<Powerrail />
<NameCon UId="23" Name="en" />
</Wire>
</Wires>
</FlgNet></NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="2C" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="2D" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2E" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2F" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="30" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="31" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="32" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="33" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="34" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="35" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>RunOut Counter</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="36" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="37" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="38" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="39" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3A" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3B" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<MultilingualText ID="3C" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="3D" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>Prode Mode Init</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3E" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3F" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="40" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="41" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="42" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="43" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.FC>
</Document>

View File

@ -0,0 +1,93 @@
{
"block_name": "BlenderCtrl_ProdModeInit",
"block_number": 2012,
"language": "LAD",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void"
}
]
},
"networks": [
{
"id": "9",
"title": "PID Reset Integral",
"comment": "",
"logic": [
{
"instruction_uid": "21",
"uid": "21",
"type": "Call",
"block_name": "BlenderPID_PIDResInteg",
"block_type": "FC",
"inputs": {
"en": {
"type": "powerrail"
}
},
"outputs": {}
}
]
},
{
"id": "1A",
"title": "Ctrl Init Errors",
"comment": "",
"logic": [
{
"instruction_uid": "21",
"uid": "21",
"type": "Call",
"block_name": "BlenderCtrl_InitErrors",
"block_type": "FC",
"inputs": {
"en": {
"type": "powerrail"
}
},
"outputs": {}
}
]
},
{
"id": "2B",
"title": "RunOut Counter",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"en": {
"type": "powerrail"
},
"in": {
"uid": "21",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Real",
"value": 0.0
}
},
"outputs": {
"out1": [
{
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"HMI_Variables_Status\".\"Analog_Values\".\"TP301RunOutCount\""
}
]
}
}
]
}
]
}

View File

@ -0,0 +1,96 @@
{
"block_name": "BlenderCtrl_ProdModeInit",
"block_number": 2012,
"language": "LAD",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void"
}
]
},
"networks": [
{
"id": "9",
"title": "PID Reset Integral",
"comment": "",
"logic": [
{
"instruction_uid": "21",
"uid": "21",
"type": "Call_FC_scl",
"block_name": "BlenderPID_PIDResInteg",
"block_type": "FC",
"inputs": {
"en": {
"type": "powerrail"
}
},
"outputs": {},
"scl": "BlenderPID_PIDResInteg();"
}
]
},
{
"id": "1A",
"title": "Ctrl Init Errors",
"comment": "",
"logic": [
{
"instruction_uid": "21",
"uid": "21",
"type": "Call_FC_scl",
"block_name": "BlenderCtrl_InitErrors",
"block_type": "FC",
"inputs": {
"en": {
"type": "powerrail"
}
},
"outputs": {},
"scl": "BlenderCtrl_InitErrors();"
}
]
},
{
"id": "2B",
"title": "RunOut Counter",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Move_scl",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"en": {
"type": "powerrail"
},
"in": {
"uid": "21",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Real",
"value": 0.0
}
},
"outputs": {
"out1": [
{
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"HMI_Variables_Status\".\"Analog_Values\".\"TP301RunOutCount\""
}
]
},
"scl": "\"HMI_Variables_Status\".\"Analog_Values\".\"TP301RunOutCount\" := 0.0;"
}
]
}
]
}

View File

@ -0,0 +1,35 @@
// Block Name (Original): BlenderCtrl_ProdModeInit
// Block Number: 2012
// Original Language: LAD
FUNCTION_BLOCK "BlenderCtrl_ProdModeInit"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR_TEMP
END_VAR
BEGIN
// Network 1: PID Reset Integral
BlenderPID_PIDResInteg();
// Network 2: Ctrl Init Errors
BlenderCtrl_InitErrors();
// Network 3: RunOut Counter
"HMI_Variables_Status"."Analog_Values"."TP301RunOutCount" := 0.0;
END_FUNCTION_BLOCK

10030
BlenderCtrl__Main.xml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,392 @@
// Block Name (Original): BlenderCtrl__Main
// Block Number: 2000
// Original Language: LAD
FUNCTION_BLOCK "BlenderCtrl__Main"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR_TEMP
All_Auto_RETVAL : Int;
Reset_SP_Word_RETVAL : Int;
mResetWaterTot : Bool;
mResetSyrupTot : Bool;
mResetCO2Tot : Bool;
mResetProductTot : Bool;
Block_Move_Err : Int;
END_VAR
BEGIN
// Network 1: Clock Generation
Clock Signal();
// Network 2: Machine Init
BlenderCtrl_MachineInit();
// Network 3: Filler Head
// RLO: "AUX FALSE"
// Network 4: Emergency Pressed
// RLO: "gIN_VoltageOk"
// Network 5: Air and CO2 pressure ok and auxiliary ok
// RLO: "gIN_LinePressCO2Ok"
// RLO: "gWorkshopTest"
// RLO: "gWorkshopTest" AND "gWorkshop_Co2_Presence"
// RLO: ("gWorkshopTest" AND "gWorkshop_Co2_Presence") AND "gWorkshop_CIP_Signals"
// RLO: ("gIN_LinePressCO2Ok" OR (("gWorkshopTest" AND "gWorkshop_Co2_Presence") AND "gWorkshop_CIP_Signals")) AND "HMI_Digital"."_PAL_S11"."Filtered"
// RLO: "Disable_Bit"
// RLO: ((("gIN_LinePressCO2Ok" OR (("gWorkshopTest" AND "gWorkshop_Co2_Presence") AND "gWorkshop_CIP_Signals")) AND "HMI_Digital"."_PAL_S11"."Filtered") OR "Disable_Bit") AND "gIN_VoltageOk"
"gBlenderSuppliesOk" := ((("gIN_LinePressCO2Ok" OR (("gWorkshopTest" AND "gWorkshop_Co2_Presence") AND "gWorkshop_CIP_Signals")) AND "HMI_Digital"."_PAL_S11"."Filtered") OR "Disable_Bit") AND "gIN_VoltageOk";
// Network 6: Blender State Num
"HMI_Variables_Status"."Procedures"."BlenderStateNum" := 0;
// Network 7: Delay Power On
// RLO: "FirstScan"
// Network 8: Production Mode
// RLO: "HMI_Variables_Status"."System"."Blender_Prod_CIP"
"gBlenderProdMode" := "HMI_Variables_Status"."System"."Blender_Prod_CIP";
// Network 9: CIp Mode
// RLO: "HMI_Variables_Status"."System"."Blender_Prod_CIP"
"gBlenderCIPMode" := "HMI_Variables_Status"."System"."Blender_Prod_CIP";
IF "HMI_Variables_Status"."System"."Blender_Prod_CIP" THEN
"HMI_Variables_Status"."Procedures"."BlenderStateNum" := 19;
END_IF;
// Network 10: Error Faults
// RLO: "AUX FALSE"
// Network 11: Filler Bottle Count Used to push Product
// RLO: "System_RunOut_Variables"."ProdPipeRunOutWaterCount"
"System_RunOut_Variables"."ProdPipeRunOutFillerBott" := "System_RunOut_Variables"."ProdPipeRunOutWaterCount";
// Network 12: Water Bypass Enable
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass"
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair"
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation"
// RLO: ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation")) AND "Blender_Variables_Pers"."gWaterRecipe"
// RLO: (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation")) AND "Blender_Variables_Pers"."gWaterRecipe") AND "Blender_Variables_Pers"."gCarboStillRecipe"
"gStillWaterByPassEn" := (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation")) AND "Blender_Variables_Pers"."gWaterRecipe") AND "Blender_Variables_Pers"."gCarboStillRecipe";
// Network 13: Still Water Bypass
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem"
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass"
// RLO: ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass") AND "Blender_Variables_Pers"."gWaterRecipe"
// RLO: (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass") AND "Blender_Variables_Pers"."gWaterRecipe") AND "Blender_Variables_Pers"."gCarboStillRecipe"
"gBlendFiStillWaterByPass" := (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass") AND "Blender_Variables_Pers"."gWaterRecipe") AND "Blender_Variables_Pers"."gCarboStillRecipe";
// Network 14: Manual Syrup Drain Valve Open - Operator Alarm
// RLO: "gSyrupRoomEn"
// RLO: "gSyrupRoomEn" AND "gIN_HVP301_Aux"
// RLO: ("gSyrupRoomEn" AND "gIN_HVP301_Aux") AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled"
// RLO: (("gSyrupRoomEn" AND "gIN_HVP301_Aux") AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled") AND "Procedure_Variables"."FTP302Line_Preparation"."Done"
// RLO: ((("gSyrupRoomEn" AND "gIN_HVP301_Aux") AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled") AND "Procedure_Variables"."FTP302Line_Preparation"."Done") AND "Procedure_Variables"."Syr_RunOut"."Done"
// RLO: "gBlenderCIPMode"
// RLO: "gBlenderCIPMode" AND "gIN_CIP_CIPRunning"
// RLO: ("gBlenderCIPMode" AND "gIN_CIP_CIPRunning") AND "Procedure_Variables"."Blender_Run"."Running"
"gHVP301_Open" := (((("gSyrupRoomEn" AND "gIN_HVP301_Aux") AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled") AND "Procedure_Variables"."FTP302Line_Preparation"."Done") AND "Procedure_Variables"."Syr_RunOut"."Done") OR (("gBlenderCIPMode" AND "gIN_CIP_CIPRunning") AND "Procedure_Variables"."Blender_Run"."Running");
// Network 15: Manual Syrup Drain Valve Open - Operator Alarm
// RLO: "gIN_HVM302_Aux"
// Network 16: Maselli Control
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BrixMeter"
// Cond: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 6
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 6 THEN
Maselli_PA_Control();
END_IF;
// Network 17: mPDS Control
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BrixMeter"
// Cond: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 5
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 5 THEN
mPDS_PA_Control();
END_IF;
// Network 18: mPDS Syrup Control
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter"
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" THEN
mPDS_SYR_PA_Control();
END_IF;
// Network 19: Co2 Analog Input
// GetProdBrixCO2_FromAnalogIn
// CALL "GetProdBrixCO2_FromAn"
// NOP 0
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BrixMeter"
// Cond: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 3
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 3 THEN
GetProdBrixCO2_Anal_Inpt();
END_IF;
// Network 20: Quality
ProductQuality();
// Network 21: Input Data
// ERROR: Call a bloque no soportado: Input
// Network 22: Sel Brix Source Check
SelCheckBrixSource();
// Network 23: Check Water Cooling System Temperature
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_InverterRecirPumpPPM306"
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_InverterRecirPumpPPM306" THEN
CTRLCoolingSystem();
END_IF;
// Network 24: Tank Level
TankLevel();
// Network 25: Production ONS
// RLO: "gBlenderProdMode"
// PBox 26 - Passing bit: "M19001"
// RLO: "M19001" AND "mDelayPowerOnTmr"
"gProductionONS" := "M19001" AND "mDelayPowerOnTmr";
// Network 26: Blender Prod Mode Init
// RLO: "gProductionONS"
// RLO: "Procedure_Variables"."Blender_Rinse"."ONS_Done"
// RLO: ("gProductionONS" OR "Procedure_Variables"."Blender_Rinse"."ONS_Done") AND "Blender_Variables_Pers"."gBlenderStarted"
IF ("gProductionONS" OR "Procedure_Variables"."Blender_Rinse"."ONS_Done") AND "Blender_Variables_Pers"."gBlenderStarted" THEN
BlenderCtrl_ProdModeInit();
END_IF;
// Network 27: Rinse ONS
// RLO: "HMI_Variables_Status"."System"."Blender_Prod_CIP"
// PBox 26 - Passing bit: "M19002"
// RLO: "M19002" AND "mDelayPowerOnTmr"
"gRinseONS" := "M19002" AND "mDelayPowerOnTmr";
// Network 28: CIP ONS
// RLO: "gBlenderCIPMode"
// PBox 26 - Passing bit: "M19003"
// RLO: "M19003" AND "mDelayPowerOnTmr"
"gCIPONS" := "M19003" AND "mDelayPowerOnTmr";
// Network 29: CIp Mode Init
// RLO: "gCIPONS"
IF "gCIPONS" THEN
BlenderCtrl_CIPModeInit();
END_IF;
// Network 30: Reset SPWords
BlenderCtrl_ResetSPWord();
// Network 31: Blender Run Control
BlenderRun__Control();
// Network 32: Tank Pressure Control
Prod Tank PressCtrl();
// Network 33: Balaiage
Baialage();
// Network 34: First Production
// ERROR: Call a bloque no soportado: ProcedureFirstProduction
// Network 35: CIP MAIN Calling
CIPMain();
// Network 36: Blender Rinse
BlenderRinse();
// Network 37: Safeties
Safeties();
// Network 38: Instrument Scanner
Instrument_Scanner();
// Network 39: Vacuum Control
VacuumCtrl();
// Network 40: Syrup Room Control
SyrupRoomCtrl();
// Network 41: Blend Procedure Data
// RLO: "mDelayPowerOnTmr"
IF "mDelayPowerOnTmr" THEN
// ERROR: Call a bloque no soportado: Procedure
END_IF;
// Network 42: Pneumatic Valve Control
Pneumatic Valve Ctrl();
// Network 43: Pumps Control
PumpsControl();
// Network 44: Prod Report Manager
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Report"
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Report" THEN
ProdReportManager();
END_IF;
// Network 45: Outputs
Output();
// Network 46: SLIM BLOCK
SLIM_Block();
// Network 47: Interlocking Panel 1
Interlocking_Panel_1();
// Network 48: Filler Control
FillerControl();
// Network 49: Blender Ctrl Update PWORD
BlenderCtrl_UpdatePWord();
// Network 50: ResetTotalizer
// RLO: "gBlendResetTotalizer"
// Network 51: ResetWaterTot
// RLO: "gFTN301_ResetTot"
// RLO: "mResetTotalizerTmr"
// Network 52: Water VFM Reset Totalizer
// RLO: "gFTN301_ResetTot"
// Network 53: ResetCO2Tot
// RLO: "gFTP302_ResetTot"
// RLO: "mResetTotalizerTmr"
// Network 54: Syrup MFM Reset Totalizer
// RLO: "gFTP302_ResetTot"
// Network 55: ResetProductTot
// RLO: "gFTM303_ResetTot"
// RLO: "mResetTotalizerTmr"
// Network 56: CO2 MFM Reset Tot
// RLO: "gFTM303_ResetTot"
// Network 57: ResetCO2Tot
// RLO: "gProductMFMResetTot"
// RLO: "mResetTotalizerTmr"
// Network 58: Reset Totalizer
// RLO: "gProductMFMResetTot"
// Network 59: Reset Totalizer
// RLO: "gBlendResetTotalizer"
// Network 60: Blender Ctrl Command
// RLO: "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Simulation"
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Simulation" THEN
BlenderCtrl_MFM Command();
END_IF;
// Network 61: DP Global Diag
CPU_DP Global Diag();
// Network 62: Profibus
Profibus Network();
// Network 63: Valve Fault
ModValveFault();
// Network 64: All Auto
// RLO: "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Command"
// RLO: "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Command" AND "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Enable"
// Network 65: Ctrl HMI Manual Active
BlenderCtrl_ManualActive();
// Network 66: Mod Copy Recipe
// RLO: "HMI_Variables_Cmd"."Recipe"."Main_Page"
// RLO: "HMI_Variables_Cmd"."Recipe"."Main_Page" AND "mFP_Recip_Main_Page"
"mAux_FP_M700_1" := "HMI_Variables_Cmd"."Recipe"."Main_Page" AND "mFP_Recip_Main_Page";
// RLO: "HMI_Variables_Cmd"."Recipe"."Main_Page"
// RLO: "HMI_Variables_Cmd"."Recipe"."Main_Page" AND "HMI_Variables_Cmd"."Recipe"."Edit"
// RLO: "mAux_FP_M700_1"
// Network 67: to HMI - Recipe Management
// RLO: "AUX TRUE"
IF "AUX TRUE" THEN
// ERROR: Call a bloque no soportado: RecipeManagement - Prod
END_IF;
// Network 68: Recipe Calculation
RecipeCalculation();
END_FUNCTION_BLOCK

Binary file not shown.

View File

@ -53,7 +53,9 @@
"logic": [
{
"instruction_uid": "26",
"uid": "26",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -69,7 +71,9 @@
},
{
"instruction_uid": "27",
"uid": "27",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "22",
@ -79,8 +83,8 @@
},
"in": {
"type": "connection",
"source_instruction_uid": "26",
"source_instruction_type": "Contact",
"source_instruction_uid": "26",
"source_pin": "out"
}
},
@ -88,14 +92,13 @@
},
{
"instruction_uid": "28",
"uid": "28",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gSLIM_Sec\""
},
"in2": {
"uid": "24",
"scope": "LiteralConstant",
@ -105,9 +108,15 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "27",
"source_instruction_type": "Contact",
"source_instruction_uid": "27",
"source_pin": "out"
},
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gSLIM_Sec\""
}
},
"outputs": {
@ -130,7 +139,9 @@
"logic": [
{
"instruction_uid": "24",
"uid": "24",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -146,7 +157,11 @@
},
{
"instruction_uid": "25",
"uid": "25",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "22",
@ -157,8 +172,8 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "24",
"source_instruction_type": "Contact",
"source_instruction_uid": "24",
"source_pin": "out"
}
},
@ -182,7 +197,9 @@
"logic": [
{
"instruction_uid": "26",
"uid": "26",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -198,7 +215,9 @@
},
{
"instruction_uid": "27",
"uid": "27",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "22",
@ -208,8 +227,8 @@
},
"in": {
"type": "connection",
"source_instruction_uid": "26",
"source_instruction_type": "Contact",
"source_instruction_uid": "26",
"source_pin": "out"
}
},
@ -217,14 +236,13 @@
},
{
"instruction_uid": "28",
"uid": "28",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdSec\""
},
"in2": {
"uid": "24",
"scope": "LiteralConstant",
@ -234,9 +252,15 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "27",
"source_instruction_type": "Contact",
"source_instruction_uid": "27",
"source_pin": "out"
},
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdSec\""
}
},
"outputs": {
@ -259,13 +283,14 @@
"logic": [
{
"instruction_uid": "24",
"uid": "24",
"type": "Eq",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdSec\""
"pre": {
"type": "powerrail"
},
"in2": {
"uid": "22",
@ -274,27 +299,32 @@
"datatype": "Int",
"value": 60
},
"pre": {
"type": "powerrail"
"in1": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdSec\""
}
},
"outputs": {}
},
{
"instruction_uid": "25",
"uid": "25",
"type": "Coil",
"template_values": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_uid": "24",
"source_instruction_type": "Eq",
"source_pin": "out"
},
"operand": {
"uid": "23",
"scope": "LocalVariable",
"type": "variable",
"name": "\"m1MinONS\""
},
"in": {
"type": "connection",
"source_instruction_type": "Eq",
"source_instruction_uid": "24",
"source_pin": "out"
}
},
"outputs": {}
@ -308,7 +338,9 @@
"logic": [
{
"instruction_uid": "27",
"uid": "27",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -324,7 +356,11 @@
},
{
"instruction_uid": "28",
"uid": "28",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "22",
@ -335,8 +371,8 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "27",
"source_instruction_type": "Contact",
"source_instruction_uid": "27",
"source_pin": "out"
}
},
@ -353,20 +389,31 @@
},
{
"instruction_uid": "29",
"uid": "29",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "24",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdMin\""
},
"in2": {
"uid": "25",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Int",
"value": 1
},
"in1": {
"uid": "24",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdMin\""
},
"en": {
"type": "connection",
"source_instruction_uid": "27",
"source_instruction_type": "Contact",
"source_pin": "out"
}
},
"outputs": {
@ -389,13 +436,14 @@
"logic": [
{
"instruction_uid": "24",
"uid": "24",
"type": "Eq",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdMin\""
"pre": {
"type": "powerrail"
},
"in2": {
"uid": "22",
@ -404,27 +452,32 @@
"datatype": "Int",
"value": 60
},
"pre": {
"type": "powerrail"
"in1": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdMin\""
}
},
"outputs": {}
},
{
"instruction_uid": "25",
"uid": "25",
"type": "Coil",
"template_values": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_uid": "24",
"source_instruction_type": "Eq",
"source_pin": "out"
},
"operand": {
"uid": "23",
"scope": "LocalVariable",
"type": "variable",
"name": "\"m1HourONS\""
},
"in": {
"type": "connection",
"source_instruction_type": "Eq",
"source_instruction_uid": "24",
"source_pin": "out"
}
},
"outputs": {}
@ -438,7 +491,9 @@
"logic": [
{
"instruction_uid": "30",
"uid": "30",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -454,7 +509,11 @@
},
{
"instruction_uid": "31",
"uid": "31",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "22",
@ -465,8 +524,8 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "30",
"source_instruction_type": "Contact",
"source_instruction_uid": "30",
"source_pin": "out"
}
},
@ -483,20 +542,31 @@
},
{
"instruction_uid": "32",
"uid": "32",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "24",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdHour\""
},
"in2": {
"uid": "25",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Int",
"value": 1
},
"in1": {
"uid": "24",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gProdHour\""
},
"en": {
"type": "connection",
"source_instruction_uid": "30",
"source_instruction_type": "Contact",
"source_pin": "out"
}
},
"outputs": {
@ -512,20 +582,31 @@
},
{
"instruction_uid": "33",
"uid": "33",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "27",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gBlendingMaintHour\""
},
"in2": {
"uid": "28",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Int",
"value": 1
},
"in1": {
"uid": "27",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gBlendingMaintHour\""
},
"en": {
"type": "connection",
"source_instruction_uid": "30",
"source_instruction_type": "Contact",
"source_pin": "out"
}
},
"outputs": {
@ -548,7 +629,9 @@
"logic": [
{
"instruction_uid": "29",
"uid": "29",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -564,7 +647,9 @@
},
{
"instruction_uid": "30",
"uid": "30",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "22",
@ -577,18 +662,22 @@
},
{
"instruction_uid": "31",
"uid": "31",
"type": "O",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in1": {
"type": "connection",
"source_instruction_uid": "29",
"source_instruction_type": "Contact",
"source_pin": "out"
},
"in2": {
"type": "connection",
"source_instruction_uid": "30",
"source_instruction_type": "Contact",
"source_instruction_uid": "30",
"source_pin": "out"
},
"in1": {
"type": "connection",
"source_instruction_type": "Contact",
"source_instruction_uid": "29",
"source_pin": "out"
}
},
@ -596,7 +685,11 @@
},
{
"instruction_uid": "32",
"uid": "32",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "23",
@ -607,8 +700,8 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "31",
"source_instruction_type": "O",
"source_instruction_uid": "31",
"source_pin": "out"
}
},
@ -625,7 +718,11 @@
},
{
"instruction_uid": "33",
"uid": "33",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "25",
@ -633,6 +730,12 @@
"type": "constant",
"datatype": "Int",
"value": 0
},
"en": {
"type": "connection",
"source_instruction_uid": "31",
"source_instruction_type": "O",
"source_pin": "out"
}
},
"outputs": {
@ -648,7 +751,11 @@
},
{
"instruction_uid": "34",
"uid": "34",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "27",
@ -656,6 +763,12 @@
"type": "constant",
"datatype": "Int",
"value": 0
},
"en": {
"type": "connection",
"source_instruction_uid": "31",
"source_instruction_type": "O",
"source_pin": "out"
}
},
"outputs": {
@ -678,7 +791,9 @@
"logic": [
{
"instruction_uid": "26",
"uid": "26",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -694,7 +809,9 @@
},
{
"instruction_uid": "27",
"uid": "27",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "22",
@ -704,8 +821,8 @@
},
"in": {
"type": "connection",
"source_instruction_uid": "26",
"source_instruction_type": "Contact",
"source_instruction_uid": "26",
"source_pin": "out"
}
},
@ -713,14 +830,13 @@
},
{
"instruction_uid": "28",
"uid": "28",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gRunningSeconds\""
},
"in2": {
"uid": "24",
"scope": "LiteralConstant",
@ -730,9 +846,15 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "27",
"source_instruction_type": "Contact",
"source_instruction_uid": "27",
"source_pin": "out"
},
"in1": {
"uid": "23",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gRunningSeconds\""
}
},
"outputs": {
@ -755,7 +877,12 @@
"logic": [
{
"instruction_uid": "35",
"uid": "35",
"type": "Convert",
"template_values": {
"SrcType": "Type",
"DestType": "Type"
},
"inputs": {
"in": {
"uid": "21",
@ -780,12 +907,23 @@
},
{
"instruction_uid": "36",
"uid": "36",
"type": "Mod",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"in2": {
"uid": "24",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#60"
},
"en": {
"type": "connection",
"source_instruction_uid": "35",
"source_instruction_type": "Convert",
"source_instruction_uid": "35",
"source_pin": "eno"
},
"in1": {
@ -793,13 +931,6 @@
"scope": "LocalVariable",
"type": "variable",
"name": "\"I_DIRunning_sec\""
},
"in2": {
"uid": "24",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#60"
}
},
"outputs": {
@ -823,52 +954,60 @@
},
{
"instruction_uid": "37",
"uid": "37",
"type": "Eq",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"pre": {
"type": "connection",
"source_instruction_uid": "36",
"source_instruction_type": "Mod",
"source_instruction_uid": "36",
"source_pin": "eno"
},
"in1": {
"uid": "26",
"scope": "LocalVariable",
"type": "variable",
"name": "\"MOD60\""
},
"in2": {
"uid": "27",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#0"
},
"in1": {
"uid": "26",
"scope": "LocalVariable",
"type": "variable",
"name": "\"MOD60\""
}
},
"outputs": {}
},
{
"instruction_uid": "38",
"uid": "38",
"type": "Contact",
"template_values": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_uid": "37",
"source_instruction_type": "Eq",
"source_pin": "out"
},
"operand": {
"uid": "28",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Procedure_Variables\".\"Blender_Run\".\"Running\""
},
"in": {
"type": "connection",
"source_instruction_type": "Eq",
"source_instruction_uid": "37",
"source_pin": "out"
}
},
"outputs": {}
},
{
"instruction_uid": "39",
"uid": "39",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "29",
@ -878,8 +1017,8 @@
},
"in": {
"type": "connection",
"source_instruction_uid": "38",
"source_instruction_type": "Contact",
"source_instruction_uid": "38",
"source_pin": "out"
}
},
@ -887,14 +1026,13 @@
},
{
"instruction_uid": "40",
"uid": "40",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in1": {
"uid": "30",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gRunningMinutes\""
},
"in2": {
"uid": "31",
"scope": "LiteralConstant",
@ -904,9 +1042,15 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "39",
"source_instruction_type": "Contact",
"source_instruction_uid": "39",
"source_pin": "out"
},
"in1": {
"uid": "30",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gRunningMinutes\""
}
},
"outputs": {
@ -922,7 +1066,9 @@
},
{
"instruction_uid": "41",
"uid": "41",
"type": "PBox",
"template_values": {},
"inputs": {
"bit": {
"uid": "33",
@ -935,19 +1081,21 @@
},
{
"instruction_uid": "42",
"uid": "42",
"type": "Coil",
"template_values": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_uid": "41",
"source_instruction_type": "PBox",
"source_pin": "out"
},
"operand": {
"uid": "34",
"scope": "LocalVariable",
"type": "variable",
"name": "\"mRunMin\""
},
"in": {
"type": "connection",
"source_instruction_type": "PBox",
"source_instruction_uid": "41",
"source_pin": "out"
}
},
"outputs": {}
@ -961,7 +1109,9 @@
"logic": [
{
"instruction_uid": "32",
"uid": "32",
"type": "Contact",
"template_values": {},
"inputs": {
"operand": {
"uid": "21",
@ -977,7 +1127,12 @@
},
{
"instruction_uid": "33",
"uid": "33",
"type": "Convert",
"template_values": {
"SrcType": "Type",
"DestType": "Type"
},
"inputs": {
"in": {
"uid": "22",
@ -987,8 +1142,8 @@
},
"en": {
"type": "connection",
"source_instruction_uid": "32",
"source_instruction_type": "Contact",
"source_instruction_uid": "32",
"source_pin": "out"
}
},
@ -1005,12 +1160,23 @@
},
{
"instruction_uid": "34",
"uid": "34",
"type": "Mod",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"in2": {
"uid": "25",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#60"
},
"en": {
"type": "connection",
"source_instruction_uid": "33",
"source_instruction_type": "Convert",
"source_instruction_uid": "33",
"source_pin": "eno"
},
"in1": {
@ -1018,13 +1184,6 @@
"scope": "LocalVariable",
"type": "variable",
"name": "\"I_DIRunning_min\""
},
"in2": {
"uid": "25",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#60"
}
},
"outputs": {
@ -1048,38 +1207,54 @@
},
{
"instruction_uid": "35",
"uid": "35",
"type": "Eq",
"template_values": {
"SrcType": "Type"
},
"inputs": {
"pre": {
"type": "connection",
"source_instruction_uid": "34",
"source_instruction_type": "Mod",
"source_instruction_uid": "34",
"source_pin": "eno"
},
"in1": {
"uid": "27",
"scope": "LocalVariable",
"type": "variable",
"name": "\"MOD60\""
},
"in2": {
"uid": "28",
"scope": "TypedConstant",
"type": "constant",
"datatype": "TypedConstant",
"value": "DINT#0"
},
"in1": {
"uid": "27",
"scope": "LocalVariable",
"type": "variable",
"name": "\"MOD60\""
}
},
"outputs": {}
},
{
"instruction_uid": "36",
"uid": "36",
"type": "Add",
"template_values": {
"Card": "Cardinality",
"SrcType": "Type"
},
"inputs": {
"in2": {
"uid": "30",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Int",
"value": 1
},
"en": {
"type": "connection",
"source_instruction_uid": "35",
"source_instruction_type": "Eq",
"source_instruction_uid": "35",
"source_pin": "out"
},
"in1": {
@ -1087,13 +1262,6 @@
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Blender_Variables_Pers\".\"gRunningMaintHour\""
},
"in2": {
"uid": "30",
"scope": "LiteralConstant",
"type": "constant",
"datatype": "Int",
"value": 1
}
},
"outputs": {
@ -1116,7 +1284,11 @@
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Move",
"template_values": {
"Card": "Cardinality"
},
"inputs": {
"in": {
"uid": "21",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
// Block Name (Original): BlenderRun_ProdTime
// Block Number: 2040
// Original Language: LAD
FUNCTION_BLOCK "BlenderRun_ProdTime"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR_TEMP
m1MinONS : Bool;
m1HourONS : Bool;
Buffer : Bool;
mRunMin : Bool;
mRunHr : Bool;
I_DIRunning_sec : DInt;
I_DIRunning_min : DInt;
MOD60 : DInt;
END_VAR
BEGIN
// Network 1: Seconds
IF "Procedure_Variables"."Blender_Run"."Running" AND "CLK_1.0S" THEN
"Blender_Variables_Pers"."gSLIM_Sec" := "Blender_Variables_Pers"."gSLIM_Sec" + 1;
END_IF;
// Network 2: Reset Hours
IF "SLIM_Variables"."ResetHour" THEN
"Blender_Variables_Pers"."gSLIM_Sec" := 0;
END_IF;
// Network 3: Seconds Counter
IF "gBlenderBlending" AND "CLK_1.0S" THEN
"Blender_Variables_Pers"."gProdSec" := "Blender_Variables_Pers"."gProdSec" + 1;
END_IF;
// Network 4: Minute
"m1MinONS" := "Blender_Variables_Pers"."gProdSec" = 60;
// Network 5: Minute Counter
IF "m1MinONS" THEN
"Blender_Variables_Pers"."gProdSec" := 0;
"Blender_Variables_Pers"."gProdMin" := "Blender_Variables_Pers"."gProdMin" + 1;
END_IF;
// Logic included in grouped IF (by UID 27)
// Logic included in grouped IF (by UID 27)
// Network 6: Hour
"m1HourONS" := "Blender_Variables_Pers"."gProdMin" = 60;
// Network 7: Hour Counter
IF "m1HourONS" THEN
"Blender_Variables_Pers"."gProdMin" := 0;
"Blender_Variables_Pers"."gProdHour" := "Blender_Variables_Pers"."gProdHour" + 1;
"Blender_Variables_Pers"."gBlendingMaintHour" := "Blender_Variables_Pers"."gBlendingMaintHour" + 1;
END_IF;
// Logic included in grouped IF (by UID 30)
// Logic included in grouped IF (by UID 30)
// Logic included in grouped IF (by UID 30)
// Network 8: Counter reset
IF "gBlenderCIPMode" OR "gBlenderRinseMode" THEN
"Blender_Variables_Pers"."gProdSec" := 0;
"Blender_Variables_Pers"."gProdMin" := 0;
"Blender_Variables_Pers"."gProdHour" := 0;
END_IF;
// Logic included in grouped IF (by UID 31)
// Logic included in grouped IF (by UID 31)
// Logic included in grouped IF (by UID 31)
// Network 9: Running Seconds
IF "Procedure_Variables"."Blender_Run"."Running" AND "CLK_1.0S" THEN
"Blender_Variables_Pers"."gRunningSeconds" := "Blender_Variables_Pers"."gRunningSeconds" + 1;
END_IF;
// Network 10: Running Minutes
"I_DIRunning_sec" := "Blender_Variables_Pers"."gRunningSeconds";
"MOD60" := "I_DIRunning_sec" MOD DINT#60;
IF ("MOD60" = DINT#0 AND "Procedure_Variables"."Blender_Run"."Running") AND "CLK_1.0S" THEN
"Blender_Variables_Pers"."gRunningMinutes" := "Blender_Variables_Pers"."gRunningMinutes" + 1;
END_IF;
"M19012" := (("MOD60" = DINT#0 AND "Procedure_Variables"."Blender_Run"."Running") AND "CLK_1.0S"); // Update edge memory bit
"mRunMin" := (("MOD60" = DINT#0 AND "Procedure_Variables"."Blender_Run"."Running") AND "CLK_1.0S") AND NOT "M19012";
// Network 11: Running Hours for Maintenance
IF "mRunMin" THEN
"I_DIRunning_min" := "Blender_Variables_Pers"."gRunningMinutes";
END_IF;
IF "mRunMin" THEN
"MOD60" := "I_DIRunning_min" MOD DINT#60;
END_IF;
IF "MOD60" = DINT#0 THEN
"Blender_Variables_Pers"."gRunningMaintHour" := "Blender_Variables_Pers"."gRunningMaintHour" + 1;
END_IF;
// Network 12: Running Hours for Maintenance
"HMI_Variables_Status"."System"."BlendingMaintHour" := "Blender_Variables_Pers"."gRunningMaintHour";
END_FUNCTION_BLOCK

486
TestLAD.xml Normal file
View File

@ -0,0 +1,486 @@
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V18" />
<SW.Blocks.FC ID="0">
<AttributeList>
<Interface><Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v5">
<Section Name="Input" />
<Section Name="Output" />
<Section Name="InOut" />
<Section Name="Temp" />
<Section Name="Constant" />
<Section Name="Return">
<Member Name="Ret_Val" Datatype="Void" />
</Section>
</Sections></Interface>
<MemoryLayout>Optimized</MemoryLayout>
<Name>TestLAD</Name>
<Namespace />
<Number>2</Number>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
<SetENOAutomatically>false</SetENOAutomatically>
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="2" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="4" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="5" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="6" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="7" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="8" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<SW.Blocks.CompileUnit ID="9" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Access Scope="GlobalVariable" UId="21">
<Symbol>
<Component Name="Clock_10Hz" />
</Symbol>
</Access>
<Access Scope="GlobalVariable" UId="22">
<Symbol>
<Component Name="Clock_5Hz" />
</Symbol>
</Access>
<Part Name="Contact" UId="23" />
<Part Name="Coil" UId="24" />
</Parts>
<Wires>
<Wire UId="25">
<Powerrail />
<NameCon UId="23" Name="in" />
</Wire>
<Wire UId="26">
<IdentCon UId="21" />
<NameCon UId="23" Name="operand" />
</Wire>
<Wire UId="27">
<NameCon UId="23" Name="out" />
<NameCon UId="24" Name="in" />
</Wire>
<Wire UId="28">
<IdentCon UId="22" />
<NameCon UId="24" Name="operand" />
</Wire>
</Wires>
</FlgNet></NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="A" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="B" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="C" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="D" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="E" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="F" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="10" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="11" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="12" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="13" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>Clock Bit</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="14" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="15" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="16" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="17" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="18" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="19" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<SW.Blocks.CompileUnit ID="1A" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Access Scope="GlobalVariable" UId="21">
<Symbol>
<Component Name="Clock_10Hz" />
</Symbol>
</Access>
<Access Scope="GlobalVariable" UId="22">
<Symbol>
<Component Name="Clock_5Hz" />
</Symbol>
</Access>
<Part Name="Contact" UId="23">
<Negated Name="operand" />
</Part>
<Part Name="Coil" UId="24" />
</Parts>
<Wires>
<Wire UId="25">
<Powerrail />
<NameCon UId="23" Name="in" />
</Wire>
<Wire UId="26">
<IdentCon UId="21" />
<NameCon UId="23" Name="operand" />
</Wire>
<Wire UId="27">
<NameCon UId="23" Name="out" />
<NameCon UId="24" Name="in" />
</Wire>
<Wire UId="28">
<IdentCon UId="22" />
<NameCon UId="24" Name="operand" />
</Wire>
</Wires>
</FlgNet></NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="1B" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="1C" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1D" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1E" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="1F" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="20" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="21" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="22" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="23" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="24" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text>Clock Bit</Text>
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="25" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="26" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="27" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="28" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="29" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2A" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<SW.Blocks.CompileUnit ID="2B" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource />
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="2C" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="2D" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2E" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="2F" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="30" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="31" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="32" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="33" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="34" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="35" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="36" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="37" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="38" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="39" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3A" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3B" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
<MultilingualText ID="3C" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="3D" CompositionName="Items">
<AttributeList>
<Culture>it-IT</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3E" CompositionName="Items">
<AttributeList>
<Culture>de-DE</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="3F" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="40" CompositionName="Items">
<AttributeList>
<Culture>es-ES</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="41" CompositionName="Items">
<AttributeList>
<Culture>fr-FR</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="42" CompositionName="Items">
<AttributeList>
<Culture>zh-CN</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
<MultilingualTextItem ID="43" CompositionName="Items">
<AttributeList>
<Culture>ja-JP</Culture>
<Text />
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.FC>
</Document>

121
TestLAD_simplified.json Normal file
View File

@ -0,0 +1,121 @@
{
"block_name": "TestLAD",
"block_number": 2,
"language": "LAD",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void"
}
]
},
"networks": [
{
"id": "9",
"title": "Clock Bit",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Contact",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "powerrail"
},
"operand": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_10Hz\""
}
},
"outputs": {}
},
{
"instruction_uid": "24",
"uid": "24",
"type": "Coil",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_type": "Contact",
"source_instruction_uid": "23",
"source_pin": "out"
},
"operand": {
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_5Hz\""
}
},
"outputs": {}
}
]
},
{
"id": "1A",
"title": "Clock Bit",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Contact",
"template_values": {},
"negated_pins": {
"operand": true
},
"inputs": {
"in": {
"type": "powerrail"
},
"operand": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_10Hz\""
}
},
"outputs": {}
},
{
"instruction_uid": "24",
"uid": "24",
"type": "Coil",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_type": "Contact",
"source_instruction_uid": "23",
"source_pin": "out"
},
"operand": {
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_5Hz\""
}
},
"outputs": {}
}
]
},
{
"id": "2B",
"title": "",
"comment": "",
"logic": [],
"error": "FlgNet not found"
}
]
}

View File

@ -0,0 +1,125 @@
{
"block_name": "TestLAD",
"block_number": 2,
"language": "LAD",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void"
}
]
},
"networks": [
{
"id": "9",
"title": "Clock Bit",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Contact_scl",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "powerrail"
},
"operand": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_10Hz\""
}
},
"outputs": {},
"scl": "// RLO: \"Clock_10Hz\""
},
{
"instruction_uid": "24",
"uid": "24",
"type": "Coil_scl",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_type": "Contact",
"source_instruction_uid": "23",
"source_pin": "out"
},
"operand": {
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_5Hz\""
}
},
"outputs": {},
"scl": "\"Clock_5Hz\" := \"Clock_10Hz\";"
}
]
},
{
"id": "1A",
"title": "Clock Bit",
"comment": "",
"logic": [
{
"instruction_uid": "23",
"uid": "23",
"type": "Contact_scl",
"template_values": {},
"negated_pins": {
"operand": true
},
"inputs": {
"in": {
"type": "powerrail"
},
"operand": {
"uid": "21",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_10Hz\""
}
},
"outputs": {},
"scl": "// RLO: (NOT \"Clock_10Hz\")"
},
{
"instruction_uid": "24",
"uid": "24",
"type": "Coil_scl",
"template_values": {},
"negated_pins": {},
"inputs": {
"in": {
"type": "connection",
"source_instruction_type": "Contact",
"source_instruction_uid": "23",
"source_pin": "out"
},
"operand": {
"uid": "22",
"scope": "GlobalVariable",
"type": "variable",
"name": "\"Clock_5Hz\""
}
},
"outputs": {},
"scl": "\"Clock_5Hz\" := (NOT \"Clock_10Hz\");"
}
]
},
{
"id": "2B",
"title": "",
"comment": "",
"logic": [],
"error": "FlgNet not found"
}
]
}

View File

@ -0,0 +1,35 @@
// Block Name (Original): TestLAD
// Block Number: 2
// Original Language: LAD
FUNCTION_BLOCK "TestLAD"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR_TEMP
END_VAR
BEGIN
// Network 1: Clock Bit
// RLO: "Clock_10Hz"
"Clock_5Hz" := "Clock_10Hz";
// Network 2: Clock Bit
// RLO: (NOT "Clock_10Hz")
"Clock_5Hz" := (NOT "Clock_10Hz");
// Network 3:
END_FUNCTION_BLOCK

View File

@ -0,0 +1,33 @@
// Block Name (Original): TestLAD
// Block Number: 2
// Original Language: LAD
FUNCTION_BLOCK "TestLAD"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR_IN_OUT
END_VAR
VAR_TEMP
END_VAR
BEGIN
// Network 1: Clock Bit
"Clock_5Hz" := "Clock_10Hz";
// Network 2: Clock Bit
"Clock_5Hz" := "Clock_10Hz";
// Network 3:
END_FUNCTION_BLOCK

View File

@ -1,259 +0,0 @@
import json
import os
# --- Funciones Procesadoras por Tipo de Instrucción ---
# Cada función recibe el diccionario de la instrucción del JSON.
# Por ahora, solo imprimen información.
def process_contact(instruction):
"""Procesa una instrucción 'Contact'."""
print(f" [Contact] UID: {instruction['instruction_uid']}")
operand = instruction['inputs'].get('operand', {})
print(f" - Checks: {operand.get('scope', '?')} UID: {operand.get('uid', '?')}") # Adaptar si 'unknown_access' se resuelve
in_source = instruction['inputs'].get('in', {})
if in_source.get('type') == 'powerrail':
print(" - Input: Power Rail")
elif in_source.get('type') == 'connection':
print(f" - Input: From instruction {in_source.get('source_instruction_uid', '?')} (Pin: {in_source.get('source_pin', '?')})")
else:
print(f" - Input: {in_source}")
def process_coil(instruction):
"""Procesa una instrucción 'Coil'."""
print(f" [Coil] UID: {instruction['instruction_uid']}")
operand = instruction['inputs'].get('operand', {})
print(f" - Assigns to: {operand.get('scope', '?')} UID: {operand.get('uid', '?')}")
in_source = instruction['inputs'].get('in', {})
if in_source.get('type') == 'connection':
print(f" - Condition from: instruction {in_source.get('source_instruction_uid', '?')} (Pin: {in_source.get('source_pin', '?')})")
else:
print(f" - Condition: {in_source}")
def process_add(instruction):
"""Procesa una instrucción 'Add'."""
print(f" [Add] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
print(f" - Input 1: {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2: {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail (Direct)") # Si Add pudiera conectarse directo
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en: # Si 'en' no está presente o no es conexión/powerrail (poco común en Add)
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)") # Asumir si no hay pin 'en'
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
def process_move(instruction):
"""Procesa una instrucción 'Move'."""
print(f" [Move] UID: {instruction['instruction_uid']}")
in_val = instruction['inputs'].get('in', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out1', []) # Asumiendo pin 'out1' para Move
print(f" - Input Value: {in_val.get('scope', '?')} UID: {in_val.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
def process_eq(instruction):
"""Procesa una instrucción 'Eq' (Equal)."""
print(f" [Compare EQ] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
pre = instruction['inputs'].get('pre', {}) # Condición previa (usualmente PowerRail o conexión)
print(f" - Input 1: {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2: {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if pre.get('type') == 'powerrail':
print(" - Pre-condition: Power Rail")
elif pre.get('type') == 'connection':
print(f" - Pre-condition: instruction {pre.get('source_instruction_uid', '?')} (Pin: {pre.get('source_pin', '?')})")
else:
print(f" - Pre-condition: {pre}")
# La salida 'out' de Eq usualmente va a otra instrucción (Contact, Coil, Enable pin)
# Lo veremos cuando procesemos la instrucción destino
def process_mod(instruction):
"""Procesa una instrucción 'Mod' (Modulo)."""
print(f" [Modulo] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
eno_outputs = instruction['outputs'].get('eno', []) # Mod también puede tener ENO
print(f" - Input 1 (Dividend): {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2 (Divisor): {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output (Remainder) to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
# ENO normalmente se conecta a pines 'en' o 'pre' de la siguiente instrucción
def process_convert(instruction):
"""Procesa una instrucción 'Convert'."""
print(f" [Convert] UID: {instruction['instruction_uid']}")
in_val = instruction['inputs'].get('in', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
# Podríamos extraer los tipos de datos de TemplateValue si estuvieran en el JSON
# template_vals = instruction.get('template_values', {})
print(f" - Input Value: {in_val.get('scope', '?')} UID: {in_val.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
# print(f" (Expected DestType: {template_vals.get('DestType', '?')})")
def process_or(instruction):
"""Procesa una instrucción 'O' (OR)."""
# Las instrucciones 'O' en LAD suelen representar la unión de ramas paralelas.
# Este parser simple solo muestra las entradas directas. Reconstruir la lógica OR completa requeriría más análisis.
print(f" [OR Logic] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
# Podría haber in3, in4... si Cardinality > 2
if in1.get('type') == 'connection':
print(f" - Input 1 from: instruction {in1.get('source_instruction_uid', '?')} (Pin: {in1.get('source_pin', '?')})")
else:
print(f" - Input 1: {in1}")
if in2.get('type') == 'connection':
print(f" - Input 2 from: instruction {in2.get('source_instruction_uid', '?')} (Pin: {in2.get('source_pin', '?')})")
else:
print(f" - Input 2: {in2}")
# La salida 'out' de O usualmente va a otra instrucción (Contact, Coil, Enable pin)
def process_pbox(instruction):
"""Procesa una instrucción 'PBox'."""
# PBox puede ser muchas cosas (Rising Edge, Falling Edge, Set, Reset, etc.)
# Necesitaríamos más información o convenciones para saber qué hace exactamente.
print(f" [PBox - Special?] UID: {instruction['instruction_uid']}")
inputs = instruction.get('inputs', {})
outputs = instruction.get('outputs', {})
for pin, source in inputs.items():
if source.get('type') == 'connection':
print(f" - Input Pin '{pin}' from: instruction {source.get('source_instruction_uid', '?')} (Pin: {source.get('source_pin', '?')})")
elif source.get('type') == 'powerrail':
print(f" - Input Pin '{pin}': Power Rail")
else:
print(f" - Input Pin '{pin}': {source.get('scope', '?')} UID: {source.get('uid', '?')}")
# La salida de PBox la veremos en el destino
def process_unknown(instruction):
"""Procesa una instrucción de tipo desconocido."""
print(f" [Unknown Type: {instruction.get('type', 'N/A')}] UID: {instruction['instruction_uid']}")
print(f" - Inputs: {instruction.get('inputs')}")
print(f" - Outputs: {instruction.get('outputs')}")
# --- Mapeo de Tipos a Funciones ---
instruction_handlers = {
"Contact": process_contact,
"Coil": process_coil,
"Add": process_add,
"Move": process_move,
"Eq": process_eq,
"Mod": process_mod,
"Convert": process_convert,
"O": process_or, # 'O' representa un OR lógico en FlgNet
"PBox": process_pbox, # Tipo genérico, tratar como desconocido por ahora
# Añade más tipos aquí si aparecen
}
# --- Función Principal de Procesamiento ---
def process_logic_data(data):
"""Itera sobre el JSON cargado y procesa cada instrucción."""
print("=" * 40)
print(f"Processing Block: {data.get('block_name')} ({data.get('block_number')})")
print(f"Language: {data.get('language')}")
print("-" * 40)
# Opcional: Imprimir interfaz
print("Interface:")
for section, members in data.get('interface', {}).items():
if members:
print(f" {section}:")
for member in members:
print(f" - {member['name']} ({member['datatype']})")
print("-" * 40)
# Procesar Redes
print("Networks:")
for network in data.get('networks', []):
print(f"\nNetwork ID: {network.get('id')} - Title: '{network.get('title', '')}'")
if 'error' in network:
print(f" ERROR en esta red: {network['error']}")
continue
if not network.get('logic'):
print(" (No logic instructions found in JSON for this network)")
continue
for instruction in network.get('logic', []):
instruction_type = instruction.get('type')
# Obtener el handler adecuado, o el default si no se encuentra
handler = instruction_handlers.get(instruction_type, process_unknown)
try:
handler(instruction)
except Exception as e:
print(f" ERROR procesando instrucción UID {instruction.get('instruction_uid')} (Tipo: {instruction_type}): {e}")
# Considerar imprimir más detalles del error o de la instrucción
# import traceback
# traceback.print_exc()
# --- Ejecución ---
if __name__ == "__main__":
json_file = 'BlenderRun_ProdTime_simplified.json' # El archivo generado por el script anterior
if not os.path.exists(json_file):
print(f"Error: Archivo JSON no encontrado en {json_file}")
print("Asegúrate de haber ejecutado el script de conversión XML a JSON primero.")
else:
try:
with open(json_file, 'r', encoding='utf-8') as f:
logic_data = json.load(f)
process_logic_data(logic_data)
except json.JSONDecodeError as e:
print(f"Error: El archivo JSON ({json_file}) no es válido: {e}")
except Exception as e:
print(f"Ocurrió un error inesperado al cargar o procesar el JSON: {e}")
import traceback
traceback.print_exc()

160
readme.md Normal file
View File

@ -0,0 +1,160 @@
# LAD-to-SCL Conversion Pipeline: Documentación de Referencia
## 1. Visión General
Este documento describe un pipeline de scripts de Python diseñado para convertir bloques de función o funciones (FC/FB) escritos en Ladder Logic (LAD) desde archivos XML de TIA Portal Openness a un código SCL (Structured Control Language) semánticamente equivalente.
El proceso se divide en tres etapas principales, cada una manejada por un script específico:
1. **XML a JSON Enriquecido (`x1_to_json.py`):** Parsea el XML de Openness, extrae la estructura lógica y las conexiones explícitas, e **infiere conexiones implícitas** (especialmente las habilitaciones EN) para crear un archivo JSON detallado.
2. **Procesamiento Semántico (`process.py`):** Lee el JSON enriquecido y, de forma iterativa, traduce cada instrucción LAD a su equivalente SCL, manejando dependencias, propagando el estado lógico (RLO), y agrupando lógica paralela. El SCL generado se almacena *dentro* del propio JSON.
3. **Generación de SCL Final (`generate_scl.py`):** Lee el JSON completamente procesado y ensambla el código SCL final en un archivo `.scl` formateado, incluyendo declaraciones de variables y el cuerpo del programa.
## 2. Etapas del Pipeline
### Etapa 1: XML a JSON Enriquecido (`x1_to_json.py`)
* **Propósito:** Transformar la compleja y a veces ambigua estructura XML de Openness en un formato JSON estructurado y más fácil de procesar, añadiendo información clave que está implícita en el LAD visual pero no siempre explícita en el XML.
* **Entrada:** Archivo `.xml` exportado desde TIA Portal Openness para un FC o FB.
* **Salida:** Archivo `_simplified.json`.
* **Proceso Clave:**
1. **Parseo XML:** Utiliza `lxml` para leer el archivo XML.
2. **Extracción de Metadatos:** Obtiene nombre del bloque, número, lenguaje original, comentario del bloque.
3. **Extracción de Interfaz:** Parsea las secciones `Input`, `Output`, `InOut`, `Temp`, `Constant`, `Return` para obtener la declaración de variables.
4. **Parseo de Redes (`CompileUnit`):** Itera sobre cada red lógica.
* **`parse_network`:**
* **Parseo de Componentes:**
* `Access`: Identifica variables globales/locales, constantes literales y tipadas (`parse_access`).
* `Part`: Identifica instrucciones estándar LAD como `Contact`, `Coil`, `Move`, `Add`, etc. **Importante:** Detecta pines negados (`<Negated>`) y los almacena en `negated_pins` (`parse_part`).
* `Call`: Identifica llamadas a otros FCs o FBs, extrayendo el nombre del bloque llamado, tipo (FC/FB) e información de instancia DB si aplica (`parse_call`).
* Crea `access_map` y `parts_and_calls_map` para referencia rápida.
* **Parseo de Conexiones (`Wire`):**
* Construye `wire_connections`: Un mapa `(dest_uid, dest_pin) -> [(src_uid, src_pin), ...]`.
* Construye `source_connections`: Un mapa `(src_uid, src_pin) -> [(dest_uid, dest_pin), ...]`.
* Construye `eno_outputs`: Un mapa `src_uid -> [(dest_uid, dest_pin), ...]` para conexiones que *salen* de un pin `eno`.
* **Construcción Lógica Inicial:** Crea una lista de diccionarios (`all_logic_steps`), uno por cada `Part` o `Call`, rellenando `inputs` y `outputs` **solo con las conexiones explícitas** encontradas en los `Wire`.
* **Inferencia de Conexión `EN` (¡Paso Crucial!):**
* Itera sobre los bloques funcionales (`Move`, `Add`, `Call`, etc.) que *no* tienen una entrada `en` explícita definida después del paso anterior.
* Utiliza una **heurística** (buscar hacia atrás en la lista ordenada por UID) para encontrar la fuente de RLO más probable que precede a este bloque (la salida `out` de un bloque lógico como `Contact`/`O`/`Eq` o la salida `eno` de un bloque funcional anterior).
* Si se encuentra una fuente inferida, **añade la conexión `en`** al diccionario `inputs` del bloque funcional actual. Esto enriquece el JSON con la dependencia lógica implícita.
* **Adición de Lógica ENO Interesante:** Identifica las conexiones que salen de un pin `eno` y *no* van directamente al pin `en` de la siguiente instrucción, almacenándolas en el campo `eno_logic`.
* **Ordenamiento:** Ordena las instrucciones en la lista `logic` final (generalmente por UID).
5. **Escritura JSON:** Guarda la estructura de datos completa en el archivo `_simplified.json`.
### Etapa 2: Procesamiento Semántico (`process.py`)
* **Propósito:** Traducir la lógica LAD (representada en el JSON enriquecido) a código SCL embebido, resolviendo dependencias entre instrucciones y aplicando optimizaciones de agrupación.
* **Entrada:** Archivo `_simplified.json` (generado por la Etapa 1).
* **Salida:** Archivo `_simplified_scl_processed.json`.
* **Proceso Clave:**
1. **Carga de JSON y Preparación:** Lee el JSON de entrada y reconstruye mapas de acceso por red. Inicializa el `scl_map` global (o por red).
2. **Bucle Iterativo:** Repite los siguientes pasos hasta que no se realicen cambios en un pase completo o se alcance un límite máximo de pases.
* **Fase 1: Procesadores Base:**
* Itera sobre cada instrucción en cada red.
* Si la instrucción no ha sido procesada (`_scl` o `_error`) ni agrupada (`grouped`), busca el procesador adecuado (`process_xxx`) basado en su `type`.
* **`process_xxx` (Ejecución):**
* Llama a `get_scl_representation` para obtener el SCL de sus pines de entrada, buscándolos en `scl_map` o directamente en `access_map`.
* Si alguna dependencia no está resuelta (devuelve `None`), el procesador retorna `False`.
* Si las dependencias están resueltas:
* **Generadores RLO (`Contact`, `O`, `Eq`, `PBox`):** Calculan la expresión booleana SCL resultante y la almacenan en `scl_map` bajo la clave `(network_id, instr_uid, 'out')`. También almacenan el estado `EN` en `scl_map` para `eno` si aplica. Guardan un comentario en `instruction['scl']`.
* **Bloques Funcionales (`Move`, `Add`, `Convert`, `Mod`, `Call`):**
* Obtienen la condición `EN` (explícita o inferida por el JSON enriquecido).
* Generan el código SCL *core* (la asignación o cálculo).
* Generan el SCL final, **siempre incluyendo `IF en_scl THEN ... END_IF;`** si `en_scl` no es exactamente `"TRUE"`.
* Almacenan el SCL final en `instruction['scl']`.
* Almacenan el nombre de la variable de salida (o temporal) en `scl_map` para `out`/`out1`.
* Almacenan el `en_scl` en `scl_map` para `eno`.
* **Bobinas (`Coil`):** Obtienen el RLO de entrada (`in`), obtienen el operando destino, generan la asignación `destino := rlo;` y la guardan en `instruction['scl']`.
* Marcan la instrucción como procesada añadiendo `_scl` a `instruction['type']`.
* Retornan `True`.
* Se registra si hubo algún cambio en esta fase (`made_change_in_base_pass`).
* **Fase 2: Agrupación de IFs (`process_group_ifs`):**
* Itera sobre las instrucciones *ya procesadas* (`_scl`) que son generadoras de condición (`Contact`, `O`, `Eq`, etc.).
* Obtiene la `condition_scl` de `scl_map`.
* Busca todos los bloques funcionales (`Move_scl`, `Add_scl`, etc.) cuyo pin `EN` esté conectado a la salida de esta instrucción generadora.
* Verifica si el SCL de estos consumidores *ya* contiene un `IF condition_scl THEN...`.
* Si encuentra **más de uno** con la misma condición `IF`:
* Extrae el código *core* (lo que está dentro del `IF...END_IF;`) de cada consumidor.
* Construye un **único bloque `IF condition_scl THEN ... END_IF;`** que contiene todos los cores extraídos.
* **Sobrescribe** el campo `scl` de la instrucción *generadora de condición* con este nuevo bloque `IF` agrupado.
* Marca cada instrucción consumidora con `grouped = True` y cambia su `scl` a un comentario (`GROUPED_COMMENT`) para evitar que `generate_scl.py` lo use.
* Se registra si hubo algún cambio en esta fase (`made_change_in_group_pass`).
* **Condición de Salida:** El bucle termina si `made_change_in_base_pass` y `made_change_in_group_pass` son ambos `False`.
3. **Escritura JSON:** Guarda el JSON modificado (con SCL embebido y marcas de agrupación) en el archivo `_simplified_scl_processed.json`.
### Etapa 3: Generación de SCL Final (`generate_scl.py`)
* **Propósito:** Ensamblar un archivo `.scl` completo y formateado a partir del JSON procesado.
* **Entrada:** Archivo `_simplified_scl_processed.json` (generado por la Etapa 2).
* **Salida:** Archivo `.scl`.
* **Proceso Clave:**
1. **Carga de JSON:** Lee el archivo JSON procesado.
2. **Generación de Cabecera:** Escribe el encabezado del bloque (`FUNCTION_BLOCK name`, `VERSION`, etc.).
3. **Generación de Declaraciones VAR:**
* Escribe las secciones `VAR_INPUT`, `VAR_OUTPUT`, `VAR_IN_OUT`, `VAR_TEMP`, `VAR_STAT`.
* Rellena cada sección con las variables definidas en la `interface` del JSON.
* **Detecta y declara** variables temporales (`_temp_...`) y estáticas (`stat_...`) encontradas en el SCL generado (requiere inferencia de tipo básica o usar `Variant`).
4. **Generación del Cuerpo (`BEGIN`/`END_FUNCTION_BLOCK`):**
* Itera sobre las `networks` en el JSON.
* Añade comentarios de red.
* Itera sobre la `logic` de cada red.
* Para cada `instruction`:
* **Verifica el flag `grouped`:** Si `instruction.get('grouped', False)` es `True`, **ignora** esta instrucción (su lógica ya está en otro lugar).
* Si no está agrupada, obtiene el valor del campo `scl`.
* **Limpia comentarios internos:** Opcionalmente, elimina comentarios como `// RLO:` o `// Cond:` que fueron útiles para depurar `process.py` pero no son necesarios en el SCL final (excepto quizás los de PBox/flancos).
* Indenta y añade las líneas del SCL al output.
* Añade líneas en blanco entre redes si contienen código.
5. **Escritura de Archivo:** Escribe el string SCL completo al archivo `.scl`.
## 3. Cómo Extender para Nuevas Instrucciones LAD/FBD
Añadir soporte para un nuevo tipo de instrucción (p.ej., un temporizador `TON`, un comparador `GT`, o una función matemática `SQRT`) requiere modificar los scripts `x1_to_json.py` y `process.py`.
**Pasos:**
1. **Analizar el XML:** Exporta un bloque simple que use la nueva instrucción y examina el XML de Openness. Identifica:
* Si se representa como `<Part Name="NombreInstruccion">` o `<Call Name="NombreInstruccion">`.
* Sus pines de entrada y salida (`NameCon`/`IdentCon` en los `Wire`).
* Cualquier atributo o `TemplateValue` relevante.
* Si tiene pines negados (`<Negated>`).
2. **Modificar `x1_to_json.py` (`parse_network`):**
* **Parseo:** Asegúrate de que `parse_part` o `parse_call` (o una nueva función si es muy diferente) capture correctamente la nueva instrucción y sus atributos específicos. Añade su `type` al `parts_and_calls_map`.
* **Clasificación:** Decide si la nueva instrucción es un `functional_block_type` (tiene EN/ENO implícito/explícito) o un `rlo_generator` (modifica el flujo lógico principal). Actualiza estas listas si es necesario.
* **Inferencia EN:** Si es un bloque funcional, revisa si la lógica de inferencia EN necesita ajustes para manejar este nuevo tipo correctamente (aunque si tiene un `EN` explícito en el XML, la inferencia no debería ser necesaria).
* **Salidas:** Asegúrate de que sus pines de salida relevantes (`out`, `Q`, etc.) sean considerados por otros bloques y por la lógica `eno_outputs` si tiene `ENO`.
3. **Modificar `process.py`:**
* **Crear Procesador:** Escribe una nueva función `process_nombre_instruccion(instruction, network_id, scl_map, access_map, ...)`.
* **Lógica SCL:** Implementa la traducción:
* Obtén las representaciones SCL de las entradas necesarias usando `get_scl_representation`.
* Verifica que todas las dependencias estén resueltas.
* Genera el SCL core equivalente.
* Maneja la habilitación `EN`: Obtén `en_scl`. Si `en_scl` no es `"TRUE"`, envuelve el SCL core en `IF en_scl THEN ... END_IF;`.
* Almacena el SCL final en `instruction['scl']`.
* Actualiza `instruction['type']` con el sufijo `_scl`.
* **Actualiza `scl_map`:** Añade entradas para los pines de salida (`out`, `Q`, etc.) con su representación SCL (puede ser un nombre de variable temporal o el resultado de una expresión). Añade la entrada para `eno` si el bloque lo tiene (generalmente `scl_map[key_eno] = en_scl`).
* Retorna `True`.
* **Añadir a la Lista:** Inserta la nueva función `process_nombre_instruccion` en la lista `base_processors` en el orden de prioridad correcto (generalmente, generadores de valores/condiciones antes que consumidores).
* **Agrupación:** Considera si este nuevo bloque debería ser parte de la lógica de agrupación (¿es un bloque funcional que puede ser habilitado en paralelo?). Si es así, no necesita cambios especiales aquí si se usa el enfoque de `process_group_ifs`. Si se usa el enfoque integrado, el generador de condición necesitaría saber cómo obtener el SCL core de este nuevo tipo.
4. **Modificar `generate_scl.py` (Menos Frecuente):**
* **Declaraciones:** Si la nueva instrucción introduce la necesidad de tipos específicos de variables temporales o estáticas, ajusta la lógica de detección y declaración en `generate_scl.py`.
* **Limpieza de Comentarios:** Si el nuevo procesador genera comentarios específicos que no quieres en el SCL final, ajusta el filtro en `generate_scl.py`.
5. **Probar:** Ejecuta el pipeline completo con un XML que contenga la nueva instrucción y verifica los JSON intermedios y el SCL final.
## 4. Futuras Mejoras y Consideraciones
* **Manejo de Tipos de Datos:** Implementar un seguimiento y conversión de tipos más robusto. Inferir tipos para variables temporales. Usar funciones de conversión SCL explícitas (`INT_TO_DINT`, etc.).
* **Lógica de Flancos Completa:** Implementar correctamente la lógica de variables estáticas para `P_TRIG`/`N_TRIG` (o generar instancias de FB `FP`/`FN`).
* **Soporte para FBs:** Manejar correctamente las llamadas a FBs con sus DBs de instancia y parámetros complejos.
* **Optimización SCL:** Además de agrupar IFs, implementar otras optimizaciones (eliminación de código muerto, simplificación de expresiones).
* **Estructuras LAD Complejas:** Mejorar la inferencia de EN para manejar bifurcaciones y uniones más complejas, o idealmente, rediseñar `x1_to_json.py` para un análisis topológico completo. Manejar saltos (`JMP`).
* **Manejo de Errores:** Mejorar el reporte de errores y la resiliencia ante XMLs inesperados.
* **Interfaz Gráfica/Configuración:** Crear una interfaz más amigable o archivos de configuración para gestionar el proceso.
* **Soporte para otros lenguajes (FBD/SCL):** Extender el parser para manejar otros lenguajes de origen.
## 5. Conclusión
Este pipeline proporciona una base sólida para la conversión automática de LAD a SCL. Siguiendo los pasos de extensión, se puede añadir soporte para más instrucciones y mejorar la calidad y completitud de la conversión. La clave es el enfoque iterativo y la separación de responsabilidades entre el parseo/enriquecimiento del JSON y el procesamiento semántico/generación de SCL.

View File

@ -1,461 +0,0 @@
# -*- coding: utf-8 -*-
import json
import os
from lxml import etree
import traceback
# --- Namespaces ---
ns = {
'iface': 'http://www.siemens.com/automation/Openness/SW/Interface/v5',
'flg': 'http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4'
}
# --- Helper Functions ---
# (get_multilingual_text, get_symbol_name, parse_access, parse_part sin cambios)
def get_multilingual_text(element, default_lang='en-US', fallback_lang='it-IT'):
"""
Intenta extraer texto de un elemento MultilingualText, priorizando idiomas.
Busca directamente los Text dentro de Items/AttributeList/Text bajo las Culture especificadas.
"""
if element is None:
return ""
try:
# Intenta encontrar el idioma por defecto
xpath_expr = f".//*[local-name()='MultilingualTextItem'][*[local-name()='AttributeList']/*[local-name()='Culture' and text()='{default_lang}']]" \
f"/*[local-name()='AttributeList']/*[local-name()='Text']"
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
# Si no, intenta encontrar el idioma de fallback
xpath_expr = f".//*[local-name()='MultilingualTextItem'][*[local-name()='AttributeList']/*[local-name()='Culture' and text()='{fallback_lang}']]" \
f"/*[local-name()='AttributeList']/*[local-name()='Text']"
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
# Si no, toma el primer texto que encuentre
xpath_expr = f".//*[local-name()='MultilingualTextItem']/*[local-name()='AttributeList']/*[local-name()='Text']"
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
return "" # Devuelve cadena vacía si no se encuentra nada
except Exception as e:
print(f"Advertencia: Error extrayendo MultilingualText desde {etree.tostring(element, encoding='unicode')[:100]}...: {e}")
return ""
def get_symbol_name(symbol_element):
"""
Construye el nombre completo del símbolo (variable) a partir de sus elementos Component.
Encapsula cada componente entre comillas dobles y los une con puntos.
"""
if symbol_element is None:
return None
try:
# Selecciona el atributo 'Name' de cada elemento 'Component' hijo directo del Symbol
components = symbol_element.xpath("./*[local-name()='Component']/@Name")
if components:
# Une los componentes, asegurándose de que cada uno esté entre comillas dobles
full_name = ".".join(f'"{c}"' for c in components)
return full_name
else:
return None # Indica que no se pudo formar un nombre
except Exception as e:
print(f"Advertencia: Excepción en get_symbol_name para {etree.tostring(symbol_element, encoding='unicode')[:100]}...: {e}")
return None
def parse_access(access_element):
"""
Parsea un elemento Access (acceso a operando) para obtener información
detallada sobre si es una variable (Symbol) o una constante (Constant).
Devuelve un diccionario con la información o None si hay un error crítico.
"""
if access_element is None:
return None
uid = access_element.get('UId')
scope = access_element.get('Scope')
info = {'uid': uid, 'scope': scope, 'type': 'unknown'} # Inicializa info
symbol = access_element.xpath("./*[local-name()='Symbol']")
constant = access_element.xpath("./*[local-name()='Constant']")
if symbol:
info['type'] = 'variable'
info['name'] = get_symbol_name(symbol[0])
if info['name'] is None:
info['type'] = 'error_parsing_symbol'
print(f"Error: No se pudo parsear el nombre del símbolo para Access UID={uid}")
return info
elif constant:
info['type'] = 'constant'
const_type_elem = constant[0].xpath("./*[local-name()='ConstantType']")
const_val_elem = constant[0].xpath("./*[local-name()='ConstantValue']")
info['datatype'] = const_type_elem[0].text if const_type_elem and const_type_elem[0].text is not None else 'Unknown'
value_str = const_val_elem[0].text if const_val_elem and const_val_elem[0].text is not None else None
if value_str is None:
info['type'] = 'error_parsing_constant'
info['value'] = None
print(f"Error: Constante sin valor encontrada para Access UID={uid}")
return info
if info['datatype'] == 'Unknown':
val_lower = value_str.lower()
if val_lower in ['true', 'false']: info['datatype'] = 'Bool'
elif value_str.isdigit() or (value_str.startswith('-') and value_str[1:].isdigit()): info['datatype'] = 'Int'
elif '.' in value_str:
try: float(value_str); info['datatype'] = 'Real'
except ValueError: pass
elif '#' in value_str: info['datatype'] = 'TypedConstant'
info['value'] = value_str
dtype_lower = info['datatype'].lower()
val_str_processed = value_str.split('#')[-1] if '#' in value_str else value_str
try:
if dtype_lower in ['int', 'dint', 'udint', 'sint', 'usint', 'lint', 'ulint', 'word', 'dword', 'lword', 'byte']:
info['value'] = int(val_str_processed)
elif dtype_lower == 'bool':
info['value'] = val_str_processed.lower() == 'true' or val_str_processed == '1'
elif dtype_lower in ['real', 'lreal']:
info['value'] = float(val_str_processed)
elif dtype_lower == 'typedconstant':
info['value'] = value_str
except (ValueError, TypeError) as e:
print(f"Advertencia: No se pudo convertir el valor '{val_str_processed}' a tipo {dtype_lower} para UID={uid}. Se mantiene como string. Error: {e}")
info['value'] = value_str
else:
info['type'] = 'unknown_structure'
print(f"Advertencia: Access UID={uid} no es ni Symbol ni Constant.")
return info
if info['type'] == 'variable' and info.get('name') is None:
print(f"Error Interno: parse_access terminó con tipo 'variable' pero sin nombre para UID {uid}.")
info['type'] = 'error_no_name'
return info
return info
def parse_part(part_element):
"""
Parsea un elemento Part (representa una instrucción como Add, Move, Contact, Coil)
y extrae su UID, nombre y valores de plantilla (TemplateValue).
Devuelve un diccionario con la información o None si el elemento es inválido.
"""
if part_element is None:
return None
uid = part_element.get('UId')
name = part_element.get('Name')
if not uid or not name:
print(f"Error: Part encontrado sin UID o Name: {etree.tostring(part_element, encoding='unicode')}")
return None
template_values = {}
try:
for tv in part_element.xpath("./*[local-name()='TemplateValue']"):
tv_name = tv.get('Name')
tv_type = tv.get('Type')
if tv_name and tv_type:
template_values[tv_name] = tv_type
except Exception as e:
print(f"Advertencia: Error extrayendo TemplateValues para Part UID={uid}: {e}")
return {
'uid': uid,
'name': name,
'template_values': template_values # Mantenemos esto por si acaso, aunque no se use prominentemente
}
# --- Main Parsing Logic ---
def parse_network(network_element):
"""
Parsea un elemento SW.Blocks.CompileUnit (representa una red de lógica)
y extrae su ID, título, comentario y la lógica interna simplificada en formato JSON,
incluyendo la lógica conectada a ENO si no es un simple EN->ENO.
"""
if network_element is None:
print("Error: parse_network llamado con network_element=None")
return {'id': 'ERROR', 'title': 'Invalid Network Element', 'comment': '', 'logic': [], 'error': 'Input element was None'}
network_id = network_element.get('ID')
# Extrae el título de la red
title_element = network_element.xpath(".//*[local-name()='MultilingualText'][@CompositionName='Title']")
network_title = get_multilingual_text(title_element[0]) if title_element else f"Network {network_id}"
# *** NUEVO: Extrae el comentario de la red ***
network_comment = ""
comment_title_element = network_element.xpath("./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']")
if comment_title_element:
network_comment = get_multilingual_text(comment_title_element[0])
# print(f"DEBUG: Comentario Red {network_id}: '{network_comment[:50]}...'")
flgnet_list = network_element.xpath(".//flg:FlgNet", namespaces=ns)
if not flgnet_list:
print(f"Error: No se encontró FlgNet en la red ID={network_id}")
return {'id': network_id, 'title': network_title, 'comment': network_comment, 'logic': [], 'error': 'FlgNet not found'}
flgnet = flgnet_list[0]
# 1. Parsear Access y Parts
access_map = {}
for acc in flgnet.xpath(".//flg:Access", namespaces=ns):
acc_info = parse_access(acc)
if acc_info and 'uid' in acc_info:
access_map[acc_info['uid']] = acc_info
parts_map = {}
for part in flgnet.xpath(".//flg:Part", namespaces=ns):
part_info = parse_part(part)
if part_info and 'uid' in part_info:
parts_map[part_info['uid']] = part_info
# 2. Parsear Wires y construir mapa de conexiones de entrada y *salida ENO*
wire_connections = {} # Clave=(dest_uid, dest_pin), Valor=lista de (source_uid, source_pin)
eno_outputs = {} # Clave=source_part_uid, Valor=lista de (dest_uid, dest_pin)
flg_ns_uri = ns['flg']
for wire in flgnet.xpath(".//flg:Wire", namespaces=ns):
source_uid, source_pin, dest_uid, dest_pin = None, None, None, None
children = wire.getchildren()
if len(children) < 2: continue
source_elem, dest_elem = children[0], children[1]
# Determina la fuente
if source_elem.tag == etree.QName(flg_ns_uri, 'Powerrail'): source_uid, source_pin = 'POWERRAIL', 'out'
elif source_elem.tag == etree.QName(flg_ns_uri, 'IdentCon'): source_uid, source_pin = source_elem.get('UId'), 'value'
elif source_elem.tag == etree.QName(flg_ns_uri, 'NameCon'): source_uid, source_pin = source_elem.get('UId'), source_elem.get('Name')
# Determina el destino
if dest_elem.tag == etree.QName(flg_ns_uri, 'IdentCon'): dest_uid, dest_pin = dest_elem.get('UId'), 'value'
elif dest_elem.tag == etree.QName(flg_ns_uri, 'NameCon'): dest_uid, dest_pin = dest_elem.get('UId'), dest_elem.get('Name')
# Registrar conexión de entrada normal
if dest_uid and dest_pin and source_uid is not None:
dest_key = (dest_uid, dest_pin)
source_info = (source_uid, source_pin)
if dest_key not in wire_connections: wire_connections[dest_key] = []
if source_info not in wire_connections[dest_key]: wire_connections[dest_key].append(source_info)
# *** NUEVO: Registrar conexiones que SALEN de un pin ENO ***
if source_pin == 'eno' and source_uid in parts_map:
if source_uid not in eno_outputs: eno_outputs[source_uid] = []
eno_dest_info = (dest_uid, dest_pin)
if eno_dest_info not in eno_outputs[source_uid]:
eno_outputs[source_uid].append(eno_dest_info)
# print(f"DEBUG: Red {network_id} - ENO de {source_uid} conectado a ({dest_uid}, {dest_pin})")
# 3. Construir la representación lógica principal
all_logic_steps = {}
for part_uid, part_info in parts_map.items():
instruction_repr = {'instruction_uid': part_uid, 'type': part_info['name'], 'inputs': {}, 'outputs': {}}
# Procesar Entradas (igual que antes)
for (conn_dest_uid, conn_dest_pin), sources_list in wire_connections.items():
if conn_dest_uid == part_uid:
input_sources_repr = []
for source_uid, source_pin in sources_list:
if source_uid == 'POWERRAIL': input_sources_repr.append({'type': 'powerrail'})
elif source_uid in access_map: input_sources_repr.append(access_map[source_uid])
elif source_uid in parts_map:
input_sources_repr.append({'type': 'connection', 'source_instruction_uid': source_uid,
'source_instruction_type': parts_map[source_uid]['name'], 'source_pin': source_pin})
else: input_sources_repr.append({'type': 'unknown_source', 'uid': source_uid})
if len(input_sources_repr) == 1: instruction_repr['inputs'][conn_dest_pin] = input_sources_repr[0]
elif len(input_sources_repr) > 1: instruction_repr['inputs'][conn_dest_pin] = input_sources_repr
# Procesar Salidas (igual que antes)
for (conn_dest_uid, conn_dest_pin), sources_list in wire_connections.items():
for source_uid, source_pin in sources_list:
if source_uid == part_uid and conn_dest_uid in access_map:
if source_pin not in instruction_repr['outputs']: instruction_repr['outputs'][source_pin] = []
if access_map[conn_dest_uid] not in instruction_repr['outputs'][source_pin]:
instruction_repr['outputs'][source_pin].append(access_map[conn_dest_uid])
all_logic_steps[part_uid] = instruction_repr
# *** NUEVO: Procesar y añadir lógica ENO "interesante" ***
for source_instr_uid, eno_destinations in eno_outputs.items():
if source_instr_uid not in all_logic_steps: continue # Seguridad
interesting_eno_logic = []
for dest_uid, dest_pin in eno_destinations:
# Determinar si es una conexión directa a EN de otra instrucción
is_direct_en_connection = (dest_uid in parts_map and dest_pin == 'en')
if not is_direct_en_connection:
# Si NO es directa a EN, es "interesante"
target_info = {'target_pin': dest_pin}
if dest_uid in parts_map:
target_info['target_type'] = 'instruction'
target_info['target_uid'] = dest_uid
target_info['target_name'] = parts_map[dest_uid]['name']
elif dest_uid in access_map:
# El destino es una variable o constante
target_info['target_type'] = 'operand'
target_info['target_details'] = access_map[dest_uid] # Incluye toda la info del Access
else:
target_info['target_type'] = 'unknown'
target_info['target_uid'] = dest_uid
interesting_eno_logic.append(target_info)
# print(f"DEBUG: Red {network_id} - ENO de {source_instr_uid}: Lógica interesante -> {target_info}")
# Añadir la lista de lógica ENO interesante a la instrucción fuente, si existe
if interesting_eno_logic:
all_logic_steps[source_instr_uid]['eno_logic'] = interesting_eno_logic
# 4. Ordenar y finalizar
try:
sorted_uids = sorted(all_logic_steps.keys(), key=lambda x: int(x) if x.isdigit() else float('inf'))
except ValueError:
print(f"Advertencia: UIDs no puramente numéricos en red {network_id}. Ordenando alfabéticamente.")
sorted_uids = sorted(all_logic_steps.keys())
network_logic = [all_logic_steps[uid] for uid in sorted_uids if uid in all_logic_steps]
# Devolver estructura de red con ID, título, comentario y lógica
return {'id': network_id, 'title': network_title, 'comment': network_comment, 'logic': network_logic}
def convert_xml_to_json(xml_filepath, json_filepath):
"""
Función principal que orquesta la conversión del archivo XML de Openness
a un archivo JSON simplificado que representa la estructura del bloque FC,
incluyendo comentarios y lógica ENO no trivial.
"""
print(f"Iniciando conversión de '{xml_filepath}' a '{json_filepath}'...")
if not os.path.exists(xml_filepath):
print(f"Error Crítico: Archivo XML no encontrado en '{xml_filepath}'")
return
try:
print("Paso 1: Parseando archivo XML...")
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(xml_filepath, parser)
root = tree.getroot()
print("Paso 1: Parseo XML completado.")
print("Paso 2: Buscando el bloque SW.Blocks.FC...")
fc_block_list = root.xpath("//*[local-name()='SW.Blocks.FC']")
if not fc_block_list:
print("Error Crítico: No se encontró el elemento <SW.Blocks.FC> en el archivo.")
return
fc_block = fc_block_list[0]
print(f"Paso 2: Bloque SW.Blocks.FC encontrado (ID={fc_block.get('ID')}).")
print("Paso 3: Extrayendo atributos del bloque...")
attribute_list_node = fc_block.xpath("./*[local-name()='AttributeList']")
block_name_val = "Unknown"
block_number_val = None
block_lang_val = "Unknown"
if attribute_list_node:
attr_list = attribute_list_node[0]
name_node = attr_list.xpath("./*[local-name()='Name']/text()")
if name_node: block_name_val = name_node[0].strip()
num_node = attr_list.xpath("./*[local-name()='Number']/text()")
if num_node and num_node[0].isdigit(): block_number_val = int(num_node[0])
lang_node = attr_list.xpath("./*[local-name()='ProgrammingLanguage']/text()")
if lang_node: block_lang_val = lang_node[0].strip()
print(f"Paso 3: Atributos extraídos: Nombre='{block_name_val}', Número={block_number_val}, Lenguaje='{block_lang_val}'")
else: print("Advertencia: No se encontró AttributeList para el bloque FC.")
# *** NUEVO: Extraer comentario del bloque ***
block_comment_val = ""
# El comentario del bloque suele estar en ObjectList > MultilingualText[@CompositionName='Comment']
comment_node_list = fc_block.xpath("./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']")
if comment_node_list:
block_comment_val = get_multilingual_text(comment_node_list[0])
print(f"Paso 3b: Comentario del bloque extraído: '{block_comment_val[:50]}...'")
result = {
"block_name": block_name_val,
"block_number": block_number_val,
"language": block_lang_val,
"block_comment": block_comment_val, # Añadido comentario del bloque
"interface": {},
"networks": []
}
print("Paso 4: Extrayendo la interfaz del bloque...")
interface_found = False
if attribute_list_node:
interface_node_list = attribute_list_node[0].xpath("./*[local-name()='Interface']")
if interface_node_list:
interface_node = interface_node_list[0]
interface_found = True
print("Paso 4: Nodo Interface encontrado dentro de AttributeList.")
for section in interface_node.xpath(".//iface:Section", namespaces=ns):
section_name = section.get('Name')
members = []
for member in section.xpath("./iface:Member", namespaces=ns):
member_name = member.get('Name')
member_dtype = member.get('Datatype')
if member_name and member_dtype: members.append({"name": member_name, "datatype": member_dtype})
if members: result["interface"][section_name] = members
if not result["interface"]: print("Advertencia: Nodo Interface encontrado, pero no contenía secciones iface:Section válidas.")
else: print("Advertencia: No se encontró el nodo <Interface> DENTRO de <AttributeList>.")
if not interface_found and not result["interface"]: print("Advertencia: No se pudo extraer ninguna información de la interfaz.")
print("Paso 5: Extrayendo la lógica de las redes (CompileUnits)...")
networks_processed_count = 0
object_list_node = fc_block.xpath("./*[local-name()='ObjectList']")
if object_list_node:
compile_units = object_list_node[0].xpath("./*[local-name()='SW.Blocks.CompileUnit']")
print(f"Paso 5: Se encontraron {len(compile_units)} elementos SW.Blocks.CompileUnit.")
for network_elem in compile_units:
networks_processed_count += 1
print(f"DEBUG: Procesando red #{networks_processed_count} (ID={network_elem.get('ID')})...")
parsed_network = parse_network(network_elem) # Ahora parse_network incluye comentario
if parsed_network and parsed_network.get('error') is None: result["networks"].append(parsed_network)
elif parsed_network:
print(f"Error: Falló el parseo de la red ID={parsed_network.get('id')}: {parsed_network.get('error')}")
result["networks"].append(parsed_network)
else: print(f"Error: parse_network devolvió None para un CompileUnit (ID={network_elem.get('ID')}).")
if networks_processed_count == 0: print("Advertencia: ObjectList encontrado, pero no contenía SW.Blocks.CompileUnit.")
else: print("Advertencia: No se encontró ObjectList para el bloque FC.")
print("Paso 6: Escribiendo el resultado en el archivo JSON...")
# Chequeos finales
if not result["interface"]: print("ADVERTENCIA FINAL: La sección 'interface' está vacía.")
if not result["networks"]: print("ADVERTENCIA FINAL: La sección 'networks' está vacía.")
else:
# Chequea si alguna instrucción tiene lógica ENO interesante
eno_logic_found = any(instr.get('eno_logic') for net in result.get('networks', []) if net.get('error') is None for instr in net.get('logic', []))
if eno_logic_found: print("INFO FINAL: Se detectó lógica ENO interesante en al menos una instrucción.")
else: print("INFO FINAL: No se detectó lógica ENO interesante (solo conexiones directas ENO->EN o ENO no conectado).")
try:
with open(json_filepath, 'w', encoding='utf-8') as f:
json.dump(result, f, indent=4, ensure_ascii=False)
print(f"Paso 6: Escritura completada.")
print(f"Conversión finalizada con éxito. Archivo JSON guardado en: '{json_filepath}'")
except IOError as e: print(f"Error Crítico: No se pudo escribir el archivo JSON en '{json_filepath}'. Error: {e}")
except TypeError as e: print(f"Error Crítico: Problema al serializar datos a JSON. Error: {e}")
except etree.XMLSyntaxError as e:
print(f"Error Crítico: Error de sintaxis en el archivo XML '{xml_filepath}'. Detalles: {e}")
except Exception as e:
print(f"Error Crítico: Ocurrió un error inesperado durante el procesamiento: {e}")
print("--- Traceback ---"); traceback.print_exc(); print("--- Fin Traceback ---")
# --- Punto de Entrada Principal ---
if __name__ == "__main__":
xml_file = 'BlenderRun_ProdTime.xml'
json_file = 'BlenderRun_ProdTime_simplified.json'
convert_xml_to_json(xml_file, json_file)

720
x1_to_json.py Normal file
View File

@ -0,0 +1,720 @@
# -*- coding: utf-8 -*-
import json
import os
from lxml import etree
import traceback
from collections import defaultdict
# --- Namespaces ---
ns = {
"iface": "http://www.siemens.com/automation/Openness/SW/Interface/v5",
"flg": "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4",
}
# --- Helper Functions ---
# (get_multilingual_text, get_symbol_name, parse_access, parse_part - sin cambios)
# ... (código de estas funciones aquí) ...
def get_multilingual_text(element, default_lang="en-US", fallback_lang="it-IT"):
if element is None:
return ""
try:
xpath_expr = (
f".//*[local-name()='MultilingualTextItem'][*[local-name()='AttributeList']/*[local-name()='Culture' and text()='{default_lang}']]"
f"/*[local-name()='AttributeList']/*[local-name()='Text']"
)
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
xpath_expr = (
f".//*[local-name()='MultilingualTextItem'][*[local-name()='AttributeList']/*[local-name()='Culture' and text()='{fallback_lang}']]"
f"/*[local-name()='AttributeList']/*[local-name()='Text']"
)
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
xpath_expr = f".//*[local-name()='MultilingualTextItem']/*[local-name()='AttributeList']/*[local-name()='Text']"
text_items = element.xpath(xpath_expr)
if text_items and text_items[0].text is not None:
return text_items[0].text.strip()
return ""
except Exception as e:
print(f"Advertencia: Error extrayendo MultilingualText: {e}")
return ""
def get_symbol_name(symbol_element):
if symbol_element is None:
return None
try:
components = symbol_element.xpath("./*[local-name()='Component']/@Name")
return ".".join(f'"{c}"' for c in components) if components else None
except Exception as e:
print(f"Advertencia: Excepción en get_symbol_name: {e}")
return None
def parse_access(access_element):
if access_element is None:
return None
uid = access_element.get("UId")
scope = access_element.get("Scope")
info = {"uid": uid, "scope": scope, "type": "unknown"}
symbol = access_element.xpath("./*[local-name()='Symbol']")
constant = access_element.xpath("./*[local-name()='Constant']")
if symbol:
info["type"] = "variable"
info["name"] = get_symbol_name(symbol[0])
if info["name"] is None:
info["type"] = "error_parsing_symbol"
print(f"Error: No se pudo parsear nombre símbolo Access UID={uid}")
return info
elif constant:
info["type"] = "constant"
const_type_elem = constant[0].xpath("./*[local-name()='ConstantType']")
const_val_elem = constant[0].xpath("./*[local-name()='ConstantValue']")
info["datatype"] = (
const_type_elem[0].text
if const_type_elem and const_type_elem[0].text is not None
else "Unknown"
)
value_str = (
const_val_elem[0].text
if const_val_elem and const_val_elem[0].text is not None
else None
)
if value_str is None:
info["type"] = "error_parsing_constant"
info["value"] = None
print(f"Error: Constante sin valor Access UID={uid}")
return info
if info["datatype"] == "Unknown":
val_lower = value_str.lower()
if val_lower in ["true", "false"]:
info["datatype"] = "Bool"
elif value_str.isdigit() or (
value_str.startswith("-") and value_str[1:].isdigit()
):
info["datatype"] = "Int"
elif "." in value_str:
try:
float(value_str)
info["datatype"] = "Real"
except ValueError:
pass
elif "#" in value_str:
info["datatype"] = "TypedConstant"
info["value"] = value_str
dtype_lower = info["datatype"].lower()
val_str_processed = value_str.split("#")[-1] if "#" in value_str else value_str
try:
if dtype_lower in [
"int",
"dint",
"udint",
"sint",
"usint",
"lint",
"ulint",
"word",
"dword",
"lword",
"byte",
]:
info["value"] = int(val_str_processed)
elif dtype_lower == "bool":
info["value"] = (
val_str_processed.lower() == "true" or val_str_processed == "1"
)
elif dtype_lower in ["real", "lreal"]:
info["value"] = float(val_str_processed)
elif dtype_lower == "typedconstant":
info["value"] = value_str
except (ValueError, TypeError) as e:
print(
f"Advertencia: No se pudo convertir valor '{val_str_processed}' a {dtype_lower} UID={uid}. Error: {e}"
)
info["value"] = value_str
else:
info["type"] = "unknown_structure"
print(f"Advertencia: Access UID={uid} no es Symbol ni Constant.")
return info
if info["type"] == "variable" and info.get("name") is None:
print(f"Error Interno: parse_access var sin nombre UID {uid}.")
info["type"] = "error_no_name"
return info
return info
def parse_part(part_element):
"""
Parsea un elemento Part (instrucción) y extrae UID, nombre,
valores de plantilla y pines negados.
"""
if part_element is None: return None
uid = part_element.get('UId'); name = part_element.get('Name')
if not uid or not name: print(f"Error: Part sin UID o Name: {etree.tostring(part_element, encoding='unicode')}"); return None
template_values = {}
try:
for tv in part_element.xpath("./*[local-name()='TemplateValue']"):
tv_name = tv.get('Name'); tv_type = tv.get('Type')
if tv_name and tv_type: template_values[tv_name] = tv_type
except Exception as e: print(f"Advertencia: Error extrayendo TemplateValues Part UID={uid}: {e}")
# --- INICIO NUEVA LÓGICA PARA NEGACIÓN ---
negated_pins = {} # Diccionario para pines negados: {pin_name: True}
try:
for negated_elem in part_element.xpath("./*[local-name()='Negated']"):
negated_pin_name = negated_elem.get('Name')
if negated_pin_name:
negated_pins[negated_pin_name] = True
# print(f"DEBUG: Pin negado detectado: {name} UID {uid}, Pin: {negated_pin_name}") # Debug
except Exception as e: print(f"Advertencia: Error extrayendo Negated Pins Part UID={uid}: {e}")
return {
'uid': uid,
'type': name, # Usar 'type' para consistencia
'template_values': template_values,
'negated_pins': negated_pins # Añadir diccionario de pines negados
}
# --- NUEVA FUNCIÓN parse_call ---
def parse_call(call_element):
"""
Parsea un elemento Call (llamada a FC/FB) y extrae su información.
"""
if call_element is None:
return None
uid = call_element.get("UId")
if not uid:
print(
f"Error: Call encontrado sin UID: {etree.tostring(call_element, encoding='unicode')}"
)
return None
call_info_elem = call_element.xpath("./*[local-name()='CallInfo']")
if not call_info_elem:
print(f"Error: Call UID {uid} sin elemento CallInfo.")
return None
call_info = call_info_elem[0]
block_name = call_info.get("Name")
block_type = call_info.get("BlockType") # FC, FB, etc.
instance_name = None
instance_scope = None
# Si es una llamada a FB, puede tener información de instancia
instance_elem = call_info.xpath("./*[local-name()='Instance']")
if instance_elem:
instance = instance_elem[0]
instance_scope = instance.get("Scope") # Ej: GlobalVariable, InstanceDB
# El nombre de la instancia DB suele estar en Component dentro de Symbol
symbol_elem = instance.xpath("./*[local-name()='Symbol']")
if symbol_elem:
instance_name = get_symbol_name(symbol_elem[0])
if not block_name or not block_type:
print(f"Error: CallInfo para UID {uid} sin Name o BlockType.")
return None
call_data = {
"uid": uid,
"type": "Call", # Tipo genérico para nuestra estructura JSON
"block_name": block_name,
"block_type": block_type,
}
if instance_name:
call_data["instance_db"] = instance_name
if instance_scope:
call_data["instance_scope"] = instance_scope
return call_data
# --- FIN NUEVA FUNCIÓN ---
# --- Función parse_network MODIFICADA ---
def parse_network(network_element):
"""
Parsea una red, extrae lógica (incluyendo Calls) y añade conexiones EN implícitas.
"""
if network_element is None:
return {
"id": "ERROR",
"title": "Invalid Network Element",
"comment": "",
"logic": [],
"error": "Input element was None",
}
network_id = network_element.get("ID")
title_element = network_element.xpath(
".//*[local-name()='MultilingualText'][@CompositionName='Title']"
)
network_title = (
get_multilingual_text(title_element[0])
if title_element
else f"Network {network_id}"
)
comment_title_element = network_element.xpath(
"./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']"
)
network_comment = (
get_multilingual_text(comment_title_element[0]) if comment_title_element else ""
)
flgnet_list = network_element.xpath(".//flg:FlgNet", namespaces=ns)
if not flgnet_list:
return {
"id": network_id,
"title": network_title,
"comment": network_comment,
"logic": [],
"error": "FlgNet not found",
}
flgnet = flgnet_list[0]
# 1. Parsear Access, Parts y Calls
access_map = {
acc_info["uid"]: acc_info
for acc in flgnet.xpath(".//flg:Access", namespaces=ns)
if (acc_info := parse_access(acc))
}
# --- MODIFICADO: Unir Parts y Calls en un solo mapa ---
parts_and_calls_map = {}
instruction_elements = flgnet.xpath(
".//flg:Part | .//flg:Call", namespaces=ns
) # Buscar ambos tipos
for element in instruction_elements:
parsed_info = None
if element.tag.endswith("Part"):
parsed_info = parse_part(element)
elif element.tag.endswith("Call"):
parsed_info = parse_call(element)
if parsed_info and "uid" in parsed_info:
parts_and_calls_map[parsed_info["uid"]] = parsed_info
else:
print(
f"Advertencia: Se ignoró un Part/Call inválido en la red {network_id}"
)
# --- FIN MODIFICADO ---
# 2. Parsear Wires y construir mapas de conexiones
wire_connections = defaultdict(list)
source_connections = defaultdict(list)
eno_outputs = defaultdict(list)
flg_ns_uri = ns["flg"]
for wire in flgnet.xpath(".//flg:Wire", namespaces=ns):
source_uid, source_pin, dest_uid, dest_pin = None, None, None, None
children = wire.getchildren()
if len(children) < 2:
continue
source_elem, dest_elem = children[0], children[1]
if source_elem.tag == etree.QName(flg_ns_uri, "Powerrail"):
source_uid, source_pin = "POWERRAIL", "out"
elif source_elem.tag == etree.QName(flg_ns_uri, "IdentCon"):
source_uid, source_pin = source_elem.get("UId"), "value"
elif source_elem.tag == etree.QName(flg_ns_uri, "NameCon"):
source_uid, source_pin = source_elem.get("UId"), source_elem.get("Name")
if dest_elem.tag == etree.QName(flg_ns_uri, "IdentCon"):
dest_uid, dest_pin = dest_elem.get("UId"), "value"
elif dest_elem.tag == etree.QName(flg_ns_uri, "NameCon"):
dest_uid, dest_pin = dest_elem.get("UId"), dest_elem.get("Name")
if dest_uid and dest_pin and source_uid is not None:
dest_key = (dest_uid, dest_pin)
source_key = (source_uid, source_pin)
source_info = (source_uid, source_pin)
dest_info = (dest_uid, dest_pin)
if source_info not in wire_connections[dest_key]:
wire_connections[dest_key].append(source_info)
if dest_info not in source_connections[source_key]:
source_connections[source_key].append(dest_info)
if (
source_pin == "eno" and source_uid in parts_and_calls_map
): # Usar mapa combinado
if dest_info not in eno_outputs[source_uid]:
eno_outputs[source_uid].append(dest_info)
# 3. Construir la representación lógica INICIAL
all_logic_steps = {}
functional_block_types = [
"Move",
"Add",
"Sub",
"Mul",
"Div",
"Mod",
"Convert",
"Call",
] # Añadir Call
rlo_generators = [
"Contact",
"O",
"Eq",
"Ne",
"Gt",
"Lt",
"Ge",
"Le",
"And",
"Xor",
"PBox",
]
# --- MODIFICADO: Iterar sobre el mapa combinado ---
for instruction_uid, instruction_info in parts_and_calls_map.items():
# Usar directamente la info parseada (part_info o call_info)
instruction_repr = {
"instruction_uid": instruction_uid,
**instruction_info,
} # Copiar datos base
instruction_repr["inputs"] = {}
instruction_repr["outputs"] = {}
# --- FIN MODIFICADO ---
# Rellenar inputs explícitos
# Añadir más pines si las llamadas a FB los usan (ej: parámetros FC/FB)
possible_pins = set(
["en", "in", "in1", "in2", "in3", "in4", "bit", "operand", "pre", "clk"]
)
# Añadir pines específicos de la llamada si es un FB? Más complejo.
for dest_pin_name in possible_pins:
dest_key = (instruction_uid, dest_pin_name)
if dest_key in wire_connections:
sources_list = wire_connections[dest_key]
input_sources_repr = []
for source_uid, source_pin in sources_list:
if source_uid == "POWERRAIL":
input_sources_repr.append({"type": "powerrail"})
elif source_uid in access_map:
input_sources_repr.append(access_map[source_uid])
elif source_uid in parts_and_calls_map: # Usar mapa combinado
input_sources_repr.append(
{
"type": "connection",
# Usar el tipo del mapa combinado
"source_instruction_type": parts_and_calls_map[
source_uid
]["type"],
"source_instruction_uid": source_uid,
"source_pin": source_pin,
}
)
else:
input_sources_repr.append(
{"type": "unknown_source", "uid": source_uid}
)
if len(input_sources_repr) == 1:
instruction_repr["inputs"][dest_pin_name] = input_sources_repr[0]
elif len(input_sources_repr) > 1:
instruction_repr["inputs"][dest_pin_name] = input_sources_repr
# Rellenar outputs explícitos (hacia Access)
for source_pin_name in [
"out",
"out1",
"Q",
"eno",
]: # Añadir salidas comunes de FC/FB si es necesario
source_key = (instruction_uid, source_pin_name)
if source_key in source_connections:
for dest_uid, dest_pin in source_connections[source_key]:
if dest_uid in access_map:
if source_pin_name not in instruction_repr["outputs"]:
instruction_repr["outputs"][source_pin_name] = []
if (
access_map[dest_uid]
not in instruction_repr["outputs"][source_pin_name]
):
instruction_repr["outputs"][source_pin_name].append(
access_map[dest_uid]
)
all_logic_steps[instruction_uid] = instruction_repr
# --- 4. INFERENCIA Y PROPAGACIÓN DE CONEXIONES 'EN' IMPLÍCITAS ---
# (Esta lógica probablemente necesite ajustes para considerar 'Call' como bloque funcional)
# print(f"DEBUG: Iniciando inferencia EN para Red {network_id}...")
processed_blocks_en_inference = set()
something_changed = True
inference_passes = 0
max_inference_passes = len(all_logic_steps) + 5
while something_changed and inference_passes < max_inference_passes:
something_changed = False
inference_passes += 1
try:
sorted_uids_for_pass = sorted(
all_logic_steps.keys(),
key=lambda x: int(x) if x.isdigit() else float("inf"),
)
except ValueError:
sorted_uids_for_pass = sorted(all_logic_steps.keys())
current_logic_list = [
all_logic_steps[uid]
for uid in sorted_uids_for_pass
if uid in all_logic_steps
]
for i, instruction in enumerate(
current_logic_list
): # Usar enumerate para obtener índice
part_uid = instruction["instruction_uid"]
part_type = instruction["type"] # Ahora puede ser 'Call'
# Si es un bloque funcional (incluyendo Call) sin 'en' explícito
if (
part_type in functional_block_types
and "en" not in instruction["inputs"]
and part_uid not in processed_blocks_en_inference
):
inferred_en_source = None
my_index = i # Ya tenemos el índice
if my_index > 0:
for j in range(my_index - 1, -1, -1): # Buscar hacia atrás
prev_instr = current_logic_list[j]
prev_uid = prev_instr["instruction_uid"]
prev_type = prev_instr["type"]
if prev_type in rlo_generators:
inferred_en_source = {
"type": "connection",
"source_instruction_uid": prev_uid,
"source_instruction_type": prev_type,
"source_pin": "out",
}
break
elif prev_type in functional_block_types:
source_key_eno = (prev_uid, "eno")
if source_key_eno in source_connections:
inferred_en_source = {
"type": "connection",
"source_instruction_uid": prev_uid,
"source_instruction_type": prev_type,
"source_pin": "eno",
}
break
if inferred_en_source:
# Asegurarse de que 'instruction' se refiera al diccionario original
all_logic_steps[part_uid]["inputs"]["en"] = inferred_en_source
processed_blocks_en_inference.add(part_uid)
something_changed = True
# --- 5. Añadir lógica ENO interesante ---
# (Necesita usar parts_and_calls_map ahora)
for source_instr_uid, eno_destinations in eno_outputs.items():
if source_instr_uid not in all_logic_steps:
continue
interesting_eno_logic = []
for dest_uid, dest_pin in eno_destinations:
is_direct_en_connection = (
dest_uid in parts_and_calls_map and dest_pin == "en"
) # Check en mapa combinado
if not is_direct_en_connection:
target_info = {"target_pin": dest_pin}
if dest_uid in parts_and_calls_map:
target_info.update(
{
"target_type": "instruction",
"target_uid": dest_uid,
"target_name": parts_and_calls_map[dest_uid].get(
"name", parts_and_calls_map[dest_uid].get("type")
),
}
) # Usar 'name' si existe (Part) o 'type' (Call)
elif dest_uid in access_map:
target_info.update(
{
"target_type": "operand",
"target_details": access_map[dest_uid],
}
)
else:
target_info.update(
{"target_type": "unknown", "target_uid": dest_uid}
)
interesting_eno_logic.append(target_info)
if interesting_eno_logic:
all_logic_steps[source_instr_uid]["eno_logic"] = interesting_eno_logic
# --- 6. Ordenar Lógica Final y Devolver ---
try:
sorted_uids = sorted(
all_logic_steps.keys(),
key=lambda x: int(x) if x.isdigit() else float("inf"),
)
except ValueError:
print(f"Advertencia: UIDs no numéricos red {network_id}. Orden alfabético.")
sorted_uids = sorted(all_logic_steps.keys())
network_logic = [
all_logic_steps[uid] for uid in sorted_uids if uid in all_logic_steps
]
return {
"id": network_id,
"title": network_title,
"comment": network_comment,
"logic": network_logic,
}
# --- Función Principal convert_xml_to_json (sin cambios) ---
def convert_xml_to_json(xml_filepath, json_filepath):
print(f"Iniciando conversión de '{xml_filepath}' a '{json_filepath}'...")
if not os.path.exists(xml_filepath):
print(f"Error Crítico: Archivo XML no encontrado: '{xml_filepath}'")
return
try:
print("Paso 1: Parseando archivo XML...")
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(xml_filepath, parser)
root = tree.getroot()
print("Paso 1: Parseo XML completado.")
print("Paso 2: Buscando el bloque SW.Blocks.FC...")
fc_block_list = root.xpath("//*[local-name()='SW.Blocks.FC']")
if not fc_block_list:
print("Error Crítico: No se encontró <SW.Blocks.FC>.")
return
fc_block = fc_block_list[0]
print(f"Paso 2: Bloque SW.Blocks.FC encontrado (ID={fc_block.get('ID')}).")
print("Paso 3: Extrayendo atributos del bloque...")
attribute_list_node = fc_block.xpath("./*[local-name()='AttributeList']")
block_name_val, block_number_val, block_lang_val = "Unknown", None, "Unknown"
if attribute_list_node:
attr_list = attribute_list_node[0]
name_node = attr_list.xpath("./*[local-name()='Name']/text()")
block_name_val = name_node[0].strip() if name_node else block_name_val
num_node = attr_list.xpath("./*[local-name()='Number']/text()")
block_number_val = (
int(num_node[0])
if num_node and num_node[0].isdigit()
else block_number_val
)
lang_node = attr_list.xpath(
"./*[local-name()='ProgrammingLanguage']/text()"
)
block_lang_val = lang_node[0].strip() if lang_node else block_lang_val
print(
f"Paso 3: Atributos: Nombre='{block_name_val}', Número={block_number_val}, Lenguaje='{block_lang_val}'"
)
else:
print("Advertencia: No se encontró AttributeList para el bloque FC.")
block_comment_val = ""
comment_node_list = fc_block.xpath(
"./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']"
)
if comment_node_list:
block_comment_val = get_multilingual_text(comment_node_list[0])
print(f"Paso 3b: Comentario bloque: '{block_comment_val[:50]}...'")
result = {
"block_name": block_name_val,
"block_number": block_number_val,
"language": block_lang_val,
"block_comment": block_comment_val,
"interface": {},
"networks": [],
}
print("Paso 4: Extrayendo la interfaz del bloque...")
if attribute_list_node:
interface_node_list = attribute_list_node[0].xpath(
"./*[local-name()='Interface']"
)
if interface_node_list:
interface_node = interface_node_list[0]
print("Paso 4: Nodo Interface encontrado.")
for section in interface_node.xpath(".//iface:Section", namespaces=ns):
section_name = section.get("Name")
members = []
for member in section.xpath("./iface:Member", namespaces=ns):
member_name = member.get("Name")
member_dtype = member.get("Datatype")
if member_name and member_dtype:
members.append(
{"name": member_name, "datatype": member_dtype}
)
if members:
result["interface"][section_name] = members
if not result["interface"]:
print("Advertencia: Interface sin secciones iface:Section válidas.")
else:
print(
"Advertencia: No se encontró <Interface> DENTRO de <AttributeList>."
)
if not result["interface"]:
print("Advertencia: No se pudo extraer información de la interfaz.")
print("Paso 5: Extrayendo y PROCESANDO lógica de redes (CompileUnits)...")
networks_processed_count = 0
object_list_node = fc_block.xpath("./*[local-name()='ObjectList']")
if object_list_node:
compile_units = object_list_node[0].xpath(
"./*[local-name()='SW.Blocks.CompileUnit']"
)
print(
f"Paso 5: Se encontraron {len(compile_units)} elementos SW.Blocks.CompileUnit."
)
for network_elem in compile_units:
networks_processed_count += 1
# print(f"DEBUG: Procesando red #{networks_processed_count} (ID={network_elem.get('ID')})...")
parsed_network = parse_network(
network_elem
) # Llamada a la función modificada
if parsed_network and parsed_network.get("error") is None:
result["networks"].append(parsed_network)
elif parsed_network:
print(
f"Error: Falló parseo red ID={parsed_network.get('id')}: {parsed_network.get('error')}"
)
result["networks"].append(parsed_network)
else:
print(
f"Error: parse_network devolvió None para CompileUnit (ID={network_elem.get('ID')})."
)
if networks_processed_count == 0:
print("Advertencia: ObjectList sin SW.Blocks.CompileUnit.")
else:
print("Advertencia: No se encontró ObjectList.")
print("Paso 6: Escribiendo el resultado en el archivo JSON...")
if not result["interface"]:
print("ADVERTENCIA FINAL: 'interface' está vacía.")
if not result["networks"]:
print("ADVERTENCIA FINAL: 'networks' está vacía.")
# else: # Chequeo ENO logic
# eno_logic_found = any(instr.get('eno_logic') for net in result.get('networks', []) if net.get('error') is None for instr in net.get('logic', []))
# if eno_logic_found: print("INFO FINAL: Lógica ENO interesante detectada.")
# else: print("INFO FINAL: No se detectó lógica ENO interesante.")
try:
with open(json_filepath, "w", encoding="utf-8") as f:
json.dump(result, f, indent=4, ensure_ascii=False)
print(f"Paso 6: Escritura completada.")
print(f"Conversión finalizada. JSON guardado en: '{json_filepath}'")
except IOError as e:
print(
f"Error Crítico: No se pudo escribir JSON en '{json_filepath}'. Error: {e}"
)
except TypeError as e:
print(f"Error Crítico: Problema al serializar a JSON. Error: {e}")
except etree.XMLSyntaxError as e:
print(f"Error Crítico: Sintaxis XML en '{xml_filepath}'. Detalles: {e}")
except Exception as e:
print(f"Error Crítico: Error inesperado: {e}")
print("--- Traceback ---")
traceback.print_exc()
print("--- Fin Traceback ---")
# --- Punto de Entrada Principal ---
if __name__ == "__main__":
xml_file = "BlenderCtrl__Main.xml" # CAMBIAR AL NUEVO ARCHIVO XML
json_file = xml_file.replace(
".xml", "_simplified.json"
) # Nombre de salida dinámico
convert_xml_to_json(xml_file, json_file)

1461
x2_process.py Normal file

File diff suppressed because it is too large Load Diff

208
x3_generate_scl.py Normal file
View File

@ -0,0 +1,208 @@
# -*- coding: utf-8 -*-
import json
import os
import re
# --- Helper Functions ---
def format_variable_name(name):
"""Limpia el nombre de la variable quitando comillas y espacios."""
if not name:
return "_INVALID_NAME_"
# Quita comillas dobles iniciales/finales
name = name.strip('"')
# Reemplaza comillas dobles internas y puntos por guión bajo
name = name.replace('"."', '_').replace('.', '_')
# Quita comillas restantes (si las hubiera)
name = name.replace('"', '')
# Asegurarse de que no empiece con número (aunque raro con comillas iniciales)
if name and name[0].isdigit():
name = "_" + name
return name
def generate_scl(processed_json_filepath, output_scl_filepath):
"""Genera un archivo SCL a partir del JSON procesado."""
if not os.path.exists(processed_json_filepath):
print(f"Error: Archivo JSON procesado no encontrado en '{processed_json_filepath}'")
return
print(f"Cargando JSON procesado desde: {processed_json_filepath}")
try:
with open(processed_json_filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
except Exception as e:
print(f"Error al cargar o parsear JSON: {e}")
return
# --- Extracción de Información del Bloque ---
block_name = data.get('block_name', 'UnknownBlock')
block_number = data.get('block_number')
block_lang = data.get('language', 'LAD') # Lenguaje original
block_comment = data.get('block_comment', '')
# Limpiar nombre del bloque para usarlo en SCL
scl_block_name = format_variable_name(block_name)
print(f"Generando SCL para el bloque: {scl_block_name} (Original: {block_name})")
# --- Identificación de Variables Temporales y Estáticas ---
temp_vars = set()
stat_vars = set() # Para flancos, si se implementan completamente
# Usar regex para encontrar variables _temp_... y stat_...
temp_pattern = re.compile(r'"?(_temp_[a-zA-Z0-9_]+)"?')
stat_pattern = re.compile(r'"?(stat_[a-zA-Z0-9_]+)"?')
for network in data.get('networks', []):
for instruction in network.get('logic', []):
scl_code = instruction.get('scl', '')
if scl_code:
# Buscar temporales en el código SCL generado
found_temps = temp_pattern.findall(scl_code)
for temp_name in found_temps:
temp_vars.add(temp_name) # Añadir al set (evita duplicados)
# Buscar estáticas (para flancos)
found_stats = stat_pattern.findall(scl_code)
for stat_name in found_stats:
stat_vars.add(stat_name)
print(f"Variables temporales detectadas: {len(temp_vars)}")
# print(f"Variables estáticas detectadas (para flancos): {len(stat_vars)}")
# --- Construcción del String SCL ---
scl_output = []
# Cabecera del Bloque
scl_output.append(f"// Block Name (Original): {block_name}")
if block_number:
scl_output.append(f"// Block Number: {block_number}")
scl_output.append(f"// Original Language: {block_lang}")
if block_comment:
scl_output.append(f"// Block Comment: {block_comment}")
scl_output.append("")
scl_output.append(f"FUNCTION_BLOCK \"{scl_block_name}\"") # Asumir FB por variables Temp/Stat
scl_output.append("{ S7_Optimized_Access := 'TRUE' }") # Opcional, común
scl_output.append("VERSION : 0.1") # Opcional
scl_output.append("")
# Declaraciones de Interfaz
scl_output.append("VAR_INPUT")
# Iterar sobre data['interface']['Input'] si existe
# for var in data.get('interface', {}).get('Input', []):
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
scl_output.append("END_VAR")
scl_output.append("")
scl_output.append("VAR_OUTPUT")
# Iterar sobre data['interface']['Output'] si existe
# for var in data.get('interface', {}).get('Output', []):
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
scl_output.append("END_VAR")
scl_output.append("")
scl_output.append("VAR_IN_OUT")
# Iterar sobre data['interface']['InOut'] si existe
# for var in data.get('interface', {}).get('InOut', []):
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
scl_output.append("END_VAR")
scl_output.append("")
# Declaraciones Estáticas (para flancos)
if stat_vars:
scl_output.append("VAR_STAT")
# Asumir Bool para flancos, se podría inferir mejor si PBox lo indicara
for var_name in sorted(list(stat_vars)):
scl_output.append(f" \"{var_name}\" : Bool; // Memory for edge detection")
scl_output.append("END_VAR")
scl_output.append("")
# Declaraciones Temporales
# Incluir las variables de la sección Temp del JSON original
# y las generadas automáticamente (_temp_...)
scl_output.append("VAR_TEMP")
declared_temps = set()
interface_temps = data.get('interface', {}).get('Temp', [])
if interface_temps:
for var in interface_temps:
formatted_name = format_variable_name(var['name'])
# Añadir comillas si el nombre original las tenía o si contiene caracteres especiales
scl_name = f'"{formatted_name}"' if '"' in var['name'] or '.' in var['name'] else formatted_name
scl_output.append(f" {scl_name} : {var['datatype']};")
declared_temps.add(scl_name) # Marcar como declarada
# Declarar las _temp_ generadas si no estaban ya en la interfaz Temp
if temp_vars:
# Intentar inferir tipo (difícil sin más info), por ahora usar Variant o Bool/DInt
for var_name in sorted(list(temp_vars)):
scl_name = f'"{var_name}"' # Asegurar comillas para _temp_
if scl_name not in declared_temps:
# Inferencia básica de tipo por nombre de pin (muy heurístico)
inferred_type = "Variant" # Tipo seguro por defecto
if var_name.endswith("_out"): # Salida de bloque lógico/comparación?
inferred_type = "Bool"
elif var_name.endswith("_out_num"): # Salida de bloque numérico?
inferred_type = "DInt" # O Real? O Int? Difícil saber
# Se podría mejorar si los procesadores añadieran info de tipo
scl_output.append(f" {scl_name} : {inferred_type}; // Auto-generated temporary")
declared_temps.add(scl_name) # Marcar como declarada
scl_output.append("END_VAR")
scl_output.append("")
# Cuerpo del Bloque
scl_output.append("BEGIN")
scl_output.append("")
# Iterar por redes y lógica
for i, network in enumerate(data.get('networks', [])):
network_title = network.get('title', f'Network {network.get("id")}')
network_comment = network.get('comment', '')
scl_output.append(f" // Network {i+1}: {network_title}")
if network_comment:
# Añadir comentario de red indentado
for line in network_comment.splitlines():
scl_output.append(f" // {line}")
scl_output.append("") # Línea en blanco antes del código de la red
network_has_code = False
for instruction in network.get('logic', []):
scl_code = instruction.get('scl')
if scl_code:
network_has_code = True
# Indentar y añadir el código SCL de la instrucción
# Quitar comentarios "// RLO updated..." o "// Comparison..."
# ya que el código SCL resultante es la representación final.
# Mantener comentarios de PBox por claridad.
is_rlo_update_comment = scl_code.strip().startswith("// RLO updated")
is_comparison_comment = scl_code.strip().startswith("// Comparison")
is_logic_o_comment = scl_code.strip().startswith("// Logic O")
if not (is_rlo_update_comment or is_comparison_comment or is_logic_o_comment) or "PBox" in scl_code :
for line in scl_code.splitlines():
scl_output.append(f" {line}") # Indentación de 2 espacios
if network_has_code:
scl_output.append("") # Añadir línea en blanco después del código de la red
scl_output.append("END_FUNCTION_BLOCK")
# --- Escritura del Archivo SCL ---
print(f"Escribiendo archivo SCL en: {output_scl_filepath}")
try:
with open(output_scl_filepath, 'w', encoding='utf-8') as f:
for line in scl_output:
f.write(line + '\n')
print("Generación de SCL completada.")
except Exception as e:
print(f"Error al escribir el archivo SCL: {e}")
# --- Ejecución ---
if __name__ == "__main__":
xml_file = "BlenderCtrl__Main.xml" # CAMBIAR AL NUEVO ARCHIVO XML
input_json_file = xml_file.replace(
".xml", "_simplified_processed.json"
) # Nombre de salida dinámico
output_scl_file = input_json_file.replace(
".json", ".scl"
) # Nombre de salida dinámico
generate_scl(input_json_file, output_scl_file)