diff --git a/BlenderCtrl_ProdModeInit_simplified.json b/BlenderCtrl_ProdModeInit_simplified.json
new file mode 100644
index 0000000..93e8516
--- /dev/null
+++ b/BlenderCtrl_ProdModeInit_simplified.json
@@ -0,0 +1,97 @@
+{
+ "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": {}
+ }
+ ],
+ "language": "LAD"
+ },
+ {
+ "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": {}
+ }
+ ],
+ "language": "LAD"
+ },
+ {
+ "id": "2B",
+ "title": "RunOut Counter",
+ "comment": "",
+ "logic": [
+ {
+ "instruction_uid": "23",
+ "uid": "23",
+ "type": "Move",
+ "template_values": {
+ "Card": "Cardinality"
+ },
+ "negated_pins": {},
+ "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\""
+ }
+ ]
+ }
+ }
+ ],
+ "language": "LAD"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/BlenderCtrl_ProdModeInit_simplified_processed.json b/BlenderCtrl_ProdModeInit_simplified_processed.json
new file mode 100644
index 0000000..344db25
--- /dev/null
+++ b/BlenderCtrl_ProdModeInit_simplified_processed.json
@@ -0,0 +1,100 @@
+{
+ "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_sympy_processed",
+ "block_name": "BlenderPID_PIDResInteg",
+ "block_type": "FC",
+ "inputs": {
+ "en": {
+ "type": "powerrail"
+ }
+ },
+ "outputs": {},
+ "scl": "BlenderPID_PIDResInteg();"
+ }
+ ],
+ "language": "LAD"
+ },
+ {
+ "id": "1A",
+ "title": "Ctrl Init Errors",
+ "comment": "",
+ "logic": [
+ {
+ "instruction_uid": "21",
+ "uid": "21",
+ "type": "Call_FC_sympy_processed",
+ "block_name": "BlenderCtrl_InitErrors",
+ "block_type": "FC",
+ "inputs": {
+ "en": {
+ "type": "powerrail"
+ }
+ },
+ "outputs": {},
+ "scl": "BlenderCtrl_InitErrors();"
+ }
+ ],
+ "language": "LAD"
+ },
+ {
+ "id": "2B",
+ "title": "RunOut Counter",
+ "comment": "",
+ "logic": [
+ {
+ "instruction_uid": "23",
+ "uid": "23",
+ "type": "Move_sympy_processed",
+ "template_values": {
+ "Card": "Cardinality"
+ },
+ "negated_pins": {},
+ "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;"
+ }
+ ],
+ "language": "LAD"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/BlenderCtrl_ProdModeInit_simplified_processed.scl b/BlenderCtrl_ProdModeInit_simplified_processed.scl
new file mode 100644
index 0000000..7f1a373
--- /dev/null
+++ b/BlenderCtrl_ProdModeInit_simplified_processed.scl
@@ -0,0 +1,30 @@
+// Block Name (Original): BlenderCtrl_ProdModeInit
+// Block Number: 2012
+// Original Language: LAD
+
+FUNCTION_BLOCK "BlenderCtrl_ProdModeInit"
+{ S7_Optimized_Access := 'TRUE' }
+VERSION : 0.1
+
+VAR_RETURN
+ Ret_Val : Void;
+END_VAR
+
+VAR_TEMP
+END_VAR
+
+BEGIN
+
+ // Network 1: PID Reset Integral (Original Language: LAD)
+
+ BlenderPID_PIDResInteg();
+
+ // Network 2: Ctrl Init Errors (Original Language: LAD)
+
+ BlenderCtrl_InitErrors();
+
+ // Network 3: RunOut Counter (Original Language: LAD)
+
+ "HMI_Variables_Status"."Analog_Values"."TP301RunOutCount" := 0.0;
+
+END_FUNCTION_BLOCK
diff --git a/TestLAD.xml b/TestLAD.xml
index 3fe7fd2..0f704be 100644
--- a/TestLAD.xml
+++ b/TestLAD.xml
@@ -8,17 +8,18 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
Optimized
@@ -77,162 +78,776 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 60
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ 60
+
+
+
+
+
+
+
+ 1000
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+ SCL
@@ -286,7 +901,7 @@
it-IT
- Manual Syrup Drain Valve Open - Operator Alarm
+
@@ -329,1412 +944,45 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#1S
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
+
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- Manual Syrup Drain Valve Open - Operator Alarm
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#2S
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- ResetTotalizer
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#2S
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- ResetWaterTot
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#2S
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- ResetCO2Tot
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#2S
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- ResetProductTot
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#2S
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- ResetCO2Tot
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- S5T#500ms
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAD
-
-
-
-
-
-
- it-IT
-
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
- it-IT
- Mod Copy Recipe
-
-
-
-
- de-DE
-
-
-
-
-
- en-US
-
-
-
-
-
- es-ES
-
-
-
-
-
- fr-FR
-
-
-
-
-
- zh-CN
-
-
-
-
-
- ja-JP
-
-
-
-
-
-
-
-
-
-
+
it-IT
-
+
de-DE
-
+
en-US
-
+
es-ES
-
+
fr-FR
-
+
zh-CN
-
+
ja-JP
diff --git a/TestLAD_simplified.json b/TestLAD_simplified.json
index 41de9f8..2ef1ac5 100644
--- a/TestLAD_simplified.json
+++ b/TestLAD_simplified.json
@@ -6,1278 +6,58 @@
"interface": {
"Temp": [
{
- "name": "All_Auto_RETVAL",
- "datatype": "Int"
+ "name": "mWaterMaxFlow",
+ "datatype": "Real"
},
{
- "name": "Reset_SP_Word_RETVAL",
- "datatype": "Int"
+ "name": "mWaterMinFlow",
+ "datatype": "Real"
},
{
- "name": "mResetWaterTot",
- "datatype": "Bool"
+ "name": "mSyrupMaxFlow",
+ "datatype": "Real"
},
{
- "name": "mResetSyrupTot",
- "datatype": "Bool"
+ "name": "mSyrupMinFlow",
+ "datatype": "Real"
},
{
- "name": "mResetCO2Tot",
- "datatype": "Bool"
+ "name": "mMinRatio",
+ "datatype": "Real"
},
{
- "name": "mResetProductTot",
- "datatype": "Bool"
+ "name": "mMaxRatio",
+ "datatype": "Real"
},
{
- "name": "Block_Move_Err",
- "datatype": "Int"
+ "name": "mBevBrixMax",
+ "datatype": "Real"
+ },
+ {
+ "name": "mBevBrixMin",
+ "datatype": "Real"
}
],
"Return": [
{
"name": "Ret_Val",
- "datatype": "Void"
+ "datatype": "Real"
}
]
},
"networks": [
{
"id": "9",
- "title": "Manual Syrup Drain Valve Open - Operator Alarm",
+ "title": "",
"comment": "",
+ "language": "SCL",
"logic": [
{
- "instruction_uid": "30",
- "uid": "30",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gSyrupRoomEn\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "31",
- "uid": "31",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_HVP301_Aux\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "30",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "32",
- "uid": "32",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "33",
- "uid": "33",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"FTP302Line_Preparation\".\"Done\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "32",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "34",
- "uid": "34",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"Syr_RunOut\".\"Done\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "35",
- "uid": "35",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gBlenderCIPMode\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "36",
- "uid": "36",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "27",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_CIP_CIPRunning\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "35",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "37",
- "uid": "37",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "28",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"Blender_Run\".\"Running\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "36",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "38",
- "uid": "38",
- "type": "O",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "34",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "37",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "39",
- "uid": "39",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "29",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gHVP301_Open\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "38",
- "source_pin": "out"
- }
- },
- "outputs": {}
+ "instruction_uid": "SCL_9",
+ "type": "RAW_SCL_CHUNK",
+ "scl": "IF \"Blender_Variables\".gSP_H2O <> 0 THEN\n \"Blender_Variables\".gWaterVFMCalcError := \"Blender_Variables\".gWaterVFMMeasError / 100 * \"Blender_Variables\".gSP_H2O;\nEND_IF;\nIF \"Blender_Variables\".gSP_SYR <> 0 THEN\n \"Blender_Variables\".gSyrupMFMCalcError := (\"Blender_Variables\".gSyrupMFMMeasError / 100 + (\"Blender_Variables\".gSyrupMFMZeroStab / (\"Blender_Variables\".gSP_SYR * 60)) / 100) * \"Blender_Variables\".gSP_SYR;\nEND_IF;\nIF \"Blender_Variables\".gSP_CO2 <> 0 THEN\n \"Blender_Variables\".gCO2MFMCalcError := (\"Blender_Variables\".gCO2MFMMeasError / 100 + (\"Blender_Variables\".gCO2MFMZeroStab / (\"Blender_Variables\".gSP_CO2 * 60 / 1000)) / 100) * \"Blender_Variables\".gSP_CO2;\nEND_IF;\n\"mWaterMaxFlow\" := \"Blender_Variables\".gSP_H2O + \"Blender_Variables\".gWaterVFMCalcError;\n\"mWaterMinFlow\" := \"Blender_Variables\".gSP_H2O - \"Blender_Variables\".gWaterVFMCalcError;\nIF \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN\n \"mSyrupMaxFlow\" := (\"Blender_Variables\".gSP_SYR + \"Blender_Variables\".gSyrupMFMCalcError) / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity;\n \"mSyrupMinFlow\" := (\"Blender_Variables\".gSP_SYR - \"Blender_Variables\".gSyrupMFMCalcError) / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity;\nEND_IF;\nIF \"mSyrupMaxFlow\" <> 0 THEN\n \"mMinRatio\" := \"mWaterMinFlow\" / \"mSyrupMaxFlow\";\nEND_IF;\nIF \"mSyrupMinFlow\" <> 0 THEN\n \"mMaxRatio\" := \"mWaterMaxFlow\" / \"mSyrupMinFlow\";\nEND_IF;\nIF \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN\n \"mBevBrixMax\" := \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupBrix / ((\"mMinRatio\" / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity) + 1);\n \"mBevBrixMin\" := \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupBrix / ((\"mMaxRatio\" / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity) + 1);\nEND_IF;\n\"Blender_Variables\".gBlenderBlendMaxError := \"mBevBrixMax\" - \"mBevBrixMin\";\n\"TestLAD\" := \"Blender_Variables\".gBlenderBlendMaxError;"
}
- ],
- "language": "LAD"
- },
- {
- "id": "1A",
- "title": "Manual Syrup Drain Valve Open - Operator Alarm",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "25",
- "uid": "25",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_HVM302_Aux\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Sd",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "25",
- "source_pin": "out"
- },
- "timer": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mHVM302_Dly\""
- },
- "tv": {
- "uid": "23",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#1S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "25",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gHVM302_Open\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Sd",
- "source_instruction_uid": "26",
- "source_pin": "q"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "2B",
- "title": "ResetTotalizer",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "24",
- "uid": "24",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gBlendResetTotalizer\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "25",
- "uid": "25",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "24",
- "source_pin": "out"
- },
- "timer": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "tv": {
- "uid": "23",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "24",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "3C",
- "title": "ResetWaterTot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTN301_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTN301TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetWaterTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "4D",
- "title": "ResetCO2Tot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTP302_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "O",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "28",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "29",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTP302TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "29",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "31",
- "uid": "31",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gSyrupRoomEn\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "30",
- "source_pin": "q"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "32",
- "uid": "32",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetSyrupTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "5E",
- "title": "ResetProductTot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTM303_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTM303TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetCO2Tot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "6F",
- "title": "ResetCO2Tot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gProductMFMResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetProductTotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetProductTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
- },
- {
- "id": "80",
- "title": "Mod Copy Recipe",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "33",
- "uid": "33",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "34",
- "uid": "34",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mFP_Recip_Main_Page\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "35",
- "uid": "35",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mAux_FP_M700_1\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "34",
- "source_pin": "out"
- }
- },
- "outputs": {}
- },
- {
- "instruction_uid": "36",
- "uid": "36",
- "type": "Coil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mFP_Recip_Main_Page\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {}
- },
- {
- "instruction_uid": "37",
- "uid": "37",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "38",
- "uid": "38",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "37",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "39",
- "uid": "39",
- "type": "Se",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "tv": {
- "uid": "28",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#500ms"
- },
- "timer": {
- "uid": "27",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"T_Pulse_Recipe_Edit\""
- },
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "38",
- "source_pin": "out"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "38",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- }
- },
- {
- "instruction_uid": "40",
- "uid": "40",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "29",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"T_Pulse_Recipe_Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "SdCoil",
- "source_instruction_uid": "39",
- "source_pin": "q"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "41",
- "uid": "41",
- "type": "RCoil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "30",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "40",
- "source_pin": "out"
- }
- },
- "outputs": {}
- },
- {
- "instruction_uid": "42",
- "uid": "42",
- "type": "Contact",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "31",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mAux_FP_M700_1\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- }
- },
- {
- "instruction_uid": "43",
- "uid": "43",
- "type": "SCoil",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "32",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "42",
- "source_pin": "out"
- }
- },
- "outputs": {}
- }
- ],
- "language": "LAD"
+ ]
}
]
}
\ No newline at end of file
diff --git a/TestLAD_simplified_processed.json b/TestLAD_simplified_processed.json
index 449f7ff..2ef1ac5 100644
--- a/TestLAD_simplified_processed.json
+++ b/TestLAD_simplified_processed.json
@@ -6,1325 +6,58 @@
"interface": {
"Temp": [
{
- "name": "All_Auto_RETVAL",
- "datatype": "Int"
+ "name": "mWaterMaxFlow",
+ "datatype": "Real"
},
{
- "name": "Reset_SP_Word_RETVAL",
- "datatype": "Int"
+ "name": "mWaterMinFlow",
+ "datatype": "Real"
},
{
- "name": "mResetWaterTot",
- "datatype": "Bool"
+ "name": "mSyrupMaxFlow",
+ "datatype": "Real"
},
{
- "name": "mResetSyrupTot",
- "datatype": "Bool"
+ "name": "mSyrupMinFlow",
+ "datatype": "Real"
},
{
- "name": "mResetCO2Tot",
- "datatype": "Bool"
+ "name": "mMinRatio",
+ "datatype": "Real"
},
{
- "name": "mResetProductTot",
- "datatype": "Bool"
+ "name": "mMaxRatio",
+ "datatype": "Real"
},
{
- "name": "Block_Move_Err",
- "datatype": "Int"
+ "name": "mBevBrixMax",
+ "datatype": "Real"
+ },
+ {
+ "name": "mBevBrixMin",
+ "datatype": "Real"
}
],
"Return": [
{
"name": "Ret_Val",
- "datatype": "Void"
+ "datatype": "Real"
}
]
},
"networks": [
{
"id": "9",
- "title": "Manual Syrup Drain Valve Open - Operator Alarm",
+ "title": "",
"comment": "",
+ "language": "SCL",
"logic": [
{
- "instruction_uid": "30",
- "uid": "30",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gSyrupRoomEn\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_"
- },
- {
- "instruction_uid": "31",
- "uid": "31",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_HVP301_Aux\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "30",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & ~v1_"
- },
- {
- "instruction_uid": "32",
- "uid": "32",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & ~v1_ & ~v2_"
- },
- {
- "instruction_uid": "33",
- "uid": "33",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"FTP302Line_Preparation\".\"Done\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "32",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v3_ & ~v1_ & ~v2_"
- },
- {
- "instruction_uid": "34",
- "uid": "34",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"Syr_RunOut\".\"Done\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v3_ & ~v1_ & ~v2_ & ~v4_"
- },
- {
- "instruction_uid": "35",
- "uid": "35",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gBlenderCIPMode\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v5_ & ~v1_"
- },
- {
- "instruction_uid": "36",
- "uid": "36",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "27",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_CIP_CIPRunning\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "35",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v5_ & v6_ & ~v1_"
- },
- {
- "instruction_uid": "37",
- "uid": "37",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "28",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"Procedure_Variables\".\"Blender_Run\".\"Running\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "36",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v5_ & v6_ & v7_ & ~v1_"
- },
- {
- "instruction_uid": "38",
- "uid": "38",
- "type": "O_sympy_processed",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "34",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "37",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy O: (v0_ & v5_ & v6_ & v7_ & ~v1_) | (v0_ & v3_ & ~v1_ & ~v2_ & ~v4_)"
- },
- {
- "instruction_uid": "39",
- "uid": "39",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "29",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gHVP301_Open\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "38",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "\"gHVP301_Open\" := (\"gSyrupRoomEn\" AND \"gBlenderCIPMode\" AND \"gIN_CIP_CIPRunning\" AND \"Procedure_Variables\".\"Blender_Run\".\"Running\" AND NOT \"gIN_HVP301_Aux\") OR (\"gSyrupRoomEn\" AND \"Procedure_Variables\".\"FTP302Line_Preparation\".\"Done\" AND NOT \"gIN_HVP301_Aux\" AND NOT \"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\" AND NOT \"Procedure_Variables\".\"Syr_RunOut\".\"Done\");"
+ "instruction_uid": "SCL_9",
+ "type": "RAW_SCL_CHUNK",
+ "scl": "IF \"Blender_Variables\".gSP_H2O <> 0 THEN\n \"Blender_Variables\".gWaterVFMCalcError := \"Blender_Variables\".gWaterVFMMeasError / 100 * \"Blender_Variables\".gSP_H2O;\nEND_IF;\nIF \"Blender_Variables\".gSP_SYR <> 0 THEN\n \"Blender_Variables\".gSyrupMFMCalcError := (\"Blender_Variables\".gSyrupMFMMeasError / 100 + (\"Blender_Variables\".gSyrupMFMZeroStab / (\"Blender_Variables\".gSP_SYR * 60)) / 100) * \"Blender_Variables\".gSP_SYR;\nEND_IF;\nIF \"Blender_Variables\".gSP_CO2 <> 0 THEN\n \"Blender_Variables\".gCO2MFMCalcError := (\"Blender_Variables\".gCO2MFMMeasError / 100 + (\"Blender_Variables\".gCO2MFMZeroStab / (\"Blender_Variables\".gSP_CO2 * 60 / 1000)) / 100) * \"Blender_Variables\".gSP_CO2;\nEND_IF;\n\"mWaterMaxFlow\" := \"Blender_Variables\".gSP_H2O + \"Blender_Variables\".gWaterVFMCalcError;\n\"mWaterMinFlow\" := \"Blender_Variables\".gSP_H2O - \"Blender_Variables\".gWaterVFMCalcError;\nIF \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN\n \"mSyrupMaxFlow\" := (\"Blender_Variables\".gSP_SYR + \"Blender_Variables\".gSyrupMFMCalcError) / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity;\n \"mSyrupMinFlow\" := (\"Blender_Variables\".gSP_SYR - \"Blender_Variables\".gSyrupMFMCalcError) / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity;\nEND_IF;\nIF \"mSyrupMaxFlow\" <> 0 THEN\n \"mMinRatio\" := \"mWaterMinFlow\" / \"mSyrupMaxFlow\";\nEND_IF;\nIF \"mSyrupMinFlow\" <> 0 THEN\n \"mMaxRatio\" := \"mWaterMaxFlow\" / \"mSyrupMinFlow\";\nEND_IF;\nIF \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN\n \"mBevBrixMax\" := \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupBrix / ((\"mMinRatio\" / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity) + 1);\n \"mBevBrixMin\" := \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupBrix / ((\"mMaxRatio\" / \"HMI_Blender_Parameters\".Actual_Recipe_Parameters._SyrupDensity) + 1);\nEND_IF;\n\"Blender_Variables\".gBlenderBlendMaxError := \"mBevBrixMax\" - \"mBevBrixMin\";\n\"TestLAD\" := \"Blender_Variables\".gBlenderBlendMaxError;"
}
- ],
- "language": "LAD"
- },
- {
- "id": "1A",
- "title": "Manual Syrup Drain Valve Open - Operator Alarm",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "25",
- "uid": "25",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gIN_HVM302_Aux\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v8_"
- },
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Sd_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "25",
- "source_pin": "out"
- },
- "timer": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mHVM302_Dly\""
- },
- "tv": {
- "uid": "23",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#1S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "25",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"mHVM302_Dly\"(IN := \"gIN_HVM302_Aux\", PT := S5T#1S); // TODO: Declarar \"mHVM302_Dly\" : TON;"
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gHVM302_Open\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Sd",
- "source_instruction_uid": "26",
- "source_pin": "q"
- }
- },
- "outputs": {},
- "scl": "\"gHVM302_Open\" := \"mHVM302_Dly\".Q;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "2B",
- "title": "ResetTotalizer",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "24",
- "uid": "24",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gBlendResetTotalizer\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v9_"
- },
- {
- "instruction_uid": "25",
- "uid": "25",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "24",
- "source_pin": "out"
- },
- "timer": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "tv": {
- "uid": "23",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "24",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "\"mResetTotalizerTmr\"(IN := \"gBlendResetTotalizer\", PT := S5T#2S); // TODO: Declarar \"mResetTotalizerTmr\" : TP;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "3C",
- "title": "ResetWaterTot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTN301_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v10_"
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v11_"
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O_sympy_processed",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy O: v10_ | v11_"
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTN301TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"mResetFTN301TotTmr\"(IN := \"gFTN301_ResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetFTN301TotTmr\" : TP;"
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetWaterTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {},
- "scl": "\"mResetWaterTot\" := \"mResetFTN301TotTmr\".Q;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "4D",
- "title": "ResetCO2Tot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTP302_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v12_"
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v11_"
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "O_sympy_processed",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "28",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy O: v11_ | v12_"
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "29",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTP302TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "29",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"mResetFTP302TotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gFTP302_ResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetFTP302TotTmr\" : TP;"
- },
- {
- "instruction_uid": "31",
- "uid": "31",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gSyrupRoomEn\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "30",
- "source_pin": "q"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v0_ & v23_"
- },
- {
- "instruction_uid": "32",
- "uid": "32",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetSyrupTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "31",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "\"mResetSyrupTot\" := \"gSyrupRoomEn\" AND \"mResetFTP302TotTmr\".Q;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "5E",
- "title": "ResetProductTot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gFTM303_ResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v13_"
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v11_"
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O_sympy_processed",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy O: v11_ | v13_"
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetFTM303TotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"mResetFTM303TotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gFTM303_ResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetFTM303TotTmr\" : TP;"
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetCO2Tot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {},
- "scl": "\"mResetCO2Tot\" := \"mResetFTM303TotTmr\".Q;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "6F",
- "title": "ResetCO2Tot",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "26",
- "uid": "26",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"gProductMFMResetTot\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v14_"
- },
- {
- "instruction_uid": "27",
- "uid": "27",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetTotalizerTmr\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v11_"
- },
- {
- "instruction_uid": "28",
- "uid": "28",
- "type": "O_sympy_processed",
- "template_values": {
- "Card": "Cardinality"
- },
- "negated_pins": {},
- "inputs": {
- "in1": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "26",
- "source_pin": "out"
- },
- "in2": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "27",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy O: v11_ | v14_"
- },
- {
- "instruction_uid": "29",
- "uid": "29",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "s": {
- "type": "connection",
- "source_instruction_type": "O",
- "source_instruction_uid": "28",
- "source_pin": "out"
- },
- "timer": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mResetProductTotTmr\""
- },
- "tv": {
- "uid": "24",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#2S"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "28",
- "source_instruction_type": "O",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"mResetProductTotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gProductMFMResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetProductTotTmr\" : TP;"
- },
- {
- "instruction_uid": "30",
- "uid": "30",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "LocalVariable",
- "type": "variable",
- "name": "\"mResetProductTot\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Se",
- "source_instruction_uid": "29",
- "source_pin": "q"
- }
- },
- "outputs": {},
- "scl": "\"mResetProductTot\" := \"mResetProductTotTmr\".Q;"
- }
- ],
- "language": "LAD"
- },
- {
- "id": "80",
- "title": "Mod Copy Recipe",
- "comment": "",
- "logic": [
- {
- "instruction_uid": "33",
- "uid": "33",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "21",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v15_"
- },
- {
- "instruction_uid": "34",
- "uid": "34",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {
- "operand": true
- },
- "inputs": {
- "operand": {
- "uid": "22",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mFP_Recip_Main_Page\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v15_ & ~v16_"
- },
- {
- "instruction_uid": "35",
- "uid": "35",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "23",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mAux_FP_M700_1\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "34",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "\"mAux_FP_M700_1\" := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND NOT \"mFP_Recip_Main_Page\";"
- },
- {
- "instruction_uid": "36",
- "uid": "36",
- "type": "Coil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "24",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mFP_Recip_Main_Page\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "33",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "\"mFP_Recip_Main_Page\" := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\";"
- },
- {
- "instruction_uid": "37",
- "uid": "37",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "25",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v15_"
- },
- {
- "instruction_uid": "38",
- "uid": "38",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "26",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "37",
- "source_pin": "out"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v15_ & v17_"
- },
- {
- "instruction_uid": "39",
- "uid": "39",
- "type": "Se_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "tv": {
- "uid": "28",
- "scope": "TypedConstant",
- "type": "constant",
- "datatype": "TypedConstant",
- "value": "S5T#500ms"
- },
- "timer": {
- "uid": "27",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"T_Pulse_Recipe_Edit\""
- },
- "s": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "38",
- "source_pin": "out"
- },
- "en": {
- "type": "connection",
- "source_instruction_uid": "38",
- "source_instruction_type": "Contact",
- "source_pin": "out"
- }
- },
- "outputs": {
- "q": []
- },
- "scl": "\"T_Pulse_Recipe_Edit\"(IN := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\", PT := S5T#500ms); // TODO: Declarar \"T_Pulse_Recipe_Edit\" : TP;"
- },
- {
- "instruction_uid": "40",
- "uid": "40",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "29",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"T_Pulse_Recipe_Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "SdCoil",
- "source_instruction_uid": "39",
- "source_pin": "q"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v18_ & v26_"
- },
- {
- "instruction_uid": "41",
- "uid": "41",
- "type": "RCoil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "30",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "40",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "IF \"T_Pulse_Recipe_Edit\" AND \"T_Pulse_Recipe_Edit\".Q THEN\n \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\" := FALSE;\nEND_IF;"
- },
- {
- "instruction_uid": "42",
- "uid": "42",
- "type": "Contact_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "31",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"mAux_FP_M700_1\""
- },
- "in": {
- "type": "powerrail"
- }
- },
- "outputs": {
- "out": []
- },
- "scl": "// SymPy Contact: v19_"
- },
- {
- "instruction_uid": "43",
- "uid": "43",
- "type": "SCoil_sympy_processed",
- "template_values": {},
- "negated_pins": {},
- "inputs": {
- "operand": {
- "uid": "32",
- "scope": "GlobalVariable",
- "type": "variable",
- "name": "\"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
- },
- "in": {
- "type": "connection",
- "source_instruction_type": "Contact",
- "source_instruction_uid": "42",
- "source_pin": "out"
- }
- },
- "outputs": {},
- "scl": "IF \"mAux_FP_M700_1\" THEN\n \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\" := TRUE;\nEND_IF;"
- }
- ],
- "language": "LAD"
+ ]
}
]
}
\ No newline at end of file
diff --git a/TestLAD_simplified_processed.scl b/TestLAD_simplified_processed.scl
index 9dff9d7..08b6a4e 100644
--- a/TestLAD_simplified_processed.scl
+++ b/TestLAD_simplified_processed.scl
@@ -7,64 +7,50 @@ FUNCTION_BLOCK "TestLAD"
VERSION : 0.1
VAR_RETURN
- Ret_Val : Void;
+ Ret_Val : Real;
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;
+ mWaterMaxFlow : Real;
+ mWaterMinFlow : Real;
+ mSyrupMaxFlow : Real;
+ mSyrupMinFlow : Real;
+ mMinRatio : Real;
+ mMaxRatio : Real;
+ mBevBrixMax : Real;
+ mBevBrixMin : Real;
END_VAR
BEGIN
- // Network 1: Manual Syrup Drain Valve Open - Operator Alarm (Original Language: LAD)
+ // Network 1: (Original Language: SCL)
- "gHVP301_Open" := ("gSyrupRoomEn" AND "gBlenderCIPMode" AND "gIN_CIP_CIPRunning" AND "Procedure_Variables"."Blender_Run"."Running" AND NOT "gIN_HVP301_Aux") OR ("gSyrupRoomEn" AND "Procedure_Variables"."FTP302Line_Preparation"."Done" AND NOT "gIN_HVP301_Aux" AND NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled" AND NOT "Procedure_Variables"."Syr_RunOut"."Done");
-
- // Network 2: Manual Syrup Drain Valve Open - Operator Alarm (Original Language: LAD)
-
- "mHVM302_Dly"(IN := "gIN_HVM302_Aux", PT := S5T#1S); // TODO: Declarar "mHVM302_Dly" : TON;
- "gHVM302_Open" := "mHVM302_Dly".Q;
-
- // Network 3: ResetTotalizer (Original Language: LAD)
-
- "mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP;
-
- // Network 4: ResetWaterTot (Original Language: LAD)
-
- "mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP;
- "mResetWaterTot" := "mResetFTN301TotTmr".Q;
-
- // Network 5: ResetCO2Tot (Original Language: LAD)
-
- "mResetFTP302TotTmr"(IN := "mResetTotalizerTmr" OR "gFTP302_ResetTot", PT := S5T#2S); // TODO: Declarar "mResetFTP302TotTmr" : TP;
- "mResetSyrupTot" := "gSyrupRoomEn" AND "mResetFTP302TotTmr".Q;
-
- // Network 6: ResetProductTot (Original Language: LAD)
-
- "mResetFTM303TotTmr"(IN := "mResetTotalizerTmr" OR "gFTM303_ResetTot", PT := S5T#2S); // TODO: Declarar "mResetFTM303TotTmr" : TP;
- "mResetCO2Tot" := "mResetFTM303TotTmr".Q;
-
- // Network 7: ResetCO2Tot (Original Language: LAD)
-
- "mResetProductTotTmr"(IN := "mResetTotalizerTmr" OR "gProductMFMResetTot", PT := S5T#2S); // TODO: Declarar "mResetProductTotTmr" : TP;
- "mResetProductTot" := "mResetProductTotTmr".Q;
-
- // Network 8: Mod Copy Recipe (Original Language: LAD)
-
- "mAux_FP_M700_1" := "HMI_Variables_Cmd"."Recipe"."Main_Page" AND NOT "mFP_Recip_Main_Page";
- "mFP_Recip_Main_Page" := "HMI_Variables_Cmd"."Recipe"."Main_Page";
- "T_Pulse_Recipe_Edit"(IN := "HMI_Variables_Cmd"."Recipe"."Main_Page" AND "HMI_Variables_Cmd"."Recipe"."Edit", PT := S5T#500ms); // TODO: Declarar "T_Pulse_Recipe_Edit" : TP;
- IF "T_Pulse_Recipe_Edit" AND "T_Pulse_Recipe_Edit".Q THEN
- "HMI_Variables_Cmd"."Recipe"."Edit" := FALSE;
+ IF "Blender_Variables".gSP_H2O <> 0 THEN
+ "Blender_Variables".gWaterVFMCalcError := "Blender_Variables".gWaterVFMMeasError / 100 * "Blender_Variables".gSP_H2O;
END_IF;
- IF "mAux_FP_M700_1" THEN
- "HMI_Variables_Cmd"."Recipe"."Edit" := TRUE;
+ IF "Blender_Variables".gSP_SYR <> 0 THEN
+ "Blender_Variables".gSyrupMFMCalcError := ("Blender_Variables".gSyrupMFMMeasError / 100 + ("Blender_Variables".gSyrupMFMZeroStab / ("Blender_Variables".gSP_SYR * 60)) / 100) * "Blender_Variables".gSP_SYR;
END_IF;
+ IF "Blender_Variables".gSP_CO2 <> 0 THEN
+ "Blender_Variables".gCO2MFMCalcError := ("Blender_Variables".gCO2MFMMeasError / 100 + ("Blender_Variables".gCO2MFMZeroStab / ("Blender_Variables".gSP_CO2 * 60 / 1000)) / 100) * "Blender_Variables".gSP_CO2;
+ END_IF;
+ "mWaterMaxFlow" := "Blender_Variables".gSP_H2O + "Blender_Variables".gWaterVFMCalcError;
+ "mWaterMinFlow" := "Blender_Variables".gSP_H2O - "Blender_Variables".gWaterVFMCalcError;
+ IF "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN
+ "mSyrupMaxFlow" := ("Blender_Variables".gSP_SYR + "Blender_Variables".gSyrupMFMCalcError) / "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity;
+ "mSyrupMinFlow" := ("Blender_Variables".gSP_SYR - "Blender_Variables".gSyrupMFMCalcError) / "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity;
+ END_IF;
+ IF "mSyrupMaxFlow" <> 0 THEN
+ "mMinRatio" := "mWaterMinFlow" / "mSyrupMaxFlow";
+ END_IF;
+ IF "mSyrupMinFlow" <> 0 THEN
+ "mMaxRatio" := "mWaterMaxFlow" / "mSyrupMinFlow";
+ END_IF;
+ IF "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity <> 0 THEN
+ "mBevBrixMax" := "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupBrix / (("mMinRatio" / "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity) + 1);
+ "mBevBrixMin" := "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupBrix / (("mMaxRatio" / "HMI_Blender_Parameters".Actual_Recipe_Parameters._SyrupDensity) + 1);
+ END_IF;
+ "Blender_Variables".gBlenderBlendMaxError := "mBevBrixMax" - "mBevBrixMin";
+ "TestLAD" := "Blender_Variables".gBlenderBlendMaxError;
END_FUNCTION_BLOCK
diff --git a/ToUpload/TestLAD.xml b/ToUpload/TestLAD.xml
new file mode 100644
index 0000000..f8ea13d
--- /dev/null
+++ b/ToUpload/TestLAD.xml
@@ -0,0 +1,763 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Optimized
+ TestLAD
+
+ 2
+ LAD
+ false
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DInt
+ 3
+
+
+
+
+
+
+
+ STL
+
+
+
+
+
+
+ it-IT
+ DEVICE
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+ it-IT
+ Set manual active
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Int
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ config
+
+
+
+
+
+
+
+
+ auto
+
+
+
+
+
+
+
+
+
+
+
+ DInt
+ 3
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ STL
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+
+
+
+ PID
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Int
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ config
+
+
+
+
+
+
+
+
+
+
+
+ ManOut
+
+
+
+
+
+
+
+
+ LocalSP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DInt
+ 3
+
+
+
+
+
+
+
+
+
+
+ ARef
+ P#76.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ STL
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
+
+
+
+ it-IT
+
+
+
+
+
+ de-DE
+
+
+
+
+
+ en-US
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ fr-FR
+
+
+
+
+
+ zh-CN
+
+
+
+
+
+ ja-JP
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ToUpload/TestLAD_simplified.json b/ToUpload/TestLAD_simplified.json
new file mode 100644
index 0000000..9f52ad5
--- /dev/null
+++ b/ToUpload/TestLAD_simplified.json
@@ -0,0 +1,101 @@
+{
+ "block_name": "TestLAD",
+ "block_number": 2,
+ "language": "LAD",
+ "block_comment": "",
+ "interface": {
+ "Temp": [
+ {
+ "name": "All_Auto_RETVAL",
+ "datatype": "Int"
+ },
+ {
+ "name": "Reset_SP_Word_RETVAL",
+ "datatype": "Int"
+ },
+ {
+ "name": "mResetWaterTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetSyrupTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetCO2Tot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetProductTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "Block_Move_Err",
+ "datatype": "Int"
+ },
+ {
+ "name": "Dim_HMI_Device",
+ "datatype": "Int"
+ },
+ {
+ "name": "PDim_HMI_Device",
+ "datatype": "DWord"
+ },
+ {
+ "name": "Dim_HMI_PID",
+ "datatype": "Int"
+ },
+ {
+ "name": "PDim_HMI_PID",
+ "datatype": "DWord"
+ }
+ ],
+ "Return": [
+ {
+ "name": "Ret_Val",
+ "datatype": "Void"
+ }
+ ]
+ },
+ "networks": [
+ {
+ "id": "9",
+ "title": "Set manual active",
+ "comment": "DEVICE",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_9",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 9 uses unsupported language: STL\n"
+ }
+ ]
+ },
+ {
+ "id": "1A",
+ "title": "",
+ "comment": "",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_1A",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 1A uses unsupported language: STL\n"
+ }
+ ]
+ },
+ {
+ "id": "2B",
+ "title": "",
+ "comment": "",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_2B",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 2B uses unsupported language: STL\n"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ToUpload/TestLAD_simplified_processed.json b/ToUpload/TestLAD_simplified_processed.json
new file mode 100644
index 0000000..9f52ad5
--- /dev/null
+++ b/ToUpload/TestLAD_simplified_processed.json
@@ -0,0 +1,101 @@
+{
+ "block_name": "TestLAD",
+ "block_number": 2,
+ "language": "LAD",
+ "block_comment": "",
+ "interface": {
+ "Temp": [
+ {
+ "name": "All_Auto_RETVAL",
+ "datatype": "Int"
+ },
+ {
+ "name": "Reset_SP_Word_RETVAL",
+ "datatype": "Int"
+ },
+ {
+ "name": "mResetWaterTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetSyrupTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetCO2Tot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "mResetProductTot",
+ "datatype": "Bool"
+ },
+ {
+ "name": "Block_Move_Err",
+ "datatype": "Int"
+ },
+ {
+ "name": "Dim_HMI_Device",
+ "datatype": "Int"
+ },
+ {
+ "name": "PDim_HMI_Device",
+ "datatype": "DWord"
+ },
+ {
+ "name": "Dim_HMI_PID",
+ "datatype": "Int"
+ },
+ {
+ "name": "PDim_HMI_PID",
+ "datatype": "DWord"
+ }
+ ],
+ "Return": [
+ {
+ "name": "Ret_Val",
+ "datatype": "Void"
+ }
+ ]
+ },
+ "networks": [
+ {
+ "id": "9",
+ "title": "Set manual active",
+ "comment": "DEVICE",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_9",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 9 uses unsupported language: STL\n"
+ }
+ ]
+ },
+ {
+ "id": "1A",
+ "title": "",
+ "comment": "",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_1A",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 1A uses unsupported language: STL\n"
+ }
+ ]
+ },
+ {
+ "id": "2B",
+ "title": "",
+ "comment": "",
+ "language": "STL",
+ "logic": [
+ {
+ "instruction_uid": "UNS_2B",
+ "type": "UNSUPPORTED_LANG",
+ "scl": "// Network 2B uses unsupported language: STL\n"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ToUpload/TestLAD_simplified_processed.scl.txt b/ToUpload/TestLAD_simplified_processed.scl.txt
new file mode 100644
index 0000000..d096e71
--- /dev/null
+++ b/ToUpload/TestLAD_simplified_processed.scl.txt
@@ -0,0 +1,42 @@
+// Block Name (Original): TestLAD
+// Block Number: 2
+// Original Language: LAD
+
+FUNCTION_BLOCK "TestLAD"
+{ S7_Optimized_Access := 'TRUE' }
+VERSION : 0.1
+
+VAR_RETURN
+ Ret_Val : Void;
+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;
+ Dim_HMI_Device : Int;
+ PDim_HMI_Device : DWord;
+ Dim_HMI_PID : Int;
+ PDim_HMI_PID : DWord;
+END_VAR
+
+BEGIN
+
+ // Network 1: Set manual active (Original Language: STL)
+ // DEVICE
+
+ // Network 9 uses unsupported language: STL
+
+ // Network 2: (Original Language: STL)
+
+ // Network 1A uses unsupported language: STL
+
+ // Network 3: (Original Language: STL)
+
+ // Network 2B uses unsupported language: STL
+
+END_FUNCTION_BLOCK
diff --git a/ToUpload/XSD Schema Definition/SW.Common_v3.xsd.xml b/ToUpload/XSD Schema Definition/SW.Common_v3.xsd.xml
new file mode 100644
index 0000000..19255f0
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.Common_v3.xsd.xml
@@ -0,0 +1,284 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A member attribute with a type restriction of boolean.
+
+
+
+
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+ An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.
+
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+ LAD/FBD: Only for Parts
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ For NumBLs in STL. NumBLs is the count of the blank spaces before the actual text in the Comment. This is informative.
+
+
+
+
+
+
+ Denotes if the comment is at the end of the line (using /*/) or inside the line (using (/* */) )
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+
+
+
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+ An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A member attribute with a type restriction of integer.
+
+
+
+
+ Not for LAD/FBD.
+
+
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+ An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not for LAD/FBD
+
+
+
+
+
+
+ For NumBLs in STL. NumBLs is the count of the blank spaces before the actual text in the LineComment. This is informative.
+
+
+
+
+
+
+
+
+
+ Denotes if the comment is at the end of the line (using //) or inside the line (using /* */)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A member attribute with a type restriction of real.
+
+
+
+
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+ An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A member attribute with a type restriction of string.
+
+
+
+
+
+
+
+
+ Exported only with ReadOnly option, ignored during import.
+
+
+
+
+ An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ For NumBLs. NumBLs is the count of the blank spaces at the start.This is informative.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.Interface.Snapshot.xsd.xml b/ToUpload/XSD Schema Definition/SW.Interface.Snapshot.xsd.xml
new file mode 100644
index 0000000..cb59523
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.Interface.Snapshot.xsd.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ToUpload/XSD Schema Definition/SW.InterfaceSections_v5.xsd.xml b/ToUpload/XSD Schema Definition/SW.InterfaceSections_v5.xsd.xml
new file mode 100644
index 0000000..7f0ba20
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.InterfaceSections_v5.xsd.xml
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The version of the library type to use. Previous to this, the version was written inside the Datatype attribute itself, like "dtl:v1.0". Now, this is written in two separate attributes, to mitigate problems with weird names ("dtl:v1.0" could be a UDT name!).
+
+
+
+
+
+
+
+
+
+
+ Write acces only inside function
+
+
+
+
+ string: Member shares offset with another member in this structure
+
+
+
+
+ boolean: Member can be synchronized with work memory
+
+
+
+
+ boolean: Editor does not show the member
+
+
+
+
+ boolean: User cannot change member name
+
+
+
+
+ boolean: Editor does not allow to delete the member
+
+
+
+
+ boolean: No HMI access, no structure item
+
+
+
+
+ boolean: Filter to reduce the number of members shown in the first place
+
+
+
+
+ integer:
+
+
+
+
+ integer:
+
+
+
+
+ boolean: Hide assignement at call if matches with PredefinedAssignment
+
+
+
+
+ string: Input for the paramter used when call is placed
+
+
+
+
+ boolean: The user cannot change the predefined assignement at the call
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Base Class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.Access_v4.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.Access_v4.xsd.xml
new file mode 100644
index 0000000..86c4cef
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.Access_v4.xsd.xml
@@ -0,0 +1,593 @@
+
+
+
+
+
+
+
+
+
+ Byte * 8 + Bit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ for NumBLs. NumBLs is informative. Not for LAD/FBD.
+
+
+
+
+
+
+
+ call of a user block. Not in Graph ActionList.
+
+
+
+
+ call of an instruction. Not for LAD/FBD, Graph ActionList.
+
+
+
+
+ STL specific
+
+
+
+
+
+ Only in SCL
+
+
+
+
+ SCL specific
+
+
+
+
+
+ for absolute addresses
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ for DB access
+
+
+
+
+ In general it is Byte * 8 + Bit. But if it is used for addressing a DB we will find the number of the DB here (e.g. "DB12" ->12).
+
+
+
+
+ if true, the import unnoted it
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ partly qualified access with DB register
+
+
+
+
+ partly qualified access with DI register
+
+
+
+
+
+
+ Classic Local Stack
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not for LAD/FBD.
+
+
+
+
+ for BlockNumber. BlockNumber is informative.
+
+
+
+
+ for ParameterModifiedTS. ParameterModifiedTS is informative
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SCL
+
+
+
+
+ SCL
+
+
+
+
+ For the indices of an array
+
+
+
+
+
+
+
+
+
+ If component has child AccessModifier is Array else AccessModifier is None
+
+
+
+
+
+
+
+
+
+
+
+ for Format and FormatFlags. They are informative..
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ the DOT; only if separated. Not in Graph ActionList, not in LAD/FBD.
+
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SCL only
+
+
+
+
+
+
+
+
+
+
+
+
+
+ for InterfaceFlags. InterfaceFlags is informative
+ The type of the value should be InterfaceFlags_TP
+ The default value is "S7_Visible"
+
+
+
+
+
+
+
+
+
+
+
+
+ for NumBLs. NumBLs is informative
+
+
+
+
+ for InterfaceFlags. InterfaceFlags is informative
+ The type of the value should be InterfaceFlags_TP
+ The default value is "S7_Visible"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SCL
+
+
+
+
+
+
+
+
+ SCL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Symbols we do not know what they are
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only for S7-300/400/WinAC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SCL.
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.CompileUnitCommon_v4.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.CompileUnitCommon_v4.xsd.xml
new file mode 100644
index 0000000..4212279
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.CompileUnitCommon_v4.xsd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+ for NumBLs. NumBLs is informative
+
+
+
+
+
+
+
+ the COLON; only if separated
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.Graph_v5.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.Graph_v5.xsd.xml
new file mode 100644
index 0000000..6f27866
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.Graph_v5.xsd.xml
@@ -0,0 +1,350 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Temporary change for enable of empty alarm text because of the graph alarm handling reconstruction.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enabler token
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ For translated transiton names
+
+
+
+
+
+
+
+
+ For translated step names
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.InstanceSupervisions_v3.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.InstanceSupervisions_v3.xsd.xml
new file mode 100644
index 0000000..a8371ae
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.InstanceSupervisions_v3.xsd.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.LADFBD_v4.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.LADFBD_v4.xsd.xml
new file mode 100644
index 0000000..4cd4a14
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.LADFBD_v4.xsd.xml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The invisible pins of this part.
+
+
+
+
+
+ The name of the invisible pin.
+
+
+
+
+
+
+
+
+
+
+
+
+ The name of the negated pin.
+
+
+
+
+
+ The negated pins of this part.
+
+
+
+
+
+ The name of the automatic chosen template parameter. Not for InstructionRef
+
+
+
+
+
+
+
+
+
+
+
+ The equation of this part. This is only used for the Calculate box.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.SCL_v3.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.SCL_v3.xsd.xml
new file mode 100644
index 0000000..65b5aad
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.SCL_v3.xsd.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.STL_v4.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.STL_v4.xsd.xml
new file mode 100644
index 0000000..be6b647
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.STL_v4.xsd.xml
@@ -0,0 +1,482 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ missing for empty lines
+
+
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+
+
+
+
+ for NumBLs. NumBLs is informative
+
+
+
+
+
+
+ e.g 0 1 for NOP 0, NOP 1; STW for L STW or DILG for L DILG; only if separated by comment
+
+
+
+
+
+
+ Not allowed in STL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SE, SV
+
+
+
+
+
+
+ SF, SA
+
+
+
+
+ SS
+
+
+
+
+ SD, SE
+
+
+
+
+ SP, SI
+
+
+
+
+
+
+
+
+
+ AUF
+
+
+
+
+ AUF DI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SPA
+
+
+
+
+ SPB
+
+
+
+
+ SPO
+
+
+
+
+ SPZ
+
+
+
+
+ SPP
+
+
+
+
+ SPM
+
+
+
+
+ SPN
+
+
+
+
+ SPBN
+
+
+
+
+ SPBB
+
+
+
+
+ SPBNB
+
+
+
+
+ SPBI
+
+
+
+
+ SPBNI
+
+
+
+
+ SPS
+
+
+
+
+ SPU
+
+
+
+
+ SPMZ
+
+
+
+
+ SPZ
+
+
+
+
+
+
+
+
+
+
+
+ SSD, SVD
+
+
+
+
+ SSW, SVW
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ KEW, INV_F
+
+
+
+
+ KZW, NEG_F
+
+
+
+
+ KED
+
+
+
+
+ KZD
+
+
+
+
+ NEG_G, ND
+
+
+
+
+ ABS_G
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DEF
+
+
+
+
+ DUF
+
+
+
+
+ DED
+
+
+
+
+ DUD
+
+
+
+
+ FDG
+
+
+
+
+ GFDN
+
+
+
+
+ GFDM
+
+
+
+
+ GFDP
+
+
+
+
+
+ FD
+
+
+
+
+ TAW
+
+
+
+
+ TAD
+
+
+
+
+
+
+ +F
+
+
+
+
+ -F
+
+
+
+
+ xF
+
+
+
+
+ :F
+
+
+
+
+ +D
+
+
+
+
+ -D
+
+
+
+
+ xD
+
+
+
+
+ :D
+
+
+
+
+
+
+
+
+
+ +G
+
+
+
+
+ -G
+
+
+
+
+ xG
+
+
+
+
+ :G
+
+
+
+
+ TAK
+
+
+
+
+
+
+
+
+
+
+ BEB
+
+
+
+
+ )
+
+
+
+
+
+
+
+ MCR(
+
+
+
+
+ MCR)
+
+
+
+
+
+
+
+
+
+
+
+ TAR
+
+
+
+
+ TDB
+
+
+
+
+
+
+
+
+
+ BEA
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.PlcBlocks.TypeSupervisions_v3.xsd.xml b/ToUpload/XSD Schema Definition/SW.PlcBlocks.TypeSupervisions_v3.xsd.xml
new file mode 100644
index 0000000..3f9745f
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.PlcBlocks.TypeSupervisions_v3.xsd.xml
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataAxis_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataAxis_v1.xsd.xml
new file mode 100644
index 0000000..feffa1b
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataAxis_v1.xsd.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+ Describes additional data, such as Connections, for Axis and ExternalEncoder TOs.
+
+
+
+
+
+
+
+
+
+
+ Describes a connection of a TO interface.
+
+
+
+ Specifies the Interface of the TO that is connected.
+
+
+
+
+ Input bit address.
+
+
+
+
+ Output bit address.
+
+
+
+
+ Connect option used when the connection has been created.
+
+
+
+
+ Index of sensor in actor telegram if connected to same telegram.
+
+
+
+
+ Path to a DB member.
+
+
+
+
+ Name of a connected tag for analog connection.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Contains a list of master values for TO_SynchronousAxis.
+
+
+
+
+ Describes a reference to a master value TO that is coupled via set points.
+
+
+
+
+ Describes a reference to a master value TO that is coupled via actual values.
+
+
+
+
+ Describes a reference to a master value TO that is coupled via delayed values.
+
+
+
+
+ Describes a reference to a master value TO of type LeadingAxisProxy.
+
+
+
+
+
+
+
+
+ Describes a reference to a Technological Object.
+
+
+
+ Specifies the name of the referenced Technological Object.
+
+
+
+
+ Specifies the type of the referenced Technological Object.
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataKinematics_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataKinematics_v1.xsd.xml
new file mode 100644
index 0000000..568f342
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataKinematics_v1.xsd.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+ Describes additional data, such as connected axes, for Kinematics TOs.
+
+
+
+
+
+
+
+
+
+
+ Describes a reference to a Technological Object.
+
+
+
+ Specifies the name of the referenced Technological Object.
+
+
+
+
+ Specifies the name of the referenced Technological Object.
+
+
+
+
+ Specifies the type of the referenced Technological Object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Contains a list of leading values for conveyor tracking.
+
+
+
+
+ Describes a reference to a leading value TO that is coupled via set points.
+
+
+
+
+ Describes a reference to a leading value TO that is coupled via actual values.
+
+
+
+
+ Describes a reference to a leading value TO that is coupled via delayed values.
+
+
+
+
+ Describes a reference to a leading value TO of type LeadingAxisProxy.
+
+
+
+
+
+
+
+
+ Describes a reference to a Technological Object.
+
+
+
+ Specifies the name of the referenced Technological Object.
+
+
+
+
+ Specifies the type of the referenced Technological Object.
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataMeasuringInput_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataMeasuringInput_v1.xsd.xml
new file mode 100644
index 0000000..8d2b293
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataMeasuringInput_v1.xsd.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+ Describes additional data, such as Connections, for MeasuringInput TOs.
+
+
+
+
+
+
+
+
+
+ Describes a connection of a TO interface.
+
+
+
+ Specifies the Interface of the TO that is connected.
+
+
+
+
+ Input bit address.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataOutputCam_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataOutputCam_v1.xsd.xml
new file mode 100644
index 0000000..7629aa2
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_AdditionalDataOutputCam_v1.xsd.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+ Describes additional data, such as Connections, for OutputCam and CamTrack TOs.
+
+
+
+
+
+
+
+
+
+ Describes a connection of a TO interface.
+
+
+
+ Specifies the Interface of the TO that is connected.
+
+
+
+
+ Output bit address.
+
+
+
+
+ Name of a connected tag.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_Parameters_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_Parameters_v1.xsd.xml
new file mode 100644
index 0000000..17a4f01
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_Parameters_v1.xsd.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+ Describes a list of parameters.
+
+
+
+
+
+
+
+
+
+
+ Describes a single parameter, having Name and Value. If the Value is missing, the default value of the Parameter is used.
+
+
+
+ Name of the Parameter
+
+
+
+
+ Value of the Parameter
+
+
+
+
diff --git a/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_ProfileDataCam_v1.xsd.xml b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_ProfileDataCam_v1.xsd.xml
new file mode 100644
index 0000000..20ee84f
--- /dev/null
+++ b/ToUpload/XSD Schema Definition/SW.TechnologicalObjects_ProfileDataCam_v1.xsd.xml
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ToUpload/processors/__init__.py b/ToUpload/processors/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ToUpload/processors/process_add.py b/ToUpload/processors/process_add.py
new file mode 100644
index 0000000..3c7fb6f
--- /dev/null
+++ b/ToUpload/processors/process_add.py
@@ -0,0 +1,87 @@
+# processors/process_add.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re # Importar re si se usa para formateo
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed" # Usar el nuevo sufijo
+
+def process_add(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera SCL para Add, simplificando la condición EN."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Add")
+ current_type = instruction.get("type","")
+ if current_type.endswith(SCL_SUFFIX) or "_error" in current_type:
+ return False
+
+ # Obtener EN (SymPy), IN1, IN2 (SymPy o Constante/String)
+ en_input = instruction["inputs"].get("en")
+ in1_info = instruction["inputs"].get("in1")
+ in2_info = instruction["inputs"].get("in2")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+ op1_sympy_or_const = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
+ op2_sympy_or_const = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener destino SCL
+ target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
+
+ # Verificar dependencias
+ if sympy_en_expr is None or op1_sympy_or_const is None or op2_sympy_or_const is None or target_scl_name is None:
+ # print(f"DEBUG Add {instr_uid}: Dependency not ready")
+ return False
+
+ # Convertir operandos SymPy/Constante a SCL strings
+ op1_scl = sympy_expr_to_scl(op1_sympy_or_const, symbol_manager)
+ op2_scl = sympy_expr_to_scl(op2_sympy_or_const, symbol_manager)
+
+ # Añadir paréntesis si contienen operadores (más seguro para SCL)
+ op1_scl_formatted = f"({op1_scl})" if re.search(r'[+\-*/ ]', op1_scl) else op1_scl
+ op2_scl_formatted = f"({op2_scl})" if re.search(r'[+\-*/ ]', op2_scl) else op2_scl
+
+ # Generar SCL Core
+ scl_core = f"{target_scl_name} := {op1_scl_formatted} + {op2_scl_formatted};"
+
+ # Aplicar Condición EN (Simplificando EN)
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+
+ except Exception as e:
+ print(f"Error simplifying EN for {instr_type_original} {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ # Evitar IF TRUE THEN...
+ if en_condition_scl == "TRUE":
+ scl_final = scl_core
+ # Evitar IF FALSE THEN...
+ elif en_condition_scl == "FALSE":
+ scl_final = f"// {instr_type_original} {instr_uid} condition simplified to FALSE."
+ else:
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # Actualizar instrucción y mapa
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar valor de salida (nombre SCL del destino) y ENO (expresión SymPy)
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino (string)
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el procesador Add."""
+ # Asegurar que la clave coincida con el tipo en JSON ('add')
+ return {'type_name': 'add', 'processor_func': process_add, 'priority': 4}
\ No newline at end of file
diff --git a/ToUpload/processors/process_blkmov.py b/ToUpload/processors/process_blkmov.py
new file mode 100644
index 0000000..9d9ccd7
--- /dev/null
+++ b/ToUpload/processors/process_blkmov.py
@@ -0,0 +1,118 @@
+# processors/process_blkmov.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed" # Usar el nuevo sufijo
+
+def process_blkmov(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL usando BLKMOV directamente como nombre de función,
+ simplificando la condición EN.
+ ADVERTENCIA: Sintaxis BLKMOV probablemente no compile en TIA estándar.
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "BlkMov") # Asegurar que el tipo base sea correcto
+ current_type = instruction.get("type","")
+ if current_type.endswith(SCL_SUFFIX) or "_error" in current_type:
+ return False
+
+ # --- Obtener Entradas ---
+ en_input = instruction["inputs"].get("en")
+ # Obtener EN como expresión SymPy
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+
+ srcblk_info = instruction["inputs"].get("SRCBLK")
+ # Obtener nombre RAW de SRCBLK (como se hacía antes, si es necesario para BLKMOV)
+ # Este nombre NO pasa por SymPy, se usa directo en el string SCL final
+ raw_srcblk_name = srcblk_info.get("name") if srcblk_info else None
+
+ # Verificar dependencias (EN debe estar resuelto, SRCBLK debe tener nombre)
+ if sympy_en_expr is None:
+ # print(f"DEBUG BlkMov {instr_uid}: EN dependency not ready")
+ return False
+ if raw_srcblk_name is None:
+ print(f"Error: BLKMOV {instr_uid} sin información válida para SRCBLK.")
+ instruction["scl"] = f"// ERROR: BLKMOV {instr_uid} sin SRCBLK válido."
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # --- Obtener Destinos (Salidas) ---
+ # RET_VAL (Obtener nombre SCL formateado)
+ retval_target_scl = get_target_scl_name(instruction, "RET_VAL", network_id, default_to_temp=True)
+ if retval_target_scl is None: # get_target_scl_name ya imprime error si falla y default_to_temp=True
+ instruction["scl"] = f"// ERROR: BLKMOV {instr_uid} no pudo generar destino RET_VAL"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # DSTBLK (Obtener nombre RAW como antes, si se necesita)
+ raw_dstblk_name = None
+ dstblk_output_list = instruction.get("outputs", {}).get("DSTBLK", [])
+ if dstblk_output_list and isinstance(dstblk_output_list, list) and len(dstblk_output_list) == 1:
+ dest_access = dstblk_output_list[0]
+ if dest_access.get("type") == "variable":
+ raw_dstblk_name = dest_access.get("name")
+ # Manejar error si no se encuentra DSTBLK
+ if raw_dstblk_name is None:
+ print(f"Error: No se encontró un destino único y válido para DSTBLK en BLKMOV {instr_uid}.")
+ instruction["scl"] = f"// ERROR: BLKMOV {instr_uid} sin destino DSTBLK válido."
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # --- Formateo especial (mantener nombres raw si es necesario para BLKMOV) ---
+ # Estos nombres van directo al string SCL, no necesitan pasar por SymPy
+ srcblk_final_str = raw_srcblk_name # Asumiendo que ya viene con comillas si las necesita
+ dstblk_final_str = raw_dstblk_name # Asumiendo que ya viene con comillas si las necesita
+
+ # --- Generar SCL Core (Usando la sintaxis no estándar BLKMOV) ---
+ scl_core = (
+ f"{retval_target_scl} := BLKMOV(SRCBLK := {srcblk_final_str}, "
+ f"DSTBLK => {dstblk_final_str}); " # Usar => para Out/InOut
+ f"// ADVERTENCIA: BLKMOV usado directamente, probablemente no compile!"
+ )
+
+ # --- Aplicar Condición EN (Simplificando EN) ---
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for {instr_type_original} {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ # Evitar IF TRUE/FALSE THEN...
+ if en_condition_scl == "TRUE":
+ scl_final = scl_core
+ elif en_condition_scl == "FALSE":
+ scl_final = f"// {instr_type_original} {instr_uid} condition simplified to FALSE."
+ else:
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # --- Actualizar Instrucción y Mapa SymPy ---
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar ENO (expresión SymPy)
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr
+
+ # Propagar el valor de retorno (nombre SCL string del destino de RET_VAL)
+ map_key_ret_val = (network_id, instr_uid, "RET_VAL")
+ sympy_map[map_key_ret_val] = retval_target_scl
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el procesador BLKMOV."""
+ # Asegurarse que el type_name coincida con el JSON ('blkmov' parece probable)
+ return {'type_name': 'blkmov', 'processor_func': process_blkmov, 'priority': 6}
\ No newline at end of file
diff --git a/ToUpload/processors/process_call.py b/ToUpload/processors/process_call.py
new file mode 100644
index 0000000..7902697
--- /dev/null
+++ b/ToUpload/processors/process_call.py
@@ -0,0 +1,131 @@
+# processors/process_call.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Asumiendo que estas funciones ahora existen y están adaptadas
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
+from .symbol_manager import SymbolManager # Necesitamos pasar el symbol_manager
+
+# Definir sufijo globalmente o importar
+SCL_SUFFIX = "_sympy_processed"
+
+def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "") # Tipo antes de añadir sufijo
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ block_name = instruction.get("block_name", f"UnknownCall_{instr_uid}")
+ block_type = instruction.get("block_type") # FC, FB
+ instance_db = instruction.get("instance_db") # Nombre del DB de instancia (para FB)
+
+ # Formatear nombres SCL (para la llamada final)
+ block_name_scl = format_variable_name(block_name)
+ instance_db_scl = format_variable_name(instance_db) if instance_db else None
+
+ # --- Manejo de EN ---
+ en_input = instruction["inputs"].get("en")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+
+ if sympy_en_expr is None:
+ # print(f"DEBUG Call {instr_uid}: EN dependency not ready.")
+ return False # Dependencia EN no resuelta
+
+ # --- Procesar Parámetros de Entrada ---
+ scl_call_params = []
+ processed_inputs = {"en"}
+ dependencies_resolved = True
+
+ # Ordenar para consistencia
+ input_pin_names = sorted(instruction.get("inputs", {}).keys())
+
+ for pin_name in input_pin_names:
+ if pin_name not in processed_inputs:
+ source_info = instruction["inputs"][pin_name]
+ # Obtener la representación de la fuente (puede ser SymPy o Constante/String)
+ source_sympy_or_const = get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
+
+ if source_sympy_or_const is None:
+ # print(f"DEBUG Call {instr_uid}: Input param '{pin_name}' dependency not ready.")
+ dependencies_resolved = False
+ break # Salir si una dependencia no está lista
+
+ # Convertir la expresión/constante a SCL para la llamada
+ # Simplificar ANTES de convertir? Probablemente no necesario para parámetros de entrada
+ # a menos que queramos optimizar el valor pasado. Por ahora, convertir directo.
+ param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
+
+ # El nombre del pin SÍ necesita formateo
+ pin_name_scl = format_variable_name(pin_name)
+ scl_call_params.append(f"{pin_name_scl} := {param_scl_value}")
+ processed_inputs.add(pin_name)
+
+ if not dependencies_resolved:
+ return False
+
+ # --- Construcción de la Llamada SCL (similar a antes) ---
+ scl_call_body = ""
+ param_string = ", ".join(scl_call_params)
+
+ if block_type == "FB":
+ if not instance_db_scl:
+ print(f"Error: Call FB '{block_name_scl}' (UID {instr_uid}) sin instancia.")
+ instruction["scl"] = f"// ERROR: FB Call {block_name_scl} sin instancia"
+ instruction["type"] = f"Call_FB_error"
+ return True
+ scl_call_body = f"{instance_db_scl}({param_string});"
+ elif block_type == "FC":
+ scl_call_body = f"{block_name_scl}({param_string});"
+ else:
+ print(f"Advertencia: Tipo de bloque no soportado para Call UID {instr_uid}: {block_type}")
+ scl_call_body = f"// ERROR: Call a bloque tipo '{block_type}' no soportado: {block_name_scl}"
+ instruction["type"] = f"Call_{block_type}_error" # Marcar como error
+
+ # --- Aplicar Condición EN (usando la expresión SymPy EN) ---
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ # Simplificar la condición EN ANTES de convertirla a SCL
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for Call {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ indented_call = "\n".join([f" {line}" for line in scl_call_body.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_call}\nEND_IF;"
+ else:
+ scl_final = scl_call_body
+
+ # --- Actualizar Instrucción y Mapa SymPy ---
+ instruction["scl"] = scl_final # Guardar el SCL final generado
+ instruction["type"] = (f"Call_{block_type}{SCL_SUFFIX}" if "_error" not in instruction["type"] else instruction["type"])
+
+ # Actualizar sympy_map con el estado ENO (es la expresión SymPy de EN)
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
+
+ # Propagar valores de salida (requiere info de interfaz o heurística)
+ # Si se sabe que hay una salida 'MyOutput', se podría añadir su SCL al mapa
+ # Ejemplo MUY simplificado:
+ # for pin_name, dest_list in instruction.get("outputs", {}).items():
+ # if pin_name != 'eno' and dest_list: # Asumir que hay un destino
+ # map_key_out = (network_id, instr_uid, pin_name)
+ # if block_type == "FB" and instance_db_scl:
+ # sympy_map[map_key_out] = f"{instance_db_scl}.{format_variable_name(pin_name)}" # Guardar el *string* de acceso SCL
+ # # Para FCs es más complejo, necesitaría asignación explícita a temp
+ # # else: # FC output -> necesita temp var
+ # # temp_var = generate_temp_var_name(...)
+ # # sympy_map[map_key_out] = temp_var
+
+ return True
+
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para las llamadas a FC y FB."""
+ return [
+ {'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6},
+ {'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_coil.py b/ToUpload/processors/process_coil.py
new file mode 100644
index 0000000..3cb689c
--- /dev/null
+++ b/ToUpload/processors/process_coil.py
@@ -0,0 +1,82 @@
+# processors/process_coil.py
+import sympy
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
+ """Genera la asignación SCL para Coil, simplificando la entrada SymPy."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Coil")
+
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Get input expression from SymPy map
+ coil_input_info = instruction["inputs"].get("in")
+ sympy_expr_in = get_sympy_representation(coil_input_info, network_id, sympy_map, symbol_manager)
+
+ # Get target variable SCL name
+ target_scl_name = get_target_scl_name(instruction, "operand", network_id, default_to_temp=False) # Coil must have explicit target
+
+ # Check dependencies
+ if sympy_expr_in is None:
+ # print(f"DEBUG Coil {instr_uid}: Input dependency not ready.")
+ return False
+ if target_scl_name is None:
+ print(f"Error: Coil {instr_uid} operando no es variable o falta info.")
+ instruction["scl"] = f"// ERROR: Coil {instr_uid} operando no es variable."
+ instruction["type"] = instr_type_original + "_error"
+ return True # Processed with error
+
+ # *** Perform Simplification ***
+ try:
+ #simplified_expr = sympy.simplify_logic(sympy_expr_in, force=False)
+ #simplified_expr = sympy_expr_in
+ simplified_expr = sympy.logic.boolalg.to_dnf(sympy_expr_in, simplify=True)
+ except Exception as e:
+ print(f"Error during SymPy simplification for Coil {instr_uid}: {e}")
+ simplified_expr = sympy_expr_in # Fallback to original expression
+
+ # *** Convert simplified expression back to SCL string ***
+ condition_scl = sympy_expr_to_scl(simplified_expr, symbol_manager)
+
+ # Generate the final SCL assignment
+ scl_assignment = f"{target_scl_name} := {condition_scl};"
+ scl_final = scl_assignment
+
+ # --- Handle Edge Detector Memory Update (Logic similar to before) ---
+ # Check if input comes from PBox/NBox and append memory update
+ mem_update_scl_combined = None
+ if isinstance(coil_input_info, dict) and coil_input_info.get("type") == "connection":
+ source_uid = coil_input_info.get("source_instruction_uid")
+ source_pin = coil_input_info.get("source_pin")
+ source_instruction = None
+ network_logic = next((net["logic"] for net in data["networks"] if net["id"] == network_id), [])
+ for instr in network_logic:
+ if instr.get("instruction_uid") == source_uid:
+ source_instruction = instr
+ break
+ if source_instruction:
+ # Check for the original type before suffix was added
+ orig_source_type = source_instruction.get("type", "").replace(SCL_SUFFIX, '').replace('_error', '')
+ if orig_source_type in ["PBox", "NBox"] and '_edge_mem_update_scl' in source_instruction:
+ mem_update_scl_combined = source_instruction.get('_edge_mem_update_scl')
+ if mem_update_scl_combined:
+ scl_final = f"{scl_assignment}\n{mem_update_scl_combined}"
+ # Clear the source SCL?
+ source_instruction['scl'] = f"// Edge Logic handled by Coil {instr_uid}"
+
+
+ # Update instruction
+ instruction["scl"] = scl_final
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ # Coil typically doesn't output to scl_map
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el procesador Coil."""
+ return {'type_name': 'coil', 'processor_func': process_coil, 'priority': 3}
\ No newline at end of file
diff --git a/ToUpload/processors/process_comparison.py b/ToUpload/processors/process_comparison.py
new file mode 100644
index 0000000..cf0bfcd
--- /dev/null
+++ b/ToUpload/processors/process_comparison.py
@@ -0,0 +1,87 @@
+# processors/process_comparison.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+from .processor_utils import get_sympy_representation, format_variable_name # No necesita sympy_expr_to_scl aquí
+from .symbol_manager import SymbolManager # Necesita acceso al manager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_comparison(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera la expresión SymPy para Comparadores (GT, LT, GE, LE, NE).
+ El resultado se propaga por sympy_map['out'].
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "") # GT, LT, GE, LE, NE
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Mapa de tipos a funciones/clases SymPy Relational
+ # Nota: Asegúrate de que los tipos coincidan (ej. si son números o booleanos)
+ op_map = {
+ "GT": sympy.Gt, # Greater Than >
+ "LT": sympy.Lt, # Less Than <
+ "GE": sympy.Ge, # Greater or Equal >=
+ "LE": sympy.Le, # Less or Equal <=
+ "NE": sympy.Ne # Not Equal <> (sympy.Ne maneja esto)
+ }
+ sympy_relation_func = op_map.get(instr_type_original.upper())
+ if not sympy_relation_func:
+ instruction["scl"] = f"// ERROR: Tipo de comparación no soportado para SymPy: {instr_type_original}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # Obtener operandos como expresiones SymPy o constantes/strings
+ in1_info = instruction["inputs"].get("in1")
+ in2_info = instruction["inputs"].get("in2")
+ op1_sympy = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
+ op2_sympy = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener 'pre' (RLO anterior) como expresión SymPy
+ pre_input = instruction["inputs"].get("pre") # Asumiendo que 'pre' es la entrada RLO
+ sympy_pre_rlo = get_sympy_representation(pre_input, network_id, sympy_map, symbol_manager) if pre_input else sympy.true
+
+ # Verificar dependencias
+ if op1_sympy is None or op2_sympy is None or sympy_pre_rlo is None:
+ # print(f"DEBUG Comparison {instr_uid}: Dependency not ready")
+ return False
+
+ # Crear la expresión de comparación SymPy
+ try:
+ # Convertir constantes string a número si es posible (Sympy puede necesitarlo)
+ # Esto es heurístico y puede fallar. Mejor si los tipos son conocidos.
+ op1_eval = sympy.sympify(op1_sympy) if isinstance(op1_sympy, str) else op1_sympy
+ op2_eval = sympy.sympify(op2_sympy) if isinstance(op2_sympy, str) else op2_sympy
+ comparison_expr = sympy_relation_func(op1_eval, op2_eval)
+ except (SyntaxError, TypeError, ValueError) as e:
+ print(f"Error creating SymPy comparison for {instr_uid}: {e}")
+ instruction["scl"] = f"// ERROR creando expr SymPy Comparison {instr_uid}: {e}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # Guardar resultado en el mapa para 'out' (es una expresión booleana SymPy)
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = comparison_expr
+
+ # Guardar el RLO de entrada ('pre') como ENO en el mapa SymPy
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_pre_rlo
+
+ # Marcar como procesado, SCL principal es solo comentario
+ instruction["scl"] = f"// SymPy Comparison {instr_type_original}: {comparison_expr}" # Comentario opcional
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ return True
+
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para los comparadores (excepto EQ, que debe ser similar)."""
+ return [
+ {'type_name': 'gt', 'processor_func': process_comparison, 'priority': 2},
+ {'type_name': 'lt', 'processor_func': process_comparison, 'priority': 2},
+ {'type_name': 'ge', 'processor_func': process_comparison, 'priority': 2},
+ {'type_name': 'le', 'processor_func': process_comparison, 'priority': 2},
+ {'type_name': 'ne', 'processor_func': process_comparison, 'priority': 2}
+ # Asegúrate de tener también un procesador para 'eq' usando sympy.Eq
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_contact.py b/ToUpload/processors/process_contact.py
new file mode 100644
index 0000000..ec5b0ab
--- /dev/null
+++ b/ToUpload/processors/process_contact.py
@@ -0,0 +1,60 @@
+# processors/process_contact.py
+import sympy
+from .processor_utils import get_sympy_representation, format_variable_name # Use new util
+from .symbol_manager import SymbolManager, extract_plc_variable_name # Need symbol manager access
+
+# Define SCL_SUFFIX or import if needed globally
+SCL_SUFFIX = "_sympy_processed" # Indicate processing type
+
+def process_contact(instruction, network_id, sympy_map, symbol_manager, data): # Pass symbol_manager
+ """Genera la expresión SymPy para Contact (normal o negado)."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Contact")
+
+ # Check if already processed with the new method
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ is_negated = instruction.get("negated_pins", {}).get("operand", False)
+
+ # Get incoming SymPy expression (RLO)
+ in_input = instruction["inputs"].get("in")
+ sympy_expr_in = get_sympy_representation(in_input, network_id, sympy_map, symbol_manager)
+
+ # Get operand SymPy Symbol
+ operand_info = instruction["inputs"].get("operand")
+ operand_plc_name = extract_plc_variable_name(operand_info)
+ sympy_symbol_operand = symbol_manager.get_symbol(operand_plc_name) if operand_plc_name else None
+
+ # Check dependencies
+ if sympy_expr_in is None or sympy_symbol_operand is None:
+ # print(f"DEBUG Contact {instr_uid}: Dependency not ready (In: {sympy_expr_in is not None}, Op: {sympy_symbol_operand is not None})")
+ return False # Dependencies not ready
+
+ # Apply negation using SymPy
+ current_term = sympy.Not(sympy_symbol_operand) if is_negated else sympy_symbol_operand
+
+ # Combine with previous RLO using SymPy
+ # Simplify common cases: TRUE AND X -> X
+ if sympy_expr_in == sympy.true:
+ sympy_expr_out = current_term
+ else:
+ # Could add FALSE AND X -> FALSE optimization here too
+ sympy_expr_out = sympy.And(sympy_expr_in, current_term)
+
+ # Store the resulting SymPy expression object in the map
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = sympy_expr_out
+
+ # Mark instruction as processed (SCL field is now less relevant here)
+ instruction["scl"] = f"// SymPy Contact: {sympy_expr_out}" # Optional debug comment
+ instruction["type"] = instr_type_original + SCL_SUFFIX # Use the new suffix
+ # Contact doesn't usually have ENO, it modifies the RLO ('out')
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el procesador Contact."""
+ # Ensure 'data' argument is added if needed by the processor function signature change
+ return {'type_name': 'contact', 'processor_func': process_contact, 'priority': 1}
\ No newline at end of file
diff --git a/ToUpload/processors/process_convert.py b/ToUpload/processors/process_convert.py
new file mode 100644
index 0000000..0825c21
--- /dev/null
+++ b/ToUpload/processors/process_convert.py
@@ -0,0 +1,90 @@
+# processors/process_convert.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_convert(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera SCL para Convert, tratando la conversión como una asignación."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Convert")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener EN y IN
+ en_input = instruction["inputs"].get("en")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+ in_info = instruction["inputs"].get("in")
+ sympy_or_const_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener destino SCL
+ target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
+
+ # Verificar dependencias
+ if sympy_en_expr is None or sympy_or_const_in is None or target_scl_name is None:
+ return False
+
+ # Convertir la entrada (SymPy o Constante) a SCL
+ # La simplificación aquí no suele aplicar a la conversión en sí,
+ # pero sí podría aplicar a la condición EN.
+ input_scl = sympy_expr_to_scl(sympy_or_const_in, symbol_manager)
+
+ # Determinar el tipo de destino (esto sigue siendo un desafío sin info completa)
+ # Usaremos funciones de conversión SCL explícitas si podemos inferirlas.
+ target_type_hint = instruction.get("template_values", {}).get("destType", "").upper() # Ejemplo
+ source_type_hint = "" # Necesitaríamos info del tipo de origen
+ conversion_func_name = None
+
+ # Heurística MUY básica (necesita mejorar con info de tipos real)
+ if target_type_hint and source_type_hint and target_type_hint != source_type_hint:
+ conversion_func_name = f"{source_type_hint}_TO_{target_type_hint}"
+
+ # Generar SCL Core
+ if conversion_func_name:
+ # Usar función explícita si la inferimos
+ scl_core = f"{target_scl_name} := {conversion_func_name}({input_scl});"
+ else:
+ # Asignación directa (MOVE implícito) si no hay conversión clara
+ # ADVERTENCIA: Esto puede causar errores de tipo en el PLC si los tipos no coinciden.
+ scl_core = f"{target_scl_name} := {input_scl};"
+ if target_type_hint: # Añadir comentario si al menos conocemos el destino
+ scl_core += f" // TODO: Verify implicit conversion to {target_type_hint}"
+
+
+ # Aplicar Condición EN (Simplificando EN)
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for Convert {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # Actualizar instrucción y mapa
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar valor de salida (el contenido del destino) y ENO
+ map_key_out = (network_id, instr_uid, "out")
+ # Guardar el *nombre* SCL del destino en el mapa, ya que contiene el valor
+ # O podríamos crear un símbolo SymPy para ello si fuera necesario aguas abajo? Por ahora, string.
+ sympy_map[map_key_out] = target_scl_name
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el procesador Convert."""
+ return {'type_name': 'convert', 'processor_func': process_convert, 'priority': 4}
\ No newline at end of file
diff --git a/ToUpload/processors/process_counter.py b/ToUpload/processors/process_counter.py
new file mode 100644
index 0000000..693355a
--- /dev/null
+++ b/ToUpload/processors/process_counter.py
@@ -0,0 +1,110 @@
+# processors/process_counter.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_counter(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL para Contadores (CTU, CTD, CTUD).
+ Requiere datos de instancia (DB o STAT).
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "") # CTU, CTD, CTUD
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # 1. Definir pines de entrada esperados
+ input_pins_map = {
+ "CTU": ["CU", "R", "PV"],
+ "CTD": ["CD", "LD", "PV"],
+ "CTUD": ["CU", "CD", "R", "LD", "PV"]
+ }
+ input_pins = input_pins_map.get(instr_type_original.upper())
+ if not input_pins:
+ instruction["scl"] = f"// ERROR: Tipo de contador no soportado: {instr_type_original}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # 2. Procesar Parámetros de Entrada
+ scl_call_params = []
+ dependencies_resolved = True
+ optional_pins = {"R", "LD"} # Estos pueden no estar conectados
+
+ for pin in input_pins:
+ pin_info = instruction["inputs"].get(pin)
+ if pin_info: # Si el pin está definido en el JSON
+ source_sympy_or_const = get_sympy_representation(pin_info, network_id, sympy_map, symbol_manager)
+ if source_sympy_or_const is None:
+ # print(f"DEBUG Counter {instr_uid}: Input param '{pin}' dependency not ready.")
+ dependencies_resolved = False
+ break
+ # Convertir a SCL para la llamada (sin simplificar aquí)
+ param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
+ pin_name_scl = format_variable_name(pin) # Formatear nombre del parámetro
+ scl_call_params.append(f"{pin_name_scl} := {param_scl_value}")
+ elif pin not in optional_pins: # Si falta un pin requerido
+ print(f"Error: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}.")
+ instruction["scl"] = f"// ERROR: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}."
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ if not dependencies_resolved:
+ return False
+
+ # 3. Obtener Nombre de Instancia
+ # Asumiendo que x1 o una fase previa llena 'instance_db' si es un FB multi-instancia
+ instance_name_raw = instruction.get("instance_db")
+ if not instance_name_raw:
+ # Asumiendo que es STAT si no hay DB instancia explícito (requiere declaración en x3)
+ instance_name_raw = instruction.get("instance_name") # Buscar nombre directo si x1 lo provee
+ if not instance_name_raw:
+ instance_name_raw = f"#CTR_INSTANCE_{instr_uid}" # Placeholder final
+ print(f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_name_raw}'.")
+ instance_name_scl = format_variable_name(instance_name_raw)
+
+ # 4. Generar la llamada SCL
+ param_string = ", ".join(scl_call_params)
+ scl_call = f"{instance_name_scl}({param_string}); // TODO: Declarar {instance_name_scl} : {instr_type_original.upper()}; en VAR_STAT o VAR"
+
+ # Contadores no suelen tener EN/ENO explícito en LAD, se asume siempre habilitado
+ instruction["scl"] = scl_call # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # 4. Actualizar sympy_map para las salidas (QU, QD, CV)
+ output_pins_map = {
+ "CTU": ["QU", "CV"],
+ "CTD": ["QD", "CV"],
+ "CTUD": ["QU", "QD", "CV"]
+ }
+ output_pins = output_pins_map.get(instr_type_original.upper(), [])
+
+ for pin in output_pins:
+ map_key = (network_id, instr_uid, pin)
+ output_scl_access = f"{instance_name_scl}.{pin.upper()}"
+ if pin.upper() in ["QU", "QD"]: # These are boolean outputs
+ # *** Store SymPy Symbol for boolean outputs QU/QD ***
+ sympy_out_symbol = symbol_manager.get_symbol(output_scl_access)
+ if sympy_out_symbol:
+ sympy_map[map_key] = sympy_out_symbol # Store SYMBOL
+ else:
+ print(f"Error: Could not create symbol for {output_scl_access} in {instr_type_original} {instr_uid}")
+ sympy_map[map_key] = None
+ else:
+ # For non-boolean (like CV - count value), store SCL access string
+ sympy_map[map_key] = output_scl_access
+
+ return True
+
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para los contadores CTU, CTD, CTUD."""
+ return [
+ {'type_name': 'ctu', 'processor_func': process_counter, 'priority': 5},
+ {'type_name': 'ctd', 'processor_func': process_counter, 'priority': 5},
+ {'type_name': 'ctud', 'processor_func': process_counter, 'priority': 5}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_edge_detector.py b/ToUpload/processors/process_edge_detector.py
new file mode 100644
index 0000000..6ff2fd3
--- /dev/null
+++ b/ToUpload/processors/process_edge_detector.py
@@ -0,0 +1,85 @@
+# processors/process_edge_detector.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_edge_detector(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera la expresión SymPy para el pulso de PBox (P_TRIG) o NBox (N_TRIG).
+ Guarda la expresión SymPy del pulso en sympy_map['out'].
+ Genera y guarda el SCL para la actualización de memoria en '_edge_mem_update_scl'.
+ El campo 'scl' principal se deja casi vacío/comentario.
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "") # PBox o NBox
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # 1. Obtener CLK (como SymPy expr) y MemBit (como SymPy Symbol)
+ clk_input = instruction["inputs"].get("in")
+ mem_bit_input = instruction["inputs"].get("bit")
+
+ sympy_clk_expr = get_sympy_representation(clk_input, network_id, sympy_map, symbol_manager)
+ mem_bit_plc_name = extract_plc_variable_name(mem_bit_input)
+ sympy_mem_bit_symbol = symbol_manager.get_symbol(mem_bit_plc_name) if mem_bit_plc_name else None
+
+ # 2. Verificar dependencias
+ if sympy_clk_expr is None: return False
+ if sympy_mem_bit_symbol is None:
+ err_msg = f"MemBit no resuelto o no es variable para {instr_type_original} UID {instr_uid}"
+ print(f"Error: {err_msg}")
+ instruction["scl"] = f"// ERROR: {err_msg}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # 3. Generar Lógica SymPy del *pulso*
+ result_pulse_sympy_expr = sympy.false # Default
+ scl_comment_prefix = ""
+ if instr_type_original.upper() == "PBOX": # P_TRIG
+ result_pulse_sympy_expr = sympy.And(sympy_clk_expr, sympy.Not(sympy_mem_bit_symbol))
+ scl_comment_prefix = "P_TRIG"
+ elif instr_type_original.upper() == "NBOX": # N_TRIG
+ result_pulse_sympy_expr = sympy.And(sympy.Not(sympy_clk_expr), sympy_mem_bit_symbol)
+ scl_comment_prefix = "N_TRIG"
+ else: # Error
+ instruction["scl"] = f"// ERROR: Tipo de flanco inesperado {instr_type_original}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # 4. Generar el SCL para la actualización del bit de memoria
+ # Necesitamos la representación SCL de la entrada CLK
+ clk_scl_str = sympy_expr_to_scl(sympy_clk_expr, symbol_manager)
+ # Usamos el nombre PLC original formateado para el bit de memoria
+ mem_bit_scl_name = format_variable_name(mem_bit_plc_name)
+ scl_mem_update = f"{mem_bit_scl_name} := {clk_scl_str};"
+ scl_comment_for_update = f"// {scl_comment_prefix}({clk_scl_str}) - Mem: {mem_bit_scl_name}"
+
+ # 5. Almacenar Resultados
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = result_pulse_sympy_expr # Guardar EXPRESIÓN SymPy del pulso
+
+ # Guardar SCL de actualización + Comentario en campo temporal
+ instruction['_edge_mem_update_scl'] = f"{scl_mem_update} {scl_comment_for_update}"
+
+ # Marcar como procesado, SCL principal es solo comentario
+ instruction['scl'] = f"// {instr_type_original} SymPy processed, logic in consumer"
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # 6. Propagar ENO (es la expresión SymPy de CLK)
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_clk_expr
+
+ return True
+
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la info para los detectores de flanco PBox y NBox."""
+ return [
+ {'type_name': 'pbox', 'processor_func': process_edge_detector, 'priority': 2},
+ {'type_name': 'nbox', 'processor_func': process_edge_detector, 'priority': 2}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_eq.py b/ToUpload/processors/process_eq.py
new file mode 100644
index 0000000..3b16a32
--- /dev/null
+++ b/ToUpload/processors/process_eq.py
@@ -0,0 +1,64 @@
+# processors/process_eq.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades de SymPy
+from .processor_utils import get_sympy_representation, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed" # Nuevo sufijo
+
+def process_eq(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera la expresión SymPy para el comparador de igualdad (EQ).
+ El resultado se propaga por sympy_map['out'].
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Eq")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener operandos como expresiones SymPy o constantes/strings
+ in1_info = instruction["inputs"].get("in1")
+ in2_info = instruction["inputs"].get("in2")
+ op1_sympy = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
+ op2_sympy = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener 'pre' (RLO anterior) como expresión SymPy
+ pre_input = instruction["inputs"].get("pre") # Asumir 'pre' como entrada RLO estándar
+ sympy_pre_rlo = get_sympy_representation(pre_input, network_id, sympy_map, symbol_manager) if pre_input else sympy.true
+
+ # Verificar dependencias
+ if op1_sympy is None or op2_sympy is None or sympy_pre_rlo is None:
+ # print(f"DEBUG EQ {instr_uid}: Dependency not ready")
+ return False
+
+ # Crear la expresión de igualdad SymPy
+ try:
+ # sympify puede ser necesario si los operandos son strings de constantes
+ op1_eval = sympy.sympify(op1_sympy) if isinstance(op1_sympy, str) else op1_sympy
+ op2_eval = sympy.sympify(op2_sympy) if isinstance(op2_sympy, str) else op2_sympy
+ comparison_expr = sympy.Eq(op1_eval, op2_eval) # Eq para igualdad
+ except (SyntaxError, TypeError, ValueError) as e:
+ print(f"Error creating SymPy equality for {instr_uid}: {e}")
+ instruction["scl"] = f"// ERROR creando expr SymPy EQ {instr_uid}: {e}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # Guardar resultado (Expresión SymPy booleana) en el mapa para 'out'
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = comparison_expr
+
+ # Guardar el RLO de entrada ('pre') como ENO en el mapa SymPy
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_pre_rlo
+
+ # Marcar como procesado, SCL principal es solo comentario
+ instruction["scl"] = f"// SymPy EQ: {comparison_expr}" # Comentario opcional
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el comparador de igualdad (EQ)."""
+ return {'type_name': 'eq', 'processor_func': process_eq, 'priority': 2}
\ No newline at end of file
diff --git a/ToUpload/processors/process_math.py b/ToUpload/processors/process_math.py
new file mode 100644
index 0000000..b181a12
--- /dev/null
+++ b/ToUpload/processors/process_math.py
@@ -0,0 +1,90 @@
+# processors/process_math.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL para operaciones matemáticas (SUB, MUL, DIV), simplificando EN.
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "") # SUB, MUL, DIV
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Mapa de tipos a operadores SCL string
+ op_map = {"SUB": "-", "MUL": "*", "DIV": "/"}
+ scl_operator = op_map.get(instr_type_original.upper())
+ if not scl_operator:
+ instruction["scl"] = f"// ERROR: Operación matemática no soportada: {instr_type_original}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # Obtener EN (SymPy), IN1, IN2 (SymPy o Constante/String)
+ en_input = instruction["inputs"].get("en")
+ in1_info = instruction["inputs"].get("in1")
+ in2_info = instruction["inputs"].get("in2")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+ op1_sympy_or_const = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
+ op2_sympy_or_const = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener destino SCL
+ target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
+
+ # Verificar dependencias
+ if sympy_en_expr is None or op1_sympy_or_const is None or op2_sympy_or_const is None or target_scl_name is None:
+ return False
+
+ # Convertir operandos SymPy/Constante a SCL strings
+ op1_scl = sympy_expr_to_scl(op1_sympy_or_const, symbol_manager)
+ op2_scl = sympy_expr_to_scl(op2_sympy_or_const, symbol_manager)
+
+ # Añadir paréntesis si contienen operadores (más seguro)
+ # La función sympy_expr_to_scl debería idealmente manejar esto, pero doble chequeo simple:
+ op1_scl_formatted = f"({op1_scl})" if re.search(r'[+\-*/ ]', op1_scl) else op1_scl
+ op2_scl_formatted = f"({op2_scl})" if re.search(r'[+\-*/ ]', op2_scl) else op2_scl
+
+ # Generar SCL Core
+ scl_core = f"{target_scl_name} := {op1_scl_formatted} {scl_operator} {op2_scl_formatted};"
+
+ # Aplicar Condición EN (Simplificando EN)
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for {instr_type_original} {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # Actualizar instrucción y mapa
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar valor de salida (nombre SCL del destino) y ENO (expresión SymPy)
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve info para SUB, MUL, DIV."""
+ return [
+ {'type_name': 'sub', 'processor_func': process_math, 'priority': 4},
+ {'type_name': 'mul', 'processor_func': process_math, 'priority': 4},
+ {'type_name': 'div', 'processor_func': process_math, 'priority': 4}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_mod.py b/ToUpload/processors/process_mod.py
new file mode 100644
index 0000000..03c0ef1
--- /dev/null
+++ b/ToUpload/processors/process_mod.py
@@ -0,0 +1,75 @@
+# processors/process_mod.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re # Importar re si no estaba
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_mod(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera SCL para Modulo (MOD), simplificando EN."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Mod")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener EN (SymPy), IN1, IN2 (SymPy o Constante/String)
+ en_input = instruction["inputs"].get("en")
+ in1_info = instruction["inputs"].get("in1")
+ in2_info = instruction["inputs"].get("in2")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+ op1_sympy_or_const = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
+ op2_sympy_or_const = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener destino SCL
+ target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
+
+ # Verificar dependencias
+ if sympy_en_expr is None or op1_sympy_or_const is None or op2_sympy_or_const is None or target_scl_name is None:
+ return False
+
+ # Convertir operandos SymPy/Constante a SCL strings
+ op1_scl = sympy_expr_to_scl(op1_sympy_or_const, symbol_manager)
+ op2_scl = sympy_expr_to_scl(op2_sympy_or_const, symbol_manager)
+
+ # Añadir paréntesis si contienen operadores
+ op1_scl_formatted = f"({op1_scl})" if re.search(r'[+\-*/ ]', op1_scl) else op1_scl
+ op2_scl_formatted = f"({op2_scl})" if re.search(r'[+\-*/ ]', op2_scl) else op2_scl
+
+ # Generar SCL Core
+ scl_core = f"{target_scl_name} := {op1_scl_formatted} MOD {op2_scl_formatted};"
+
+ # Aplicar Condición EN (Simplificando EN)
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for {instr_type_original} {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # Actualizar instrucción y mapa
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar valor de salida (nombre SCL del destino) y ENO (expresión SymPy)
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
+ map_key_eno = (network_id, instr_uid, "eno")
+ sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la operación Modulo."""
+ return {'type_name': 'mod', 'processor_func': process_mod, 'priority': 4}
\ No newline at end of file
diff --git a/ToUpload/processors/process_move.py b/ToUpload/processors/process_move.py
new file mode 100644
index 0000000..b950797
--- /dev/null
+++ b/ToUpload/processors/process_move.py
@@ -0,0 +1,75 @@
+# processors/process_move.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re # Importar re
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_move(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera SCL para Move, simplificando la condición EN."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Move")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener EN (SymPy) e IN (SymPy o Constante/String)
+ en_input = instruction["inputs"].get("en")
+ in_info = instruction["inputs"].get("in")
+ sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
+ input_sympy_or_const = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener destino SCL (requiere destino explícito para MOVE)
+ target_scl_name = get_target_scl_name(instruction, "out1", network_id, default_to_temp=False)
+ if target_scl_name is None:
+ target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=False)
+
+ # Verificar dependencias
+ if sympy_en_expr is None or input_sympy_or_const is None:
+ return False
+ if target_scl_name is None:
+ print(f"Error: MOVE {instr_uid} sin destino claro en 'out' o 'out1'.")
+ instruction["scl"] = f"// ERROR: MOVE {instr_uid} sin destino claro."
+ instruction["type"] = instr_type_original + "_error"
+ return True # Procesado con error
+
+ # Convertir la entrada (SymPy o Constante) a SCL string
+ input_scl = sympy_expr_to_scl(input_sympy_or_const, symbol_manager)
+
+ # Generar SCL Core
+ scl_core = f"{target_scl_name} := {input_scl};"
+
+ # Aplicar Condición EN (Simplificando EN)
+ scl_final = ""
+ if sympy_en_expr != sympy.true:
+ try:
+ #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True)
+ simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying EN for {instr_type_original} {instr_uid}: {e}")
+ simplified_en_expr = sympy_en_expr # Fallback
+ en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
+
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {en_condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ scl_final = scl_core
+
+ # Actualizar instrucción y mapa
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # Propagar valor de salida (nombre SCL del destino) y ENO (expresión SymPy)
+ # Asumiendo que out y out1 deben propagar el mismo valor
+ sympy_map[(network_id, instr_uid, "out")] = target_scl_name
+ sympy_map[(network_id, instr_uid, "out1")] = target_scl_name
+ sympy_map[(network_id, instr_uid, "eno")] = sympy_en_expr
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la operación Move."""
+ return {'type_name': 'move', 'processor_func': process_move, 'priority': 3}
\ No newline at end of file
diff --git a/ToUpload/processors/process_not.py b/ToUpload/processors/process_not.py
new file mode 100644
index 0000000..b721aee
--- /dev/null
+++ b/ToUpload/processors/process_not.py
@@ -0,0 +1,54 @@
+# processors/process_not.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed" # Nuevo sufijo
+
+def process_not(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera la expresión SymPy para la inversión lógica NOT."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "Not")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener entrada como expresión SymPy
+ in_info = instruction["inputs"].get("in")
+ sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+
+ # Verificar dependencias
+ if sympy_expr_in is None:
+ # print(f"DEBUG Not {instr_uid}: Dependency not ready")
+ return False
+
+ # Crear la expresión NOT de SymPy
+ try:
+ not_expr = sympy.Not(sympy_expr_in)
+ # ¿Simplificar aquí? NOT(NOT A) -> A; NOT(TRUE) -> FALSE, etc.
+ # simplify_logic podría hacer esto, pero puede ser costoso en cada paso.
+ # SymPy podría manejar simplificaciones básicas automáticamente.
+ # Opcional: not_expr = sympy.simplify_logic(not_expr)
+ except Exception as e:
+ print(f"Error creating SymPy Not for {instr_uid}: {e}")
+ instruction["scl"] = f"// ERROR creando expr SymPy NOT {instr_uid}: {e}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # Guardar resultado (Expresión SymPy) en el mapa para 'out'
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = not_expr
+
+ # Marcar como procesado, SCL principal es solo comentario
+ instruction["scl"] = f"// SymPy NOT: {not_expr}" # Comentario opcional
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ # NOT no tiene EN/ENO explícito en LAD, modifica el RLO ('out')
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la operación Not."""
+ return {'type_name': 'not', 'processor_func': process_not, 'priority': 1}
\ No newline at end of file
diff --git a/ToUpload/processors/process_o.py b/ToUpload/processors/process_o.py
new file mode 100644
index 0000000..446817f
--- /dev/null
+++ b/ToUpload/processors/process_o.py
@@ -0,0 +1,69 @@
+# processors/process_o.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed" # Nuevo sufijo
+
+def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera la expresión SymPy para la operación lógica O (OR)."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "O")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Buscar todas las entradas 'in', 'in1', 'in2', ...
+ input_pins = sorted([pin for pin in instruction.get("inputs", {}) if pin.startswith("in")])
+
+ if not input_pins:
+ print(f"Error: O {instr_uid} sin pines de entrada (inX).")
+ instruction["scl"] = f"// ERROR: O {instr_uid} sin pines inX"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ sympy_parts = []
+ all_resolved = True
+ for pin in input_pins:
+ input_info = instruction["inputs"][pin]
+ sympy_expr = get_sympy_representation(input_info, network_id, sympy_map, symbol_manager)
+
+ if sympy_expr is None:
+ all_resolved = False
+ # print(f"DEBUG: O {instr_uid} esperando pin {pin}")
+ break # Salir si una dependencia no está lista
+
+ # Optimización: No incluir FALSE en un OR
+ if sympy_expr != sympy.false:
+ sympy_parts.append(sympy_expr)
+
+ if not all_resolved:
+ return False # Esperar dependencias
+
+ # Construir la expresión OR de SymPy
+ result_sympy_expr = sympy.false # Valor por defecto si no hay entradas válidas o todas son FALSE
+ if sympy_parts:
+ # Usar sympy.Or para construir la expresión
+ result_sympy_expr = sympy.Or(*sympy_parts)
+ # Simplificar casos obvios como OR(X) -> X, OR(X, TRUE) -> TRUE
+ # simplify_logic aquí puede ser prematuro, mejor al final.
+ # Pero Or() podría simplificar automáticamente OR(X) -> X.
+ # Opcional: result_sympy_expr = sympy.simplify_logic(result_sympy_expr)
+
+
+ # Guardar la expresión SymPy resultante en el mapa para 'out'
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_map[map_key_out] = result_sympy_expr
+
+ # Marcar como procesado, SCL principal es solo comentario
+ instruction["scl"] = f"// SymPy O: {result_sympy_expr}" # Comentario opcional
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ # La instrucción 'O' no tiene ENO propio, propaga el resultado por 'out'
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la operación lógica O (OR)."""
+ return {'type_name': 'o', 'processor_func': process_o, 'priority': 1}
\ No newline at end of file
diff --git a/ToUpload/processors/process_rcoil.py b/ToUpload/processors/process_rcoil.py
new file mode 100644
index 0000000..9824b83
--- /dev/null
+++ b/ToUpload/processors/process_rcoil.py
@@ -0,0 +1,74 @@
+# processors/process_rcoil.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_rcoil(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data ):
+ """Genera SCL para Reset Coil (RCoil), simplificando la condición."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "RCoil")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener condición de entrada (SymPy expr)
+ in_info = instruction["inputs"].get("in")
+ sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener operando (nombre SCL del destino)
+ target_scl_name = get_target_scl_name(instruction, "operand", network_id, default_to_temp=False) # RCoil necesita destino explícito
+
+ # Verificar dependencias
+ if sympy_expr_in is None: return False
+ if target_scl_name is None:
+ print(f"Error: RCoil {instr_uid} operando no es variable o falta info.")
+ instruction["scl"] = f"// ERROR: RCoil {instr_uid} operando no es variable."
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # No hacer nada si la condición es FALSE constante
+ if sympy_expr_in == sympy.false:
+ instruction["scl"] = f"// RCoil {instr_uid} con condición FALSE constante, optimizado."
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ return True
+
+ # Generar SCL Core (Reset)
+ scl_core = f"{target_scl_name} := FALSE;"
+
+ # Aplicar Condición IF si no es TRUE constante
+ scl_final = ""
+ if sympy_expr_in != sympy.true:
+ # Simplificar la condición ANTES de convertirla a SCL
+ try:
+ #simplified_expr = sympy.simplify_logic(sympy_expr_in, force=True)
+ simplified_expr = sympy.logic.boolalg.to_dnf(sympy_expr_in, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying condition for RCoil {instr_uid}: {e}")
+ simplified_expr = sympy_expr_in # Fallback
+ condition_scl = sympy_expr_to_scl(simplified_expr, symbol_manager)
+
+ # Evitar IF TRUE THEN...
+ if condition_scl == "TRUE":
+ scl_final = scl_core
+ else:
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ # Condición es TRUE constante
+ scl_final = scl_core
+
+ # Actualizar instrucción
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ # RCoil no tiene salida lógica para propagar en sympy_map
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la bobina Reset (RCoil)."""
+ return {'type_name': 'rcoil', 'processor_func': process_rcoil, 'priority': 3}
\ No newline at end of file
diff --git a/ToUpload/processors/process_scoil.py b/ToUpload/processors/process_scoil.py
new file mode 100644
index 0000000..4411f51
--- /dev/null
+++ b/ToUpload/processors/process_scoil.py
@@ -0,0 +1,74 @@
+# processors/process_scoil.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+import re
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
+from .symbol_manager import SymbolManager
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_scoil(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """Genera SCL para Set Coil (SCoil), simplificando la condición."""
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "SCoil")
+ if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
+ return False
+
+ # Obtener condición de entrada (SymPy expr)
+ in_info = instruction["inputs"].get("in")
+ sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+
+ # Obtener operando (nombre SCL del destino)
+ target_scl_name = get_target_scl_name(instruction, "operand", network_id, default_to_temp=False) # SCoil necesita destino
+
+ # Verificar dependencias
+ if sympy_expr_in is None: return False
+ if target_scl_name is None:
+ print(f"Error: SCoil {instr_uid} operando no es variable o falta info.")
+ instruction["scl"] = f"// ERROR: SCoil {instr_uid} operando no es variable."
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # No hacer nada si la condición es FALSE constante
+ if sympy_expr_in == sympy.false:
+ instruction["scl"] = f"// SCoil {instr_uid} con condición FALSE constante, optimizado."
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ return True
+
+ # Generar SCL Core (Set)
+ scl_core = f"{target_scl_name} := TRUE;"
+
+ # Aplicar Condición IF si no es TRUE constante
+ scl_final = ""
+ if sympy_expr_in != sympy.true:
+ # Simplificar la condición ANTES de convertirla a SCL
+ try:
+ #simplified_expr = sympy.simplify_logic(sympy_expr_in, force=True)
+ simplified_expr = sympy.logic.boolalg.to_dnf(sympy_expr_in, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying condition for SCoil {instr_uid}: {e}")
+ simplified_expr = sympy_expr_in # Fallback
+ condition_scl = sympy_expr_to_scl(simplified_expr, symbol_manager)
+
+ # Evitar IF TRUE THEN...
+ if condition_scl == "TRUE":
+ scl_final = scl_core
+ else:
+ indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
+ scl_final = f"IF {condition_scl} THEN\n{indented_core}\nEND_IF;"
+ else:
+ # Condición es TRUE constante
+ scl_final = scl_core
+
+ # Actualizar instrucción
+ instruction["scl"] = scl_final # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+ # SCoil no tiene salida lógica para propagar en sympy_map
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para la bobina Set (SCoil)."""
+ return {'type_name': 'scoil', 'processor_func': process_scoil, 'priority': 3}
\ No newline at end of file
diff --git a/ToUpload/processors/process_sd.py b/ToUpload/processors/process_sd.py
new file mode 100644
index 0000000..72d51eb
--- /dev/null
+++ b/ToUpload/processors/process_sd.py
@@ -0,0 +1,77 @@
+# processors/process_sd.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_sd(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL para Temporizador On-Delay (Sd -> TON).
+ Requiere datos de instancia (DB o STAT/TEMP).
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = "Sd" # Tipo original LAD
+ if instruction.get("type","").endswith(SCL_SUFFIX) or "_error" in instruction.get("type",""):
+ return False
+
+ # 1. Obtener Inputs: s (start), tv (time value), timer (instance)
+ s_info = instruction["inputs"].get("s")
+ tv_info = instruction["inputs"].get("tv")
+ timer_instance_info = instruction["inputs"].get("timer")
+
+ sympy_s_expr = get_sympy_representation(s_info, network_id, sympy_map, symbol_manager)
+ # tv suele ser constante, pero lo obtenemos igual
+ sympy_or_const_tv = get_sympy_representation(tv_info, network_id, sympy_map, symbol_manager)
+ # Obtener el nombre de la INSTANCIA (no su valor)
+ instance_plc_name = extract_plc_variable_name(timer_instance_info)
+
+ # Verificar dependencias
+ if sympy_s_expr is None or sympy_or_const_tv is None: return False
+ if instance_plc_name is None:
+ print(f"Error: Sd {instr_uid} sin variable de instancia 'timer'.")
+ instance_plc_name = f"#TON_INSTANCE_{instr_uid}" # Placeholder con error implícito
+ print(f"Advertencia: Usando placeholder '{instance_plc_name}'. ¡Declarar en SCL!")
+ # Podríamos marcar como error, pero intentamos generar algo
+ # instruction["type"] = instr_type_original + "_error"
+ # return True
+
+ # Formatear nombre de instancia
+ instance_name_scl = format_variable_name(instance_plc_name)
+
+ # Convertir entradas SymPy/Constante a SCL strings
+ s_scl = sympy_expr_to_scl(sympy_s_expr, symbol_manager)
+ tv_scl = sympy_expr_to_scl(sympy_or_const_tv, symbol_manager)
+
+ # Generar la llamada SCL (TON usa IN, PT)
+ # Ignoramos 'r' (reset) de Sd
+ scl_call = f"{instance_name_scl}(IN := {s_scl}, PT := {tv_scl}); // TODO: Declarar {instance_name_scl} : TON;"
+
+ # Actualizar instrucción
+ instruction["scl"] = scl_call # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # 7. Actualizar sympy_map para las salidas Q y RT
+ map_key_q = (network_id, instr_uid, "q")
+ q_output_scl_access = f"{instance_name_scl}.Q" # SCL string to access output
+ # *** GET/CREATE AND STORE SYMBOL for boolean output Q ***
+ sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access)
+ if sympy_q_symbol:
+ sympy_map[map_key_q] = sympy_q_symbol # STORE THE SYMBOL OBJECT
+ else:
+ print(f"Error: Could not create symbol for {q_output_scl_access} in Sd {instr_uid}")
+ sympy_map[map_key_q] = None # Indicate error/unresolved
+
+ map_key_rt = (network_id, instr_uid, "rt")
+ # ET is TIME, store SCL access string
+ sympy_map[map_key_rt] = f"{instance_name_scl}.ET"
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la información para el temporizador On-Delay (Sd -> TON)."""
+ return {'type_name': 'sd', 'processor_func': process_sd, 'priority': 5}
\ No newline at end of file
diff --git a/ToUpload/processors/process_se.py b/ToUpload/processors/process_se.py
new file mode 100644
index 0000000..cb44741
--- /dev/null
+++ b/ToUpload/processors/process_se.py
@@ -0,0 +1,112 @@
+# processors/process_se.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_se(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL para Temporizador de Pulso (Se -> TP) o SdCoil (-> TON).
+ Usa SymPy para entradas y almacena Symbol para salida Q.
+ """
+ instr_uid = instruction["instruction_uid"]
+ # Obtener tipo original (antes de añadir sufijo) para determinar comportamiento
+ instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX,"").replace("_error","") # Se o SdCoil
+ current_type = instruction.get("type","") # Tipo actual para chequeo inicial
+ if current_type.endswith(SCL_SUFFIX) or "_error" in current_type:
+ return False
+
+ # Determinar el tipo de instrucción SCL y pines de entrada/salida correctos
+ scl_timer_type = "TP"
+ pin_in = "s" # Pin de entrada para Se
+ pin_time = "tv" # Pin de valor de tiempo para Se
+ pin_instance = "timer" # Pin donde se conecta la instancia para Se
+ pin_out_q = "q" # Pin de salida Q para Se
+ pin_out_time = "rt" # Pin de tiempo restante para Se -> TP.ET
+
+ # Ajustar pines si el tipo original era SdCoil
+ if instr_type_original == "SdCoil":
+ scl_timer_type = "TON" # SdCoil es funcionalmente un TON
+ pin_in = "in" # SdCoil usa 'in'
+ pin_time = "value" # SdCoil usa 'value'
+ pin_instance = "operand" # SdCoil usa 'operand' como instancia/variable de salida
+ pin_out_q = "out" # SdCoil usa 'out' como pin de salida Q
+ pin_out_time = None # SdCoil no tiene salida ET explícita
+
+ # 1. Obtener Inputs usando los nombres de pin correctos
+ s_info = instruction["inputs"].get(pin_in)
+ tv_info = instruction["inputs"].get(pin_time)
+ timer_instance_info = instruction["inputs"].get(pin_instance)
+
+ # Obtener representaciones (SymPy o Constante/String)
+ sympy_s_expr = get_sympy_representation(s_info, network_id, sympy_map, symbol_manager)
+ sympy_or_const_tv = get_sympy_representation(tv_info, network_id, sympy_map, symbol_manager)
+ # Obtener el nombre PLC original de la INSTANCIA
+ instance_plc_name = extract_plc_variable_name(timer_instance_info)
+
+ # 2. Verificar dependencias
+ if sympy_s_expr is None or sympy_or_const_tv is None:
+ # print(f"DEBUG {instr_type_original} {instr_uid}: Input/TV dependency not ready")
+ return False
+ if instance_plc_name is None:
+ print(f"Error: {instr_type_original} {instr_uid} sin variable de instancia en pin '{pin_instance}'.")
+ instance_plc_name = f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder
+ print(f"Advertencia: Usando placeholder '{instance_plc_name}'. ¡Declarar en SCL!")
+
+ # 3. Formatear nombre de instancia para SCL
+ instance_name_scl = format_variable_name(instance_plc_name)
+
+ # 4. Convertir entradas SymPy/Constante a SCL strings (simplificando la entrada IN)
+ try:
+ # Simplificar la expresión de entrada booleana
+ simplified_s_expr = sympy.simplify_logic(sympy_s_expr, force=True)
+ simplified_s_expr = sympy.logic.boolalg.to_dnf(sympy_s_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying '{pin_in}' input for {instr_type_original} {instr_uid}: {e}")
+ simplified_s_expr = sympy_s_expr # Fallback
+ s_scl = sympy_expr_to_scl(simplified_s_expr, symbol_manager)
+
+ # tv normalmente es constante, sympy_expr_to_scl debería manejarlo
+ tv_scl = sympy_expr_to_scl(sympy_or_const_tv, symbol_manager)
+
+ # 5. Generar la llamada SCL
+ # Ignoramos 'r' (reset) de Se si existiera
+ scl_call = f"{instance_name_scl}(IN := {s_scl}, PT := {tv_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};"
+
+ # 6. Actualizar instrucción con el SCL final
+ instruction["scl"] = scl_call
+ instruction["type"] = instr_type_original + SCL_SUFFIX # Marcar como procesado
+
+ # 7. Actualizar sympy_map para las salidas (Q y ET si aplica)
+ # Usar los nombres de pin originales determinados al principio
+ map_key_q = (network_id, instr_uid, pin_out_q) # pin_out_q es 'q' o 'out'
+ q_output_scl_access = f"{instance_name_scl}.Q" # Siempre accedemos a .Q del FB SCL
+ # *** OBTENER/CREAR Y ALMACENAR SYMBOL para la salida booleana Q ***
+ sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access)
+ if sympy_q_symbol:
+ sympy_map[map_key_q] = sympy_q_symbol # Almacenar el OBJETO SYMBOL
+ else:
+ # Manejar error si no se pudo crear el símbolo
+ print(f"Error: No se pudo crear símbolo para {q_output_scl_access} en {instr_type_original} {instr_uid}")
+ sympy_map[map_key_q] = None # Indicar error/irresoluble
+
+ # Almacenar ET solo si corresponde (para Se, no para SdCoil)
+ if pin_out_time: # pin_out_time es 'rt' o None
+ map_key_rt = (network_id, instr_uid, pin_out_time)
+ # ET es TIME, no booleano. Almacenar el string SCL de acceso está bien.
+ sympy_map[map_key_rt] = f"{instance_name_scl}.ET" # Salida ET del FB SCL
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve la info para Se (-> TP) y SdCoil (-> TON, manejado aquí)."""
+ return [
+ {'type_name': 'se', 'processor_func': process_se, 'priority': 5},
+ # Asegurarse que x1.py mapea SdCoil a este procesador o a uno específico
+ {'type_name': 'sdcoil', 'processor_func': process_se, 'priority': 5}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/process_timer.py b/ToUpload/processors/process_timer.py
new file mode 100644
index 0000000..f6b3d40
--- /dev/null
+++ b/ToUpload/processors/process_timer.py
@@ -0,0 +1,85 @@
+# processors/process_timer.py
+# -*- coding: utf-8 -*-
+import sympy
+import traceback
+# Usar las nuevas utilidades
+from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed"
+
+def process_timer(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
+ """
+ Genera SCL para Temporizadores (TON, TOF) directamente.
+ Requiere datos de instancia.
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX,"").replace("_error","") # TON o TOF
+ if instruction.get("type","").endswith(SCL_SUFFIX) or "_error" in instruction.get("type",""):
+ return False
+
+ scl_timer_type = instr_type_original.upper()
+ if scl_timer_type not in ["TON", "TOF"]:
+ instruction["scl"] = f"// ERROR: Tipo de temporizador directo no soportado: {instr_type_original}"
+ instruction["type"] = instr_type_original + "_error"
+ return True
+
+ # 1. Obtener Inputs: IN, PT, y nombre de instancia (implícito o explícito)
+ in_info = instruction["inputs"].get("IN")
+ pt_info = instruction["inputs"].get("PT")
+ # Buscar instancia: ¿está en inputs? ¿o como instance_db?
+ instance_plc_name = instruction.get("instance_db") # Buscar primero aquí
+ if not instance_plc_name:
+ # Si no, buscar un input llamado 'timer' o similar? No estándar.
+ # Asumir que debe estar declarado como STAT si no hay instance_db
+ instance_plc_name = instruction.get("instance_name") # Nombre directo?
+ if not instance_plc_name:
+ instance_plc_name = f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder final
+ print(f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_plc_name}'.")
+
+
+ sympy_in_expr = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
+ sympy_or_const_pt = get_sympy_representation(pt_info, network_id, sympy_map, symbol_manager)
+
+ # Verificar dependencias
+ if sympy_in_expr is None or sympy_or_const_pt is None or instance_plc_name is None:
+ return False
+
+ # Formatear nombre de instancia
+ instance_name_scl = format_variable_name(instance_plc_name)
+
+ # Convertir entradas SymPy/Constante a SCL strings
+ in_scl = sympy_expr_to_scl(sympy_in_expr, symbol_manager)
+ pt_scl = sympy_expr_to_scl(sympy_or_const_pt, symbol_manager)
+
+ # Generar la llamada SCL
+ scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};"
+
+ # Actualizar instrucción
+ instruction["scl"] = scl_call # SCL final generado
+ instruction["type"] = instr_type_original + SCL_SUFFIX
+
+ # 7. Actualizar sympy_map para las salidas Q y ET
+ map_key_q = (network_id, instr_uid, "Q") # Pin estándar SCL
+ # *** Store SymPy Symbol for boolean output Q ***
+ q_output_scl_access = f"{instance_name_scl}.Q" # String for SCL access
+ sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access) # Get/Create Symbol
+ if sympy_q_symbol:
+ sympy_map[map_key_q] = sympy_q_symbol # Store the SYMBOL
+ else:
+ print(f"Error: Could not create symbol for {q_output_scl_access} in {instr_type_original} {instr_uid}")
+ sympy_map[map_key_q] = None
+
+ map_key_et = (network_id, instr_uid, "ET") # Pin estándar SCL
+ # ET is TIME, store SCL access string
+ sympy_map[map_key_et] = f"{instance_name_scl}.ET"
+
+ return True
+
+# --- Processor Information Function ---
+def get_processor_info():
+ """Devuelve info para TON y TOF directos."""
+ return [
+ {'type_name': 'ton', 'processor_func': process_timer, 'priority': 5},
+ {'type_name': 'tof', 'processor_func': process_timer, 'priority': 5}
+ ]
\ No newline at end of file
diff --git a/ToUpload/processors/processor_utils.py b/ToUpload/processors/processor_utils.py
new file mode 100644
index 0000000..6d55415
--- /dev/null
+++ b/ToUpload/processors/processor_utils.py
@@ -0,0 +1,310 @@
+# -*- coding: utf-8 -*-
+# processors/processor_utils.py
+import re
+import sympy
+from .symbol_manager import SymbolManager, extract_plc_variable_name
+
+SCL_SUFFIX = "_sympy_processed" # <<< AÑADE ESTA LÍNEA
+
+def format_variable_name(name):
+ """Limpia el nombre de la variable para SCL."""
+ if not name:
+ return "_INVALID_NAME_"
+ if name.startswith('"') and name.endswith('"'):
+ return name
+ prefix = ""
+ if name.startswith("#"):
+ prefix = "#"
+ name = name[1:]
+ if name and name[0].isdigit():
+ name = "_" + name
+ name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
+ return prefix + name
+
+def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager):
+ """Gets the SymPy expression object representing the source."""
+ if not source_info:
+ print("Warning: get_sympy_representation called with None source_info.")
+ return None # Or raise error
+
+ # Handle lists (OR branches) - Recursively call and combine with sympy.Or
+ if isinstance(source_info, list):
+ sympy_parts = []
+ all_resolved = True
+ for sub_source in source_info:
+ sub_sympy = get_sympy_representation(sub_source, network_id, sympy_map, symbol_manager)
+ if sub_sympy is None:
+ all_resolved = False
+ break
+ sympy_parts.append(sub_sympy)
+
+ if not all_resolved:
+ return None
+ if not sympy_parts:
+ return sympy.false # Empty OR is false
+ # Return sympy.Or only if there are multiple parts
+ return sympy.Or(*sympy_parts) if len(sympy_parts) > 1 else sympy_parts[0]
+
+ # Handle single source dictionary
+ source_type = source_info.get("type")
+
+ if source_type == "powerrail":
+ return sympy.true
+ elif source_type == "variable":
+ plc_name = extract_plc_variable_name(source_info)
+ if plc_name:
+ return symbol_manager.get_symbol(plc_name)
+ else:
+ print(f"Error: Variable source without name: {source_info}")
+ return None # Error case
+ elif source_type == "constant":
+ # Represent constants directly if possible, otherwise maybe as symbols?
+ # For boolean simplification, only TRUE/FALSE matter significantly.
+ dtype = str(source_info.get("datatype", "")).upper()
+ value = source_info.get("value")
+ if dtype == "BOOL":
+ return sympy.true if str(value).upper() == "TRUE" else sympy.false
+ else:
+ # For simplification, treat non-boolean constants as opaque symbols?
+ # Or just return their string representation if they won't be simplified anyway?
+ # Let's return their string value for now, processors will handle it.
+ # This might need refinement if constants need symbolic handling.
+ return str(value) # Or maybe symbol_manager.get_symbol(str(value))?
+
+ elif source_type == "connection":
+ map_key = (
+ network_id,
+ source_info.get("source_instruction_uid"),
+ source_info.get("source_pin"),
+ )
+ # Return the SymPy object from the map
+ return sympy_map.get(map_key) # Returns None if not found (dependency not ready)
+ elif source_type == "unknown_source":
+ print(f"Warning: Referring to unknown source UID: {source_info.get('uid')}")
+ return None # Cannot resolve
+ else:
+ print(f"Warning: Unknown source type: {source_info}")
+ return None # Cannot resolve
+
+def sympy_expr_to_scl(expr, symbol_manager, format_prec=5):
+ """Converts a SymPy expression to an SCL string using the symbol map."""
+ if expr is None: return "/* ERROR: None expression */"
+ if expr == sympy.true: return "TRUE"
+ if expr == sympy.false: return "FALSE"
+
+ # Use sympy's string printer with custom settings if needed
+ # For boolean, standard printing might be okay, but need to substitute symbols
+ try:
+ # Get the inverse map (py_id -> plc_name)
+ inverse_map = symbol_manager.get_inverse_map()
+
+ # Substitute symbols back to their py_id strings first
+ # Need to handle the structure (And, Or, Not)
+ scl_str = sympy.sstr(expr, order=None) # Basic string representation
+
+ # Now, carefully replace py_id back to PLC names using regex
+ # Sort keys by length descending to replace longer IDs first
+ for py_id in sorted(inverse_map.keys(), key=len, reverse=True):
+ # Use word boundaries to avoid replacing parts of other IDs
+ scl_str = re.sub(r'\b' + re.escape(py_id) + r'\b', inverse_map[py_id], scl_str)
+
+ # Replace SymPy operators/functions with SCL equivalents
+ scl_str = scl_str.replace('&', ' AND ')
+ scl_str = scl_str.replace('|', ' OR ')
+ scl_str = scl_str.replace('^', ' XOR ') # If XOR is used
+ scl_str = scl_str.replace('~', 'NOT ')
+ # Add spaces around operators if needed after substitution
+ scl_str = re.sub(r'AND', ' AND ', scl_str)
+ scl_str = re.sub(r'OR', ' OR ', scl_str)
+ scl_str = re.sub(r'XOR', ' XOR ', scl_str)
+ scl_str = re.sub(r'NOT', 'NOT ', scl_str) # Space after NOT
+
+ # Clean up potential double spaces, etc.
+ scl_str = re.sub(r'\s+', ' ', scl_str).strip()
+ # Handle parentheses potentially added by sstr - maybe remove redundant ones?
+ # Be careful not to break operator precedence.
+
+ return scl_str
+
+ except Exception as e:
+ print(f"Error converting SymPy expr '{expr}' to SCL: {e}")
+ traceback.print_exc()
+ return f"/* ERROR converting SymPy: {expr} */"
+
+def get_scl_representation(source_info, network_id, scl_map, access_map):
+ if not source_info:
+ return None
+ if isinstance(source_info, list):
+ scl_parts = []
+ all_resolved = True
+ for sub_source in source_info:
+ sub_scl = get_scl_representation(
+ sub_source, network_id, scl_map, access_map
+ )
+ if sub_scl is None:
+ all_resolved = False
+ break
+ if (
+ sub_scl in ["TRUE", "FALSE"]
+ or (sub_scl.startswith('"') and sub_scl.endswith('"'))
+ or sub_scl.isdigit()
+ or (sub_scl.startswith("(") and sub_scl.endswith(")"))
+ ):
+ scl_parts.append(sub_scl)
+ else:
+ scl_parts.append(f"({sub_scl})")
+ return (
+ " OR ".join(scl_parts)
+ if len(scl_parts) > 1
+ else (scl_parts[0] if scl_parts else "FALSE") if all_resolved else None
+ )
+ source_type = source_info.get("type")
+ if source_type == "powerrail":
+ return "TRUE"
+ elif source_type == "variable":
+ name = source_info.get("name")
+ # Asegurar que los nombres de variables se formatean correctamente aquí también
+ return (
+ format_variable_name(name)
+ if name
+ else f"_ERR_VAR_NO_NAME_{source_info.get('uid')}_"
+ )
+ elif source_type == "constant":
+ dtype = str(source_info.get("datatype", "")).upper()
+ value = source_info.get("value")
+ try:
+ if dtype == "BOOL":
+ return str(value).upper()
+ elif dtype in [
+ "INT",
+ "DINT",
+ "SINT",
+ "USINT",
+ "UINT",
+ "UDINT",
+ "LINT",
+ "ULINT",
+ "WORD",
+ "DWORD",
+ "LWORD",
+ "BYTE",
+ ]:
+ return str(value)
+ elif dtype in ["REAL", "LREAL"]:
+ s_val = str(value)
+ return s_val if "." in s_val or "e" in s_val.lower() else s_val + ".0"
+ elif dtype == "STRING":
+ # Escapar comillas simples dentro del string si es necesario
+ str_val = str(value).replace("'", "''")
+ return f"'{str_val}'"
+ elif dtype == "TYPEDCONSTANT":
+ # Podría necesitar formateo específico basado en el tipo real
+ return str(value)
+ else:
+ # Otros tipos (TIME, DATE, etc.) - devolver como string por ahora
+ str_val = str(value).replace("'", "''")
+ return f"'{str_val}'"
+ except Exception as e:
+ print(f"Advertencia: Error formateando constante {source_info}: {e}")
+ return f"_ERR_CONST_FORMAT_{source_info.get('uid')}_"
+ elif source_type == "connection":
+ map_key = (
+ network_id,
+ source_info.get("source_instruction_uid"),
+ source_info.get("source_pin"),
+ )
+ return scl_map.get(map_key)
+ elif source_type == "unknown_source":
+ print(
+ f"Advertencia: Refiriendo a fuente desconocida UID: {source_info.get('uid')}"
+ )
+ return f"_ERR_UNKNOWN_SRC_{source_info.get('uid')}_"
+ else:
+ print(f"Advertencia: Tipo de fuente desconocido: {source_info}")
+ return f"_ERR_INVALID_SRC_TYPE_"
+
+def format_variable_name(name):
+ """Limpia el nombre de la variable para SCL."""
+ if not name:
+ return "_INVALID_NAME_"
+
+ # Si ya está entre comillas dobles, asumimos que es un nombre complejo (ej. "DB"."Variable")
+ # y lo devolvemos tal cual para SCL.
+ if name.startswith('"') and name.endswith('"'):
+ # Podríamos añadir validación extra aquí si fuera necesario
+ return name
+
+ # Si no tiene comillas, es un nombre simple (ej. Tag_1, #tempVar)
+ # Reemplazar caracteres no válidos (excepto '_') por '_'
+ # Permitir '#' al inicio para variables temporales
+ prefix = ""
+ if name.startswith("#"):
+ prefix = "#"
+ name = name[1:]
+
+ # Permitir letras, números y guiones bajos. Reemplazar el resto.
+ # Asegurarse de que no empiece con número (después del # si existe)
+ if name and name[0].isdigit():
+ name = "_" + name
+ # Reemplazar caracteres no válidos
+ name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
+
+ return prefix + name
+
+def generate_temp_var_name(network_id, instr_uid, pin_name):
+ net_id_clean = str(network_id).replace("-", "_")
+ instr_uid_clean = str(instr_uid).replace("-", "_")
+ pin_name_clean = str(pin_name).replace("-", "_").lower()
+ # Usar # para variables temporales SCL estándar
+ return f"#_temp_{net_id_clean}_{instr_uid_clean}_{pin_name_clean}"
+
+def get_target_scl_name(instruction, pin_name, network_id, default_to_temp=True):
+ """Gets the SCL formatted name for a target variable.
+ Handles instruction outputs AND specific inputs like Coil operand.
+ """
+ instr_uid = instruction["instruction_uid"]
+ # Ahora SCL_SUFFIX está definido en este módulo
+ instr_type_upper = instruction.get("type", "").upper().replace(SCL_SUFFIX.upper(), "").replace("_ERROR", "") # Check original type
+ target_info = None
+
+ # Special handling for inputs that represent the target variable
+ if instr_type_upper in ["COIL", "SCOIL", "RCOIL"] and pin_name == "operand":
+ target_info = instruction.get("inputs", {}).get("operand")
+ # Add other instructions where input pin == target if necessary
+ # elif instr_type_upper == "XYZ" and pin_name == "some_input_target_pin":
+ # target_info = instruction.get("inputs", {}).get(pin_name)
+ else:
+ # Default: Assume pin_name refers to an output pin
+ output_pin_data = instruction.get("outputs", {}).get(pin_name)
+ # Check if it's a list and has one connection (standard case)
+ if (output_pin_data and isinstance(output_pin_data, list) and len(output_pin_data) == 1):
+ target_info = output_pin_data[0]
+ # Add handling for direct output assignment if your JSON structure supports it
+
+ target_scl = None
+ if target_info:
+ if target_info.get("type") == "variable":
+ plc_name = target_info.get("name")
+ if plc_name:
+ target_scl = format_variable_name(plc_name) # Use existing util
+ else:
+ print(f"Error: Target variable for {instr_uid}.{pin_name} has no name (UID: {target_info.get('uid')}).")
+ elif target_info.get("type") == "constant":
+ print(f"Advertencia: Attempt to write to constant target {instr_uid}.{pin_name} (UID: {target_info.get('uid')}).")
+ # else: # Handle other target types if needed
+ # print(f"Advertencia: Target {instr_uid}.{pin_name} is not a variable: {target_info.get('type')}.")
+ # else: # No target info found for the specified pin
+ # print(f"DEBUG: No target info found for {instr_uid}.{pin_name}")
+ pass
+
+
+ # Handle default_to_temp logic
+ if target_scl:
+ return target_scl
+ elif default_to_temp:
+ # Generate temp only if no explicit target was found AND default is allowed
+ print(f"INFO: Generating temp var for {instr_uid}.{pin_name}") # Be informative
+ return generate_temp_var_name(network_id, instr_uid, pin_name)
+ else:
+ # No target found and default temps not allowed
+ return None
diff --git a/ToUpload/processors/symbol_manager.py b/ToUpload/processors/symbol_manager.py
new file mode 100644
index 0000000..ed271d2
--- /dev/null
+++ b/ToUpload/processors/symbol_manager.py
@@ -0,0 +1,58 @@
+# processors/symbol_manager.py
+import sympy
+import re
+
+class SymbolManager:
+ def __init__(self):
+ # plc_name -> py_id (e.g., '"DB".Var' -> 'v0_')
+ self.plc_to_py_id = {}
+ # py_id -> Symbol object (e.g., 'v0_' -> sympy.Symbol('v0_'))
+ self.py_id_to_symbol = {}
+ # py_id -> plc_name (e.g., 'v0_' -> '"DB".Var') - Inverse mapping
+ self.py_id_to_plc = {}
+ self.counter = 0
+ # Pre-define common keywords/constants to avoid mapping them
+ self.reserved_names = {"TRUE", "FALSE"} # Add others if needed
+
+ def _generate_py_id(self):
+ py_id = f"v{self.counter}_"
+ self.counter += 1
+ # Extremely unlikely collision, but check anyway
+ while py_id in self.py_id_to_symbol:
+ py_id = f"v{self.counter}_"
+ self.counter += 1
+ return py_id
+
+ def get_symbol(self, plc_var_name):
+ """Gets/Creates a SymPy Symbol for a PLC variable name."""
+ if plc_var_name is None:
+ print("Warning: Attempted to get symbol for None PLC name.")
+ return None # Or handle error appropriately
+ if plc_var_name.upper() in self.reserved_names:
+ print(f"Warning: Attempted to create symbol for reserved name: {plc_var_name}")
+ return None # Or handle differently (e.g., return sympy.true/false?)
+
+ if plc_var_name not in self.plc_to_py_id:
+ py_id = self._generate_py_id()
+ self.plc_to_py_id[plc_var_name] = py_id
+ self.py_id_to_plc[py_id] = plc_var_name
+ self.py_id_to_symbol[py_id] = sympy.symbols(py_id)
+ # print(f"DEBUG SymbolManager: Created {py_id} -> {plc_var_name}") # Debug
+ else:
+ py_id = self.plc_to_py_id[plc_var_name]
+
+ return self.py_id_to_symbol.get(py_id)
+
+ def get_plc_name(self, py_id):
+ """Gets the original PLC name from a py_id."""
+ return self.py_id_to_plc.get(py_id)
+
+ def get_inverse_map(self):
+ """Returns the map needed for postprocessing (py_id -> plc_name)."""
+ return self.py_id_to_plc.copy()
+
+# Helper function to extract PLC variable name from JSON operand info
+def extract_plc_variable_name(operand_info):
+ if operand_info and operand_info.get("type") == "variable":
+ return operand_info.get("name")
+ return None # Not a variable or info missing
\ No newline at end of file
diff --git a/ToUpload/x0_main.py b/ToUpload/x0_main.py
new file mode 100644
index 0000000..b82e9d2
--- /dev/null
+++ b/ToUpload/x0_main.py
@@ -0,0 +1,145 @@
+import argparse
+import subprocess
+import os
+import sys
+import locale
+import glob # <--- Importar glob para buscar archivos
+
+# (Función get_console_encoding y variable CONSOLE_ENCODING como en la respuesta anterior)
+def get_console_encoding():
+ """Obtiene la codificación preferida de la consola, con fallback."""
+ try:
+ return locale.getpreferredencoding(False)
+ except Exception:
+ return 'cp1252'
+
+CONSOLE_ENCODING = get_console_encoding()
+# Descomenta la siguiente línea si quieres ver la codificación detectada:
+# print(f"Detected console encoding: {CONSOLE_ENCODING}")
+
+# (Función run_script como en la respuesta anterior, usando CONSOLE_ENCODING)
+def run_script(script_name, xml_arg):
+ """Runs a given script with the specified XML file argument."""
+ script_path = os.path.join(os.path.dirname(__file__), script_name)
+ command = [sys.executable, script_path, xml_arg]
+ print(f"\n--- Running {script_name} with argument: {xml_arg} ---")
+ try:
+ result = subprocess.run(command,
+ check=True,
+ capture_output=True,
+ text=True,
+ encoding=CONSOLE_ENCODING,
+ errors='replace') # 'replace' para evitar errores
+
+ # Imprimir stdout y stderr
+ # Eliminar saltos de línea extra al final si existen
+ stdout_clean = result.stdout.strip()
+ stderr_clean = result.stderr.strip()
+ if stdout_clean:
+ print(stdout_clean)
+ if stderr_clean:
+ print("--- Stderr ---")
+ print(stderr_clean)
+ print("--------------")
+ print(f"--- {script_name} finished successfully ---")
+ return True
+ except FileNotFoundError:
+ print(f"Error: Script '{script_path}' not found.")
+ return False
+ except subprocess.CalledProcessError as e:
+ print(f"Error running {script_name}:")
+ print(f"Return code: {e.returncode}")
+ stdout_decoded = e.stdout.decode(CONSOLE_ENCODING, errors='replace').strip() if isinstance(e.stdout, bytes) else (e.stdout or "").strip()
+ stderr_decoded = e.stderr.decode(CONSOLE_ENCODING, errors='replace').strip() if isinstance(e.stderr, bytes) else (e.stderr or "").strip()
+ if stdout_decoded:
+ print("--- Stdout ---")
+ print(stdout_decoded)
+ if stderr_decoded:
+ print("--- Stderr ---")
+ print(stderr_decoded)
+ print("--------------")
+ return False
+ except Exception as e:
+ print(f"An unexpected error occurred while running {script_name}: {e}")
+ return False
+
+# --- NUEVA FUNCIÓN PARA SELECCIONAR ARCHIVO ---
+def select_xml_file():
+ """Busca archivos .xml, los lista y pide al usuario que elija uno."""
+ print("No XML file specified. Searching for XML files in current directory...")
+ # Buscar archivos .xml en el directorio actual (.)
+ xml_files = sorted(glob.glob('*.xml')) # sorted para orden alfabético
+
+ if not xml_files:
+ print("Error: No .xml files found in the current directory.")
+ sys.exit(1)
+
+ print("\nAvailable XML files:")
+ for i, filename in enumerate(xml_files, start=1):
+ print(f" {i}: {filename}")
+
+ while True:
+ try:
+ choice = input(f"Enter the number of the file to process (1-{len(xml_files)}): ")
+ choice_num = int(choice)
+ if 1 <= choice_num <= len(xml_files):
+ selected_file = xml_files[choice_num - 1]
+ print(f"Selected: {selected_file}")
+ return selected_file
+ else:
+ print("Invalid choice. Please enter a number from the list.")
+ except ValueError:
+ print("Invalid input. Please enter a number.")
+ except EOFError: # Manejar si la entrada se cierra inesperadamente
+ print("\nSelection cancelled.")
+ sys.exit(1)
+# --- FIN NUEVA FUNCIÓN ---
+
+
+if __name__ == "__main__":
+ xml_filename = None
+
+ # Comprobar si se pasó un argumento de línea de comandos
+ # sys.argv[0] es el nombre del script, sys.argv[1] sería el primer argumento
+ if len(sys.argv) > 1:
+ # Si hay argumentos, usar argparse para parsearlo (permite -h, etc.)
+ parser = argparse.ArgumentParser(
+ description="Run the Simatic XML processing pipeline."
+ )
+ parser.add_argument(
+ "xml_file",
+ # Ya no necesitamos nargs='?' ni default aquí porque sabemos que hay un argumento
+ help="Path to the XML file to process.",
+ )
+ # Parsear solo los argumentos conocidos, ignorar extras si los hubiera
+ args, unknown = parser.parse_known_args()
+ xml_filename = args.xml_file
+ print(f"XML file specified via argument: {xml_filename}")
+ else:
+ # Si no hay argumentos, llamar a la función interactiva
+ xml_filename = select_xml_file()
+
+ # --- El resto del script continúa igual, usando xml_filename ---
+
+ # Verificar si el archivo XML de entrada (seleccionado o pasado) existe
+ if not os.path.exists(xml_filename):
+ print(f"Error: Selected or specified XML file not found: {xml_filename}")
+ sys.exit(1)
+
+ print(f"\nStarting pipeline for: {xml_filename}")
+
+ # Run scripts sequentially (asegúrate que los nombres son correctos)
+ script1 = "x1_to_json.py"
+ script2 = "x2_process.py"
+ script3 = "x3_generate_scl.py"
+
+ if run_script(script1, xml_filename):
+ if run_script(script2, xml_filename):
+ if run_script(script3, xml_filename):
+ print("\nPipeline completed successfully.")
+ else:
+ print("\nPipeline failed at script:", script3)
+ else:
+ print("\nPipeline failed at script:", script2)
+ else:
+ print("\nPipeline failed at script:", script1)
\ No newline at end of file
diff --git a/ToUpload/x1_to_json.py b/ToUpload/x1_to_json.py
new file mode 100644
index 0000000..8b1a128
--- /dev/null
+++ b/ToUpload/x1_to_json.py
@@ -0,0 +1,1106 @@
+# -*- coding: utf-8 -*-
+import json
+import argparse
+import os
+from lxml import etree
+import traceback
+from collections import defaultdict
+
+# --- Namespaces ---
+# Se añade el namespace 'st' para Structured Text
+ns = {
+ "iface": "http://www.siemens.com/automation/Openness/SW/Interface/v5",
+ "flg": "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4",
+ "st": "http://www.siemens.com/automation/Openness/SW/NetworkSource/StructuredText/v3", # <--- Added SCL namespace
+}
+
+
+# --- Helper Functions ---
+def get_multilingual_text(element, default_lang="en-US", fallback_lang="it-IT"):
+ # (Sin cambios respecto a la versión anterior)
+ 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):
+ # (Sin cambios respecto a la versión anterior)
+ 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):
+ # (Sin cambios respecto a la versión anterior)
+ 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):
+ # (Sin cambios respecto a la versión anterior)
+ 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}")
+ negated_pins = {}
+ 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
+ except Exception as e:
+ print(f"Advertencia: Error extrayendo Negated Pins Part UID={uid}: {e}")
+ return {
+ "uid": uid,
+ "type": name,
+ "template_values": template_values,
+ "negated_pins": negated_pins,
+ }
+
+
+def parse_call(call_element):
+ # (Mantiene la corrección para DB de instancia)
+ 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")
+ instance_name = None
+ instance_scope = None
+ if not block_name or not block_type:
+ print(f"Error: CallInfo para UID {uid} sin Name o BlockType.")
+ return None
+ if block_type == "FB":
+ instance_elem_list = call_info.xpath("./*[local-name()='Instance']")
+ if instance_elem_list:
+ instance_elem = instance_elem_list[0]
+ instance_scope = instance_elem.get("Scope")
+ component_elem_list = instance_elem.xpath(
+ "./*[local-name()='Component']"
+ ) # Busca Component directo
+ if component_elem_list:
+ component_elem = component_elem_list[0]
+ db_name_raw = component_elem.get("Name")
+ if db_name_raw:
+ instance_name = f'"{db_name_raw}"' # Añade comillas
+ else:
+ print(
+ f"Advertencia: dentro de para FB Call UID {uid} no tiene atributo 'Name'."
+ )
+ else:
+ print(
+ f"Advertencia: No se encontró dentro de para FB Call UID {uid}. No se pudo obtener el nombre del DB."
+ )
+ else:
+ print(
+ f"Advertencia: FB Call '{block_name}' UID {uid} no tiene elemento ."
+ )
+ call_data = {
+ "uid": uid,
+ "type": "Call",
+ "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
+
+
+# EN x1_to_json.py, junto a otras funciones auxiliares
+
+
+def reconstruct_scl_from_tokens(st_node):
+ """
+ Intenta reconstruir una cadena SCL a partir de los elementos hijos
+ de un nodo . Es una aproximación y puede no ser perfecta.
+ """
+ if st_node is None:
+ return "// Error: StructuredText node not found.\n"
+
+ scl_parts = []
+ # Obtener todos los elementos hijos directos en orden
+ children = st_node.xpath("./st:*", namespaces=ns)
+
+ for elem in children:
+ tag = etree.QName(elem.tag).localname
+
+ if tag == "Token":
+ scl_parts.append(elem.get("Text", ""))
+ elif tag == "Blank":
+ scl_parts.append(" " * int(elem.get("Num", 1)))
+ elif tag == "NewLine":
+ # Añadir un salto de línea real. strip() al final de la línea actual
+ # para evitar espacios extra antes del salto.
+ scl_parts.append("\n")
+ elif tag == "Access":
+ # Reconstruir acceso (simplificado)
+ symbol_parts = []
+ # Buscar componentes y puntos dentro del símbolo de este acceso
+ symbol_children = elem.xpath(
+ ".//st:Component | .//st:Token[@Text='.']", namespaces=ns
+ )
+ for sym_child in symbol_children:
+ sym_tag = etree.QName(sym_child.tag).localname
+ if sym_tag == "Component":
+ comp_name = sym_child.get("Name", "_ERR_")
+ # Comprobar si necesita comillas (podríamos necesitar parsear BooleanAttribute)
+ # Simplificación: Añadir comillas si el nombre original parece tenerlas (o siempre para DBs?)
+ # Aquí usamos el nombre tal cual viene en el XML por ahora
+ # Si el XML tiene true podríamos usarlo
+ has_quotes_elem = sym_child.xpath(
+ "../st:BooleanAttribute[@Name='HasQuotes']/text()",
+ namespaces=ns,
+ )
+ has_quotes = (
+ has_quotes_elem and has_quotes_elem[0].lower() == "true"
+ )
+
+ # Reconstrucción básica: poner comillas si HasQuotes es true o si es el primer componente (posible DB)
+ # Esto es heurístico y puede fallar.
+ # if has_quotes or (len(symbol_parts) == 0 and '.' not in comp_name): # Asumir primer componente es DB
+ # symbol_parts.append(f'"{comp_name}"')
+ # else:
+ # symbol_parts.append(comp_name)
+
+ # Versión más simple: usar nombre tal cual del XML
+ symbol_parts.append(comp_name)
+
+ elif sym_tag == "Token": # Solo nos interesa el punto aquí
+ symbol_parts.append(".")
+ access_str = "".join(symbol_parts)
+
+ # Manejar llamadas a funciones/FB dentro de Access Scope="Call"
+ if elem.get("Scope") == "Call":
+ instruction_elem = elem.xpath("./st:Instruction", namespaces=ns)
+ if instruction_elem:
+ instr_name = instruction_elem[0].get("Name", "_UNKNOWN_CALL_")
+ # Reconstrucción básica de parámetros (muy simplificada)
+ # Necesitaría parsear Parameters, Tokens '(', ')', ':=', '=>', etc.
+ # Por ahora, solo añadimos el nombre y paréntesis vacíos
+ access_str = f"{instr_name}()" # Placeholder muy básico
+
+ scl_parts.append(access_str)
+
+ elif tag == "Comment" or tag == "LineComment":
+ # Añadir comentarios
+ comment_text = elem.text if elem.text else ""
+ if tag == "Comment" and "\n" in comment_text: # Comentario multi-línea
+ scl_parts.append(f"(*{comment_text}*)")
+ else: # Comentario de una línea
+ scl_parts.append(f"// {comment_text}")
+ else:
+ # Ignorar otros tipos de nodos por ahora o añadir manejo específico
+ pass
+
+ # Unir todas las partes, limpiar espacios extra alrededor de saltos de línea
+ full_scl = "".join(scl_parts)
+ # Limpieza básica de formato
+ lines = [line.rstrip() for line in full_scl.split("\n")]
+ # Re-ensamblar, asegurando que líneas vacías (solo espacios previos) se mantengan
+ cleaned_scl = "\n".join(lines)
+ # Eliminar múltiples líneas vacías consecutivas (opcional)
+ # import re
+ # cleaned_scl = re.sub(r'\n\s*\n', '\n\n', cleaned_scl)
+
+ return cleaned_scl.strip() # Quitar espacios/saltos al inicio/final
+
+
+# --- Función parse_network con XPath corregido para Title/Comment ---
+# --- Función parse_network MODIFICADA (maneja multi-destino en Wire) ---
+def parse_network(network_element):
+ """
+ Parsea una red, extrae lógica y añade conexiones EN implícitas.
+ Maneja wires con múltiples destinos.
+ """
+ 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")
+
+ # --- Extracción Título/Comentario (sin cambios respecto a la última versión) ---
+ 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_element = network_element.xpath(
+ "./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']"
+ )
+ network_comment = (
+ get_multilingual_text(comment_element[0]) if comment_element else ""
+ )
+
+ flgnet_list = network_element.xpath(".//flg:FlgNet", namespaces=ns)
+ if not flgnet_list:
+ # print(f"Advertencia: FlgNet no encontrado en Red ID={network_id}. Puede estar vacía o ser comentario.")
+ 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 (sin cambios)
+ access_map = {
+ acc_info["uid"]: acc_info
+ for acc in flgnet.xpath(".//flg:Access", namespaces=ns)
+ if (acc_info := parse_access(acc)) and acc_info["type"] != "unknown"
+ }
+ parts_and_calls_map = {}
+ instruction_elements = flgnet.xpath(".//flg:Part | .//flg:Call", namespaces=ns)
+ for element in instruction_elements:
+ parsed_info = None
+ tag_name = etree.QName(
+ element.tag
+ ).localname # Obtener nombre local de la etiqueta
+ if tag_name == "Part":
+ parsed_info = parse_part(element)
+ elif tag_name == "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}"
+ )
+
+ # --- 2. Parsear Wires (MODIFICADO para multi-destino) ---
+ wire_connections = defaultdict(
+ list
+ ) # (dest_uid, dest_pin) -> [(src_uid, src_pin), ...]
+ source_connections = defaultdict(
+ list
+ ) # (src_uid, src_pin) -> [(dest_uid, dest_pin), ...]
+ eno_outputs = defaultdict(
+ list
+ ) # src_uid -> [(dest_uid, dest_pin), ...] (conexiones DESDE eno)
+
+ flg_ns_uri = ns["flg"] # Cache namespace URI
+ qname_powerrail = etree.QName(flg_ns_uri, "Powerrail")
+ qname_identcon = etree.QName(flg_ns_uri, "IdentCon")
+ qname_namecon = etree.QName(flg_ns_uri, "NameCon")
+
+ for wire in flgnet.xpath(".//flg:Wire", namespaces=ns):
+ children = wire.getchildren()
+ if len(children) < 2:
+ continue # Ignorar wires sin fuente y al menos un destino
+
+ source_elem = children[0]
+ source_uid, source_pin = None, None
+
+ # Determinar fuente
+ if source_elem.tag == qname_powerrail:
+ source_uid, source_pin = "POWERRAIL", "out"
+ elif source_elem.tag == qname_identcon:
+ source_uid, source_pin = (
+ source_elem.get("UId"),
+ "value",
+ ) # Acceso a variable/constante
+ elif source_elem.tag == qname_namecon:
+ source_uid, source_pin = source_elem.get("UId"), source_elem.get(
+ "Name"
+ ) # Salida de instrucción
+
+ if source_uid is None:
+ continue # No se pudo determinar la fuente
+
+ source_info = (source_uid, source_pin) # Par de fuente
+
+ # Iterar sobre TODOS los posibles destinos (desde el segundo hijo en adelante)
+ for dest_elem in children[1:]:
+ dest_uid, dest_pin = None, None
+
+ # Determinar destino
+ if dest_elem.tag == qname_identcon:
+ dest_uid, dest_pin = (
+ dest_elem.get("UId"),
+ "value",
+ ) # Entrada a variable/constante (Coil, etc.)
+ elif dest_elem.tag == qname_namecon:
+ dest_uid, dest_pin = dest_elem.get("UId"), dest_elem.get(
+ "Name"
+ ) # Entrada a instrucción
+
+ # Guardar conexiones si son válidas
+ if dest_uid is not None and dest_pin is not None:
+ # Mapa de Conexiones (Destino -> [Fuentes])
+ dest_key = (dest_uid, dest_pin)
+ if source_info not in wire_connections[dest_key]:
+ wire_connections[dest_key].append(source_info)
+
+ # Mapa de Fuentes (Fuente -> [Destinos])
+ source_key = (source_uid, source_pin)
+ dest_info = (dest_uid, dest_pin)
+ if dest_info not in source_connections[source_key]:
+ source_connections[source_key].append(dest_info)
+
+ # Registrar conexiones que SALEN de un pin 'eno'
+ if source_pin == "eno" and source_uid in parts_and_calls_map:
+ if dest_info not in eno_outputs[source_uid]:
+ eno_outputs[source_uid].append(dest_info)
+ # else: # Debug opcional si un elemento no es destino válido
+ # print(f"Advertencia: Elemento en Wire {wire.get('UId')} no es destino válido: {etree.tostring(dest_elem)}")
+ # --- FIN MODIFICACIÓN Wire ---
+
+ # 3. Construcción Lógica Inicial (sin cambios)
+ all_logic_steps = {}
+ functional_block_types = [
+ "Move",
+ "Add",
+ "Sub",
+ "Mul",
+ "Div",
+ "Mod",
+ "Convert",
+ "Call",
+ "Se",
+ "Sd",
+ "BLKMOV",
+ ]
+ rlo_generators = [
+ "Contact",
+ "O",
+ "Eq",
+ "Ne",
+ "Gt",
+ "Lt",
+ "Ge",
+ "Le",
+ "And",
+ "Xor",
+ "PBox",
+ "NBox",
+ ]
+ for instruction_uid, instruction_info in parts_and_calls_map.items():
+ # Copiar info básica
+ instruction_repr = {"instruction_uid": instruction_uid, **instruction_info}
+ instruction_repr["inputs"] = {}
+ instruction_repr["outputs"] = {}
+
+ # --- INICIO: Manejo Especial SdCoil y otros Timers ---
+ original_type = instruction_info["type"]
+ current_type = original_type
+ input_pin_mapping = {} # Mapa XML pin -> JSON pin
+ output_pin_mapping = {} # Mapa XML pin -> JSON pin
+
+ if original_type == "SdCoil":
+ print(
+ f" Advertencia: Reinterpretando 'SdCoil' (UID: {instruction_uid}) como 'Se' (Pulse Timer)."
+ )
+ current_type = "Se" # Tratarlo como Se (TP)
+ input_pin_mapping = {
+ "in": "s", # Pin XML 'in' mapea a JSON 's' (Start)
+ "operand": "timer", # Pin XML 'operand' mapea a JSON 'timer' (Instance)
+ "value": "tv", # Pin XML 'value' mapea a JSON 'tv' (Time Value)
+ }
+ output_pin_mapping = {"out": "q"} # Pin XML 'out' mapea a JSON 'q' (Output)
+ elif original_type in ["Se", "Sd"]:
+ # Mapear pines estándar de Se/Sd para consistencia con TP/TON
+ input_pin_mapping = {"s": "s", "tv": "tv", "r": "r", "timer": "timer"}
+ output_pin_mapping = {
+ "q": "q",
+ "rt": "rt", # "rtbcd": "rtbcd" (ignorar BCD)
+ }
+ # Añadir otros mapeos si son necesarios para otros bloques (ej. Contadores)
+ # elif original_type == "CTU":
+ # input_pin_mapping = {"cu": "cu", "r": "r", "pv": "pv", "counter": "counter"} # 'counter' es inventado para instancia
+ # output_pin_mapping = {"qu": "qu", "cv": "cv"}
+ # --- FIN Manejo Especial ---
+
+ instruction_repr["type"] = current_type # Actualizar el tipo si se cambió
+
+ # Mapear Entradas usando el mapeo de pines
+ possible_input_pins = set(
+ [
+ "en",
+ "in",
+ "in1",
+ "in2",
+ "s",
+ "r",
+ "tv",
+ "value",
+ "operand",
+ "timer",
+ "bit",
+ "clk",
+ "pv",
+ "cu",
+ "cd",
+ "ld",
+ "pre",
+ "SRCBLK",
+ ]
+ ) # Ampliar con pines conocidos
+ for xml_pin_name in possible_input_pins:
+ dest_key = (instruction_uid, xml_pin_name)
+ if dest_key in wire_connections:
+ sources_list = wire_connections[dest_key]
+ input_sources_repr = []
+ # ... (lógica existente para obtener input_sources_repr de sources_list) ...
+ 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:
+ source_instr_info = parts_and_calls_map[source_uid]
+ source_original_type = source_instr_info["type"]
+ # Obtener el mapeo de salida para el tipo de la fuente (si existe)
+ source_output_mapping = {}
+ if source_original_type == "SdCoil":
+ source_output_mapping = {"out": "q"}
+ elif source_original_type in ["Se", "Sd"]:
+ source_output_mapping = {"q": "q", "rt": "rt"}
+
+ # Usar el pin mapeado si existe, sino el original
+ mapped_source_pin = source_output_mapping.get(source_pin, source_pin)
+
+ input_sources_repr.append({
+ "type": "connection",
+ "source_instruction_type": source_original_type, # Guardar tipo original puede ser útil
+ "source_instruction_uid": source_uid,
+ "source_pin": mapped_source_pin # <-- USAR PIN MAPEADO
+ })
+ else:
+ input_sources_repr.append(
+ {"type": "unknown_source", "uid": source_uid}
+ )
+
+ # Usar el nombre de pin mapeado para el JSON
+ json_pin_name = input_pin_mapping.get(xml_pin_name, xml_pin_name)
+
+ if len(input_sources_repr) == 1:
+ instruction_repr["inputs"][json_pin_name] = input_sources_repr[0]
+ elif len(input_sources_repr) > 1:
+ instruction_repr["inputs"][json_pin_name] = input_sources_repr
+
+ # Mapear Salidas usando el mapeo de pines
+ possible_output_pins = set(
+ [
+ "out",
+ "out1",
+ "Q",
+ "q",
+ "eno",
+ "RET_VAL",
+ "DSTBLK",
+ "rt",
+ "rtbcd",
+ "cv",
+ "cvbcd",
+ "QU",
+ "QD",
+ ]
+ )
+ for xml_pin_name in possible_output_pins:
+ source_key = (instruction_uid, xml_pin_name)
+ if source_key in source_connections:
+ # Usar el nombre de pin mapeado para el JSON
+ json_pin_name = output_pin_mapping.get(xml_pin_name, xml_pin_name)
+
+ if json_pin_name not in instruction_repr["outputs"]:
+ instruction_repr["outputs"][json_pin_name] = []
+
+ for dest_uid, dest_pin in source_connections[source_key]:
+ if dest_uid in access_map:
+ if (
+ access_map[dest_uid]
+ not in instruction_repr["outputs"][json_pin_name]
+ ):
+ instruction_repr["outputs"][json_pin_name].append(
+ access_map[dest_uid]
+ )
+
+ all_logic_steps[instruction_uid] = instruction_repr
+
+ # 4. Inferencia EN (sin cambios)
+ processed_blocks_en_inference = set()
+ something_changed = True
+ inference_passes = 0
+ max_inference_passes = len(all_logic_steps) + 5
+ try:
+ sorted_uids_for_en = sorted(
+ all_logic_steps.keys(),
+ key=lambda x: int(x) if x.isdigit() else float("inf"),
+ )
+ except ValueError:
+ sorted_uids_for_en = sorted(all_logic_steps.keys())
+ ordered_logic_list_for_en = [
+ all_logic_steps[uid] for uid in sorted_uids_for_en if uid in all_logic_steps
+ ]
+ while something_changed and inference_passes < max_inference_passes:
+ something_changed = False
+ inference_passes += 1
+ for i, instruction in enumerate(ordered_logic_list_for_en):
+ part_uid = instruction["instruction_uid"]
+ part_type_original = (
+ instruction["type"].replace("_scl", "").replace("_error", "")
+ )
+ if (
+ part_type_original in functional_block_types
+ and "en" not in instruction["inputs"]
+ and part_uid not in processed_blocks_en_inference
+ ):
+ inferred_en_source = None
+ if i > 0:
+ for j in range(i - 1, -1, -1):
+ prev_instr = ordered_logic_list_for_en[j]
+ prev_uid = prev_instr["instruction_uid"]
+ prev_type_original = (
+ prev_instr["type"].replace("_scl", "").replace("_error", "")
+ )
+ if prev_type_original in rlo_generators:
+ inferred_en_source = {
+ "type": "connection",
+ "source_instruction_uid": prev_uid,
+ "source_instruction_type": prev_type_original,
+ "source_pin": "out",
+ }
+ break
+ elif prev_type_original 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_original,
+ "source_pin": "eno",
+ }
+ break
+ else:
+ continue
+ elif prev_type_original in [
+ "Coil",
+ "SCoil",
+ "RCoil",
+ "SetCoil",
+ "ResetCoil",
+ "SdCoil",
+ ]:
+ break
+ if inferred_en_source:
+ 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 (sin cambios)
+ 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 = False
+ if dest_uid in parts_and_calls_map and dest_pin == "en":
+ try:
+ source_idx = sorted_uids_for_en.index(source_instr_uid)
+ dest_idx = sorted_uids_for_en.index(dest_uid)
+ if (
+ dest_idx == source_idx + 1
+ and parts_and_calls_map[dest_uid]["type"]
+ in functional_block_types
+ ):
+ is_direct_en_connection = True
+ except ValueError:
+ pass
+ 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")
+ ),
+ }
+ )
+ 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 y Devolver (sin cambios)
+ network_logic_final = [
+ all_logic_steps[uid] for uid in sorted_uids_for_en if uid in all_logic_steps
+ ]
+ return {
+ "id": network_id,
+ "title": network_title,
+ "comment": network_comment,
+ "logic": network_logic_final,
+ }
+
+
+# --- Función Principal convert_xml_to_json (sin cambios en su flujo general) ---
+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...") # Asume FC primero
+ block_list = root.xpath("//*[local-name()='SW.Blocks.FC']")
+ block_type_found = "FC"
+ if not block_list:
+ block_list = root.xpath(
+ "//*[local-name()='SW.Blocks.FB']"
+ ) # Busca FB si no hay FC
+ block_type_found = "FB"
+ if not block_list:
+ print("Error Crítico: No se encontró ni .")
+ return
+ else:
+ print(
+ "Advertencia: Se encontró en lugar de ."
+ )
+ the_block = block_list[0]
+ print(
+ f"Paso 2: Bloque SW.Blocks.{block_type_found} encontrado (ID={the_block.get('ID')})."
+ )
+ print("Paso 3: Extrayendo atributos del bloque...")
+ attribute_list_node = the_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()")
+ try:
+ block_number_val = int(num_node[0]) if num_node else None
+ except ValueError:
+ block_number_val = None
+ 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(
+ f"Advertencia: No se encontró AttributeList para el bloque {block_type_found}."
+ )
+ block_comment_val = ""
+ comment_node_list = the_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")
+ if not section_name:
+ continue
+ 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ó dentro de ."
+ )
+ 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
+ result["networks"] = [] # Initialize networks list here
+ object_list_node = the_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
+ network_id = network_elem.get("ID")
+ if not network_id:
+ print(" Advertencia: Se encontró CompileUnit sin ID. Saltando.")
+ continue
+
+ # --- Detectar lenguaje de la red ---
+ attribute_list = network_elem.xpath("./*[local-name()='AttributeList']")
+ programming_language = "LAD" # Default a LAD si no se especifica
+ network_source_node = None # Nodo
+
+ if attribute_list:
+ lang_node = attribute_list[0].xpath(
+ "./*[local-name()='ProgrammingLanguage']/text()"
+ )
+ if lang_node:
+ programming_language = lang_node[0].strip()
+ # Obtener el nodo NetworkSource para pasarlo a los parsers
+ network_source_list = attribute_list[0].xpath(
+ "./*[local-name()='NetworkSource']"
+ )
+ if network_source_list:
+ network_source_node = network_source_list[0]
+
+ print(
+ f" - Procesando Red ID={network_id}, Lenguaje={programming_language}"
+ )
+
+ # --- Extraer título y comentario (común) ---
+ title_element = network_elem.xpath(
+ ".//*[local-name()='MultilingualText'][@CompositionName='Title']"
+ )
+ network_title = (
+ get_multilingual_text(title_element[0])
+ if title_element
+ else f"Network {network_id}"
+ )
+
+ comment_element = network_elem.xpath(
+ "./*[local-name()='ObjectList']/*[local-name()='MultilingualText'][@CompositionName='Comment']"
+ )
+ network_comment = (
+ get_multilingual_text(comment_element[0]) if comment_element else ""
+ )
+
+ # --- Procesar según el lenguaje ---
+ parsed_network_data = None
+ if programming_language == "SCL":
+ structured_text_node = (
+ network_source_node.xpath("./st:StructuredText", namespaces=ns)
+ if network_source_node is not None
+ else None
+ )
+
+ reconstructed_scl = f"// SCL extraction failed for Network {network_id}: StructuredText node not found.\n"
+ if structured_text_node:
+ print(
+ f" Reconstruyendo SCL desde tokens para red {network_id}..."
+ )
+ reconstructed_scl = reconstruct_scl_from_tokens(
+ structured_text_node[0]
+ )
+ # print(f" ... SCL reconstruido (parcial):\n{reconstructed_scl[:200]}...") # Preview opcional
+ else:
+ print(
+ f" Advertencia: No se encontró nodo para red SCL {network_id}."
+ )
+
+ parsed_network_data = {
+ "id": network_id,
+ "title": network_title,
+ "comment": network_comment,
+ "language": "SCL",
+ "logic": [
+ {
+ "instruction_uid": f"SCL_{network_id}", # UID inventado
+ "type": "RAW_SCL_CHUNK",
+ "scl": reconstructed_scl,
+ }
+ ],
+ }
+
+ elif programming_language in ["LAD", "FBD"]:
+ # Para LAD/FBD, llamar a parse_network (que espera FlgNet dentro de NetworkSource)
+ # parse_network ya maneja su propio título/comentario si es necesario, pero podemos pasar los extraídos
+ # Nota: parse_network espera el *CompileUnit* element, no el NetworkSource
+ parsed_network_data = parse_network(network_elem)
+ if parsed_network_data:
+ parsed_network_data["language"] = (
+ programming_language # Asegurar que el lenguaje se guarda
+ )
+ if parsed_network_data.get("error"):
+ print(
+ f" Error al parsear red {programming_language} ID={network_id}: {parsed_network_data['error']}"
+ )
+ # parsed_network_data = None # Descomentar para omitir redes con error
+ else:
+ print(
+ f" Error: parse_network devolvió None para red {programming_language} ID={network_id}"
+ )
+
+ else:
+ # Manejar otros lenguajes o casos inesperados
+ print(
+ f" Advertencia: Lenguaje no soportado '{programming_language}' en red ID={network_id}. Creando placeholder."
+ )
+ parsed_network_data = {
+ "id": network_id,
+ "title": network_title,
+ "comment": network_comment,
+ "language": programming_language,
+ "logic": [
+ {
+ "instruction_uid": f"UNS_{network_id}",
+ "type": "UNSUPPORTED_LANG",
+ "scl": f"// Network {network_id} uses unsupported language: {programming_language}\n",
+ }
+ ],
+ }
+
+ # Añadir la red procesada (si es válida) al resultado
+ if parsed_network_data:
+ result["networks"].append(parsed_network_data)
+
+ # --- Fin del bucle for network_elem ---
+
+ if networks_processed_count == 0:
+ print(
+ "Advertencia: ObjectList no contenía elementos SW.Blocks.CompileUnit."
+ )
+ else:
+ print("Advertencia: No se encontró ObjectList para el bloque.")
+
+ 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.")
+ try:
+ with open(json_filepath, "w", encoding="utf-8") as f:
+ json.dump(result, f, indent=4, ensure_ascii=False)
+ print("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 inválida en '{xml_filepath}'. Detalles: {e}"
+ )
+ except Exception as e:
+ print(f"Error Crítico: Error inesperado durante la conversión: {e}")
+ print("--- Traceback ---")
+ traceback.print_exc()
+ print("--- Fin Traceback ---")
+
+
+# --- Punto de Entrada Principal ---
+if __name__ == "__main__":
+ # Imports necesarios solo para la ejecución como script principal
+ import argparse
+ import os
+ import sys
+
+ parser = argparse.ArgumentParser(
+ description="Convert Simatic XML LAD/FBD to simplified JSON."
+ )
+ parser.add_argument(
+ "xml_filepath",
+ nargs="?", # Argumento opcional
+ default="TestLAD.xml", # Valor por defecto si se ejecuta sin argumentos
+ help="Path to the input XML file (default: TestLAD.xml)",
+ )
+ args = parser.parse_args()
+
+ xml_input_file = args.xml_filepath
+
+ # Verificar si el archivo de entrada existe
+ if not os.path.exists(xml_input_file):
+ print(f"Error Crítico: Archivo XML no encontrado: '{xml_input_file}'")
+ sys.exit(1) # Salir si el archivo no existe
+
+ # Derivar nombre base para archivo de salida JSON
+ # os.path.basename obtiene el nombre del archivo de la ruta
+ # os.path.splitext divide el nombre y la extensión
+ xml_filename_base = os.path.splitext(os.path.basename(xml_input_file))[0]
+ # Construir la ruta de salida en el mismo directorio que el script o el XML
+ output_dir = os.path.dirname(
+ xml_input_file
+ ) # O usar os.path.dirname(__file__) para el directorio del script
+ json_output_file = os.path.join(output_dir, f"{xml_filename_base}_simplified.json")
+
+ # Llamar a la función principal con los nombres de archivo derivados
+ convert_xml_to_json(xml_input_file, json_output_file)
diff --git a/ToUpload/x2_process.py b/ToUpload/x2_process.py
new file mode 100644
index 0000000..d7a10a1
--- /dev/null
+++ b/ToUpload/x2_process.py
@@ -0,0 +1,447 @@
+# -*- coding: utf-8 -*-
+import json
+import argparse
+import os
+import copy
+import traceback
+import re
+import importlib
+import sys
+import sympy # Import sympy
+
+# Import necessary components from processors directory
+from processors.processor_utils import (
+ format_variable_name, # Keep if used outside processors
+ sympy_expr_to_scl, # Needed for IF grouping and maybe others
+ # get_target_scl_name might be used here? Unlikely.
+)
+from processors.symbol_manager import SymbolManager # Import the manager
+
+# --- Constantes y Configuración ---
+# SCL_SUFFIX = "_scl" # Old suffix
+SCL_SUFFIX = "_sympy_processed" # New suffix to indicate processing method
+GROUPED_COMMENT = "// Logic included in grouped IF"
+SIMPLIFIED_IF_COMMENT = "// Simplified IF condition by script" # May still be useful
+
+
+# Global data variable
+data = {}
+
+def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
+ """
+ Busca condiciones (ya procesadas -> tienen expr SymPy en sympy_map)
+ y, si habilitan un grupo (>1) de bloques funcionales (con SCL ya generado),
+ construye el bloque IF agrupado CON LA CONDICIÓN SIMPLIFICADA.
+ Modifica el campo 'scl' de la instrucción generadora de condición.
+ """
+ instr_uid = instruction["instruction_uid"]
+ instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX, "").replace("_error", "")
+ made_change = False
+
+ # Check if this instruction *could* generate a condition suitable for grouping
+ # It must have been processed by the new SymPy method
+ if (
+ not instruction.get("type", "").endswith(SCL_SUFFIX) # Check if processed by new method
+ or "_error" in instruction.get("type", "")
+ or instruction.get("grouped", False)
+ or instr_type_original not in [ # Original types that produce boolean results
+ "Contact", "O", "Eq", "Ne", "Gt", "Lt", "Ge", "Le", "PBox", "NBox", "And", "Xor", "Not" # Add others like comparison
+ ]
+ ):
+ return False
+
+ # Avoid reagruping if SCL already contains a complex IF (less likely now)
+ current_scl = instruction.get("scl", "")
+ if current_scl.strip().startswith("IF") and "END_IF;" in current_scl and GROUPED_COMMENT not in current_scl:
+ return False
+
+ # *** Get the SymPy expression for the condition ***
+ map_key_out = (network_id, instr_uid, "out")
+ sympy_condition_expr = sympy_map.get(map_key_out)
+
+ # No SymPy expression found or trivial conditions
+ if sympy_condition_expr is None or sympy_condition_expr in [sympy.true, sympy.false]:
+ return False
+
+ # --- Find consumer instructions (logic similar to before) ---
+ grouped_instructions_cores = []
+ consumer_instr_list = []
+ network_logic = next((net["logic"] for net in data["networks"] if net["id"] == network_id), [])
+ if not network_logic: return False
+
+ groupable_types = [ # Types whose *final SCL* we want to group
+ "Move", "Add", "Sub", "Mul", "Div", "Mod", "Convert",
+ "Call_FC", "Call_FB", # Assuming these generate final SCL in their processors now
+ # SCoil/RCoil might also be groupable if their SCL is final assignment
+ "SCoil", "RCoil"
+ ]
+
+ for consumer_instr in network_logic:
+ consumer_uid = consumer_instr["instruction_uid"]
+ if consumer_instr.get("grouped", False) or consumer_uid == instr_uid:
+ continue
+
+ consumer_en = consumer_instr.get("inputs", {}).get("en")
+ consumer_type = consumer_instr.get("type", "") # Current type suffix matters
+ consumer_type_original = consumer_type.replace(SCL_SUFFIX, "").replace("_error", "")
+
+ is_enabled_by_us = False
+ if ( isinstance(consumer_en, dict) and consumer_en.get("type") == "connection" and
+ consumer_en.get("source_instruction_uid") == instr_uid and
+ consumer_en.get("source_pin") == "out"):
+ is_enabled_by_us = True
+
+ # Check if consumer is groupable AND has its final SCL generated
+ # The suffix check needs adjustment based on how terminating processors set it.
+ # Assuming processors like Move, Add, Call, SCoil, RCoil NOW generate final SCL and add a suffix.
+ if ( is_enabled_by_us and consumer_type.endswith(SCL_SUFFIX) and # Or a specific "final_scl" suffix
+ consumer_type_original in groupable_types ):
+
+ consumer_scl = consumer_instr.get("scl", "")
+ # Extract core SCL (logic is similar, maybe simpler if SCL is cleaner now)
+ core_scl = None
+ if consumer_scl:
+ # If consumer SCL itself is an IF generated by EN, take the body
+ if consumer_scl.strip().startswith("IF"):
+ match = re.search(r"THEN\s*(.*?)\s*END_IF;", consumer_scl, re.DOTALL | re.IGNORECASE)
+ core_scl = match.group(1).strip() if match else None
+ elif not consumer_scl.strip().startswith("//"): # Otherwise, take the whole line if not comment
+ core_scl = consumer_scl.strip()
+
+ if core_scl:
+ grouped_instructions_cores.append(core_scl)
+ consumer_instr_list.append(consumer_instr)
+
+ # --- If groupable consumers found ---
+ if len(grouped_instructions_cores) > 1:
+ print(f"INFO: Agrupando {len(grouped_instructions_cores)} instr. bajo condición de {instr_type_original} UID {instr_uid}")
+
+ # *** Simplify the SymPy condition ***
+ try:
+ #simplified_expr = sympy.simplify_logic(sympy_condition_expr, force=True)
+ simplified_expr = sympy.logic.boolalg.to_dnf(sympy_condition_expr, simplify=True)
+ except Exception as e:
+ print(f"Error simplifying condition for grouping UID {instr_uid}: {e}")
+ simplified_expr = sympy_condition_expr # Fallback
+
+ # *** Convert simplified condition to SCL string ***
+ condition_scl_simplified = sympy_expr_to_scl(simplified_expr, symbol_manager)
+
+ # *** Build the grouped IF SCL ***
+ scl_grouped_lines = [f"IF {condition_scl_simplified} THEN"]
+ for core_line in grouped_instructions_cores:
+ indented_core = "\n".join([f" {line.strip()}" for line in core_line.splitlines()])
+ scl_grouped_lines.append(indented_core)
+ scl_grouped_lines.append("END_IF;")
+ final_grouped_scl = "\n".join(scl_grouped_lines)
+
+ # Update the generator instruction's SCL
+ instruction["scl"] = final_grouped_scl
+ # Mark consumers as grouped
+ for consumer_instr in consumer_instr_list:
+ consumer_instr["scl"] = f"{GROUPED_COMMENT} (by UID {instr_uid})"
+ consumer_instr["grouped"] = True
+ made_change = True
+
+ return made_change
+
+def load_processors(processors_dir="processors"):
+ """
+ Escanea el directorio, importa módulos, construye el mapa y una lista
+ ordenada por prioridad.
+ """
+ processor_map = {}
+ processor_list_unsorted = [] # Lista para guardar (priority, type_name, func)
+ default_priority = 10 # Prioridad si no se define en get_processor_info
+
+ if not os.path.isdir(processors_dir):
+ print(f"Error: Directorio de procesadores no encontrado: '{processors_dir}'")
+ return processor_map, [] # Devuelve mapa vacío y lista vacía
+
+ print(f"Cargando procesadores desde: '{processors_dir}'")
+ processors_package = os.path.basename(processors_dir)
+
+ for filename in os.listdir(processors_dir):
+ if filename.startswith("process_") and filename.endswith(".py"):
+ module_name_rel = filename[:-3]
+ full_module_name = f"{processors_package}.{module_name_rel}"
+ try:
+ module = importlib.import_module(full_module_name)
+
+ if hasattr(module, 'get_processor_info') and callable(module.get_processor_info):
+ processor_info = module.get_processor_info()
+ info_list = []
+ if isinstance(processor_info, dict):
+ info_list = [processor_info]
+ elif isinstance(processor_info, list):
+ info_list = processor_info
+ else:
+ print(f" Advertencia: get_processor_info en {full_module_name} devolvió tipo inesperado. Se ignora.")
+ continue
+
+ for info in info_list:
+ if isinstance(info, dict) and 'type_name' in info and 'processor_func' in info:
+ type_name = info['type_name'].lower()
+ processor_func = info['processor_func']
+ # Obtener prioridad, usar default si no existe
+ priority = info.get('priority', default_priority)
+
+ if callable(processor_func):
+ if type_name in processor_map:
+ print(f" Advertencia: '{type_name}' en {full_module_name} sobrescribe definición anterior.")
+ processor_map[type_name] = processor_func
+ # Añadir a la lista para ordenar
+ processor_list_unsorted.append({'priority': priority, 'type_name': type_name, 'func': processor_func})
+ print(f" - Cargado '{type_name}' (Prio: {priority}) desde {module_name_rel}.py")
+ else:
+ print(f" Advertencia: 'processor_func' para '{type_name}' en {full_module_name} no es callable.")
+ else:
+ print(f" Advertencia: Entrada inválida en {full_module_name}: {info}")
+ else:
+ print(f" Advertencia: Módulo {module_name_rel}.py no tiene 'get_processor_info'.")
+
+ except ImportError as e:
+ print(f"Error importando {full_module_name}: {e}")
+ except Exception as e:
+ print(f"Error procesando {full_module_name}: {e}")
+ traceback.print_exc()
+
+ # Ordenar la lista por prioridad (menor primero)
+ processor_list_sorted = sorted(processor_list_unsorted, key=lambda x: x['priority'])
+
+ print(f"\nTotal de tipos de procesadores cargados: {len(processor_map)}")
+ print(f"Orden de procesamiento por prioridad: {[item['type_name'] for item in processor_list_sorted]}")
+
+ # Devolver el mapa (para lookup rápido si es necesario) y la lista ordenada
+ return processor_map, processor_list_sorted
+
+# --- Bucle Principal de Procesamiento (Modificado) ---
+def process_json_to_scl(json_filepath):
+ """
+ Lee el JSON simplificado, aplica los procesadores dinámicamente cargados
+ siguiendo un orden de prioridad, y guarda el JSON procesado.
+ """
+ global data # Necesario si process_group_ifs (definido fuera) accede a data globalmente.
+ # Si process_group_ifs está definida DENTRO de process_json_to_scl,
+ # no necesitarías global, ya que accedería a la 'data' local.
+ # Lo más limpio es definir process_group_ifs fuera y pasarle 'data'
+ # como argumento (como ya se hace). Así que 'global data' aquí es probablemente innecesario.
+ # Eliminémoslo por ahora y aseguremos que data se pasa a process_group_ifs.
+
+ if not os.path.exists(json_filepath): print(f"Error: JSON no encontrado: {json_filepath}"); return
+ print(f"Cargando JSON desde: {json_filepath}")
+ try:
+ with open(json_filepath, "r", encoding="utf-8") as f: data = json.load(f)
+ except Exception as e: print(f"Error al cargar JSON: {e}"); traceback.print_exc(); return
+
+ # --- Carga dinámica de procesadores (sin cambios) ---
+ script_dir = os.path.dirname(__file__); processors_dir_path = os.path.join(script_dir, 'processors')
+ processor_map, sorted_processors = load_processors(processors_dir_path)
+ if not processor_map: print("Error crítico: No se cargaron procesadores. Abortando."); return
+
+ # --- Crear mapas de acceso por red (sin cambios) ---
+ network_access_maps = {}
+ # ... (logic to populate network_access_maps remains the same) ...
+ for network in data.get("networks", []):
+ net_id = network["id"]
+ current_access_map = {}
+ # Extraer todos los 'Access' usados en esta red
+ for instr in network.get("logic", []):
+ # Revisar Inputs
+ for _, source in instr.get("inputs", {}).items():
+ sources_to_check = (source if isinstance(source, list) else ([source] if isinstance(source, dict) else []))
+ for src in sources_to_check:
+ if (isinstance(src, dict) and src.get("uid") and src.get("type") in ["variable", "constant"]):
+ current_access_map[src["uid"]] = src
+ # Revisar Outputs
+ for _, dest_list in instr.get("outputs", {}).items():
+ if isinstance(dest_list, list):
+ for dest in dest_list:
+ if (isinstance(dest, dict) and dest.get("uid") and dest.get("type") in ["variable", "constant"]):
+ current_access_map[dest["uid"]] = dest
+ network_access_maps[net_id] = current_access_map
+
+ # --- Inicializar mapa SymPy y SymbolManager por red ---
+ # Cada red puede tener su propio contexto de símbolos si es necesario,
+ # pero un SymbolManager global suele ser suficiente si no hay colisiones graves.
+ # Usaremos uno global por simplicidad ahora.
+ symbol_manager = SymbolManager()
+ sympy_map = {} # Mapa para resultados SymPy intermedios (expresiones)
+
+ max_passes = 30
+ passes = 0
+ processing_complete = False
+
+ print("\n--- Iniciando Bucle de Procesamiento Iterativo (con SymPy y prioridad) ---")
+ while passes < max_passes and not processing_complete:
+ passes += 1
+ made_change_in_base_pass = False # Renombrar: made_change_in_sympy_pass
+ made_change_in_group_pass = False
+ # made_change_in_simplify_pass = False # Ya no existe Fase 3
+ print(f"\n--- Pase {passes} ---")
+ num_processed_this_pass = 0 # Renombrar: num_sympy_processed_this_pass
+ num_grouped_this_pass = 0
+ # num_simplified_this_pass = 0 # Ya no existe Fase 3
+
+ # --- FASE 1: Procesadores Base (Ahora usan SymPy) ---
+ print(f" Fase 1 (SymPy Base - Orden por Prioridad):")
+ num_sympy_processed_this_pass = 0 # Contador específico
+ for processor_info in sorted_processors:
+ current_type_name = processor_info['type_name']
+ func_to_call = processor_info['func']
+
+ for network in data.get("networks", []):
+ network_id = network["id"]
+ access_map = network_access_maps.get(network_id, {})
+ network_logic = network.get("logic", [])
+
+ for instruction in network_logic:
+ instr_uid = instruction.get("instruction_uid")
+ instr_type_original = instruction.get("type", "Unknown")
+
+ # Saltar si ya está procesado con el NUEVO método, es error, o agrupado
+ if (instr_type_original.endswith(SCL_SUFFIX) # Check new suffix
+ or "_error" in instr_type_original
+ or instruction.get("grouped", False)):
+ continue
+
+ # Determinar tipo efectivo (como antes)
+ lookup_key = instr_type_original.lower()
+ effective_type_name = lookup_key
+ if instr_type_original == "Call": # ... (manejo Call FC/FB) ...
+ block_type = instruction.get("block_type", "").upper()
+ if block_type == "FC": effective_type_name = "call_fc"
+ elif block_type == "FB": effective_type_name = "call_fb"
+
+ if effective_type_name == current_type_name:
+ try:
+ # *** Llamar al procesador refactorizado ***
+ # Pasa sympy_map y symbol_manager, no scl_map
+ changed = func_to_call(instruction, network_id, sympy_map, symbol_manager, data) # Pasamos SymbolManager
+ if changed:
+ made_change_in_base_pass = True
+ num_sympy_processed_this_pass += 1
+ except Exception as e:
+ print(f"ERROR(SymPy Base) al procesar {instr_type_original} UID {instr_uid}: {e}")
+ traceback.print_exc()
+ instruction["scl"] = f"// ERROR en SymPy procesador base: {e}"
+ instruction["type"] = instr_type_original + "_error"
+ made_change_in_base_pass = True
+ print(f" -> {num_sympy_processed_this_pass} instrucciones procesadas con SymPy.")
+
+
+ # --- FASE 2: Agrupación IF (Ahora usa SymPy para simplificar) ---
+ # Ejecutar si hubo cambios en base o es el primer pase
+ if made_change_in_base_pass or passes == 1:
+ print(f" Fase 2 (Agrupación IF con Simplificación):")
+ num_grouped_this_pass = 0 # Reiniciar contador
+ for network in data.get("networks", []):
+ network_id = network["id"]
+ # access_map = network_access_maps.get(network_id, {}) # No usado directamente por group_ifs
+ network_logic = network.get("logic", [])
+ # Iterar sobre instrucciones que *pueden* generar condiciones booleanas
+ for instruction in network_logic:
+ # process_group_ifs ahora verifica internamente si la instr. fue procesada
+ try:
+ # *** Llamar a process_group_ifs adaptado ***
+ group_changed = process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data)
+ if group_changed:
+ made_change_in_group_pass = True
+ num_grouped_this_pass += 1
+ except Exception as e:
+ print(f"ERROR(GroupLoop) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}")
+ traceback.print_exc()
+ print(f" -> {num_grouped_this_pass} agrupaciones realizadas.")
+
+ # --- FASE 3 Eliminada ---
+
+ # --- Comprobar si se completó el procesamiento ---
+ # Solo considera Fase 1 (SymPy Base) y Fase 2 (Grouping)
+ if not made_change_in_base_pass and not made_change_in_group_pass:
+ print(f"\n--- No se hicieron más cambios en el pase {passes}. Proceso iterativo completado. ---")
+ processing_complete = True
+ else:
+ # Mensaje de fin de pase actualizado
+ print(f"--- Fin Pase {passes}: {num_sympy_processed_this_pass} proc SymPy, {num_grouped_this_pass} agrup. Continuando...")
+
+ # --- Comprobar límite de pases ---
+ if passes == max_passes and not processing_complete:
+ print(f"\n--- ADVERTENCIA: Límite de {max_passes} pases alcanzado...")
+
+ # --- FIN BUCLE ITERATIVO ---
+
+ # --- Verificación Final ---
+ # La lógica aquí podría necesitar ajustes si el sufijo cambió o si el SCL final
+ # solo se genera en instrucciones terminales.
+ print("\n--- Verificación Final de Instrucciones No Procesadas ---")
+ unprocessed_count = 0
+ unprocessed_details = []
+ ignored_types = ['raw_scl_chunk', 'unsupported_lang']
+
+ for network in data.get("networks", []):
+ network_id = network.get("id", "Unknown ID")
+ network_title = network.get("title", f"Network {network_id}")
+ for instruction in network.get("logic", []):
+ instr_uid = instruction.get("instruction_uid", "Unknown UID")
+ instr_type = instruction.get("type", "Unknown Type")
+ is_grouped = instruction.get("grouped", False)
+ has_final_scl = bool(instruction.get("scl", "").strip()) and not instruction.get("scl", "").strip().startswith("//")
+
+ # Condición revisada: No tiene el sufijo nuevo Y no es error Y no está agrupada Y no es tipo ignorado
+ # Y ADEMÁS, ¿debería tener SCL final si es una instr. terminal?
+ # Simplificación: si no tiene sufijo, no es error, no agrupada, no ignorada -> problema
+ if (not instr_type.endswith(SCL_SUFFIX) and
+ "_error" not in instr_type and
+ not is_grouped and
+ instr_type.lower() not in ignored_types):
+ unprocessed_count += 1
+ unprocessed_details.append(
+ f" - Red '{network_title}' (ID: {network_id}), "
+ f"Instrucción UID: {instr_uid}, Tipo Original: '{instr_type}'"
+ )
+ # Opcional: añadir si tiene SCL o no
+ # unprocessed_details[-1] += f" (Tiene SCL final: {has_final_scl})"
+
+
+ if unprocessed_count > 0:
+ print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones que no fueron procesadas:")
+ for detail in unprocessed_details: print(detail)
+ else:
+ print("INFO: Todas las instrucciones relevantes parecen haber sido procesadas o agrupadas.")
+
+ # --- Guardar JSON Final (sin cambios) ---
+ output_filename = json_filepath.replace("_simplified.json", "_simplified_processed.json")
+ print(f"\nGuardando JSON procesado en: {output_filename}")
+ try:
+ with open(output_filename, "w", encoding="utf-8") as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+ print("Guardado completado.")
+ except Exception as e:
+ print(f"Error Crítico al guardar JSON procesado: {e}")
+ traceback.print_exc()
+
+
+# --- Ejecución (igual que antes) ---
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic.")
+ parser.add_argument(
+ "source_xml_filepath",
+ nargs="?",
+ default="TestLAD.xml",
+ help="Path to the original source XML file (used to derive JSON input name, default: TestLAD.xml)"
+ )
+ args = parser.parse_args()
+
+ xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
+ # Usar directorio del script actual si el XML no tiene ruta, o la ruta del XML si la tiene
+ xml_dir = os.path.dirname(args.source_xml_filepath)
+ input_dir = xml_dir if xml_dir else os.path.dirname(__file__) # Directorio de entrada/salida
+
+ input_json_file = os.path.join(input_dir, f"{xml_filename_base}_simplified.json")
+
+ if not os.path.exists(input_json_file):
+ print(f"Error Fatal: El archivo de entrada JSON simplificado no existe: '{input_json_file}'")
+ print(f"Asegúrate de haber ejecutado 'x1_to_json.py' primero sobre '{args.source_xml_filepath}'.")
+ sys.exit(1)
+ else:
+ process_json_to_scl(input_json_file)
\ No newline at end of file
diff --git a/ToUpload/x3_generate_scl.py b/ToUpload/x3_generate_scl.py
new file mode 100644
index 0000000..66207c4
--- /dev/null
+++ b/ToUpload/x3_generate_scl.py
@@ -0,0 +1,298 @@
+# x3_generate_scl.py
+# -*- coding: utf-8 -*-
+import json
+import os
+import re
+import argparse
+import sys
+import traceback # Importar traceback para errores
+
+# --- Importar Utilidades y Constantes (Asumiendo ubicación) ---
+try:
+ # Intenta importar desde el paquete de procesadores si está estructurado así
+ from processors.processor_utils import format_variable_name
+ # Definir SCL_SUFFIX aquí o importarlo si está centralizado
+ SCL_SUFFIX = "_sympy_processed" # Asegúrate que coincida con x2_process.py
+ GROUPED_COMMENT = "// Logic included in grouped IF" # Opcional, si se usa para filtrar
+except ImportError:
+ print("Advertencia: No se pudo importar 'format_variable_name' desde processors.processor_utils.")
+ print("Usando una implementación local básica (¡PUEDE FALLAR CON NOMBRES COMPLEJOS!).")
+ # Implementación local BÁSICA como fallback (MENOS RECOMENDADA)
+ def format_variable_name(name):
+ if not name: return "_INVALID_NAME_"
+ if name.startswith('"') and name.endswith('"'): return name # Mantener comillas
+ prefix = "#" if name.startswith("#") else ""
+ if prefix: name = name[1:]
+ if name and name[0].isdigit(): name = "_" + name
+ name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
+ return prefix + name
+ SCL_SUFFIX = "_sympy_processed"
+ GROUPED_COMMENT = "// Logic included in grouped IF"
+
+
+# --- Función Principal de Generación SCL ---
+
+def generate_scl(processed_json_filepath, output_scl_filepath):
+ """Genera un archivo SCL a partir del JSON procesado por x2_process (versión SymPy)."""
+
+ 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}")
+ traceback.print_exc()
+ return
+
+ # --- Extracción de Información del Bloque ---
+ block_name = data.get('block_name', 'UnknownBlock')
+ block_number = data.get('block_number')
+ block_lang_original = data.get('language', 'LAD') # Lenguaje original
+ # Determinar tipo de bloque SCL (Asumir FB si no se especifica)
+ # Idealmente, x1_to_json.py guardaría esto en data['block_type_scl'] = 'FC' o 'FB'
+ block_type_scl = data.get('block_type_scl', 'FUNCTION_BLOCK')
+ block_comment = data.get('block_comment', '')
+
+ # Usar format_variable_name para el nombre del bloque en SCL
+ scl_block_name = format_variable_name(block_name)
+ print(f"Generando SCL para {block_type_scl}: {scl_block_name} (Original: {block_name})")
+
+ # --- Identificación de Variables Temporales y Estáticas ---
+ # La detección basada en regex sobre el SCL final debería seguir funcionando
+ temp_vars = set()
+ stat_vars = set()
+ # Regex mejorado para capturar variables temporales que empiezan con # o _temp_
+ # y estáticas (si usas un prefijo como 'stat_' o para bits de memoria de flanco)
+ temp_pattern = re.compile(r'"?#(_temp_[a-zA-Z0-9_]+)"?|"?(_temp_[a-zA-Z0-9_]+)"?') # Captura con o sin #
+ stat_pattern = re.compile(r'"?(stat_[a-zA-Z0-9_]+)"?') # Para memorias de flanco si usan prefijo 'stat_'
+
+ edge_memory_bits = set() # Para detectar bits de memoria de flanco por nombre
+
+ for network in data.get('networks', []):
+ for instruction in network.get('logic', []):
+ scl_code = instruction.get('scl', '')
+ # Buscar también en _edge_mem_update_scl si existe
+ edge_update_code = instruction.get('_edge_mem_update_scl','')
+ code_to_scan = (scl_code if scl_code else '') + '\n' + (edge_update_code if edge_update_code else '')
+
+ if code_to_scan:
+ # Buscar #_temp_... o _temp_...
+ found_temps = temp_pattern.findall(code_to_scan)
+ for temp_tuple in found_temps:
+ # findall devuelve tuplas por los grupos de captura, tomar el no vacío
+ temp_name = next((t for t in temp_tuple if t), None)
+ if temp_name:
+ temp_vars.add("#"+temp_name if not temp_name.startswith("#") else temp_name) # Asegurar que empiece con #
+
+ # Buscar estáticas (ej: stat_...)
+ found_stats = stat_pattern.findall(code_to_scan)
+ stat_vars.update(found_stats)
+
+ # Identificar explícitamente bits de memoria usados por PBox/NBox
+ # Asumiendo que el nombre se guarda en el JSON (requiere ajuste en x1/x2)
+ # if instruction.get("type","").startswith(("PBox", "NBox")):
+ # mem_bit_info = instruction.get("inputs", {}).get("bit")
+ # if mem_bit_info and mem_bit_info.get("type") == "variable":
+ # edge_memory_bits.add(format_variable_name(mem_bit_info.get("name")))
+
+
+ print(f"Variables temporales (#_temp_...) detectadas: {len(temp_vars)}")
+ # Si se detectan memorias de flanco, añadirlas a stat_vars si no tienen prefijo 'stat_'
+ # stat_vars.update(edge_memory_bits - stat_vars) # Añadir solo las nuevas
+ print(f"Variables estáticas (stat_...) detectadas: {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_original}")
+ if block_comment: scl_output.append(f"// Block Comment: {block_comment}")
+ scl_output.append("")
+ scl_output.append(f"{block_type_scl} \"{scl_block_name}\"")
+ scl_output.append("{ S7_Optimized_Access := 'TRUE' }")
+ scl_output.append("VERSION : 0.1")
+ scl_output.append("")
+
+ # Declaraciones de Interfaz (Implementación básica)
+ interface_sections = ["Input", "Output", "InOut", "Static", "Temp", "Constant", "Return"]
+ interface_data = data.get('interface', {})
+
+ for section_name in interface_sections:
+ scl_section_name = section_name
+ # Ajustar nombres de sección para SCL (Static -> STAT, Temp -> TEMP)
+ if section_name == "Static": scl_section_name = "STAT"
+ if section_name == "Temp": scl_section_name = "TEMP" # Usar VAR_TEMP para variables #temp
+
+ vars_in_section = interface_data.get(section_name, [])
+ # No declarar VAR_TEMP aquí, se hará después con las detectadas/originales
+ if section_name == "Temp": continue
+
+ # No declarar VAR_STAT aquí si ya lo hacemos abajo con las detectadas
+ if section_name == "Static" and stat_vars: continue
+
+
+ if vars_in_section or (section_name == "Static" and stat_vars): # Incluir STAT si hay detectadas
+ # Usar VAR para Input/Output/InOut/Constant/Return
+ var_keyword = "VAR" if section_name != "Static" else "VAR_STAT"
+ scl_output.append(f"{var_keyword}_{section_name.upper()}")
+
+ for var in vars_in_section:
+ var_name = var.get('name')
+ var_dtype = var.get('datatype', 'VARIANT') # Default a VARIANT
+ if var_name:
+ # Usar format_variable_name CORRECTO
+ scl_name = format_variable_name(var_name)
+ scl_output.append(f" {scl_name} : {var_dtype};")
+
+ # Declarar stat_vars detectadas si esta es la sección STAT
+ if section_name == "Static" and stat_vars:
+ for var_name in sorted(list(stat_vars)):
+ # Asumir Bool para stat_, podría necesitar inferencia
+ scl_output.append(f" {format_variable_name(var_name)} : Bool; // Auto-detected STAT")
+
+ scl_output.append("END_VAR")
+ scl_output.append("")
+
+ # Declaraciones Estáticas (Si no estaban en la interfaz y se detectaron)
+ # Esto es redundante si la sección VAR_STAT ya se generó arriba
+ # if stat_vars and not interface_data.get("Static"):
+ # scl_output.append("VAR_STAT")
+ # for var_name in sorted(list(stat_vars)):
+ # scl_output.append(f" {format_variable_name(var_name)} : Bool; // Auto-detected STAT")
+ # scl_output.append("END_VAR")
+ # scl_output.append("")
+
+
+ # Declaraciones Temporales (Interfaz Temp + _temp_ detectadas)
+ scl_output.append("VAR_TEMP")
+ declared_temps = set()
+ interface_temps = interface_data.get('Temp', [])
+ if interface_temps:
+ for var in interface_temps:
+ var_name = var.get('name')
+ var_dtype = var.get('datatype', 'VARIANT')
+ if var_name:
+ scl_name = format_variable_name(var_name)
+ scl_output.append(f" {scl_name} : {var_dtype};")
+ declared_temps.add(scl_name) # Marcar como declarada
+
+ # Declarar las _temp_ generadas si no estaban ya en la interfaz Temp
+ if temp_vars:
+ for var_name in sorted(list(temp_vars)):
+ scl_name = format_variable_name(var_name) # #_temp_...
+ if scl_name not in declared_temps:
+ # Inferencia básica de tipo
+ inferred_type = "Bool" # Asumir Bool para la mayoría de temps de lógica
+ # 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)
+ 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', '')
+ network_lang = network.get('language', 'LAD') # O el lenguaje original
+
+ scl_output.append(f" // Network {i+1}: {network_title} (Original Language: {network_lang})")
+ if network_comment:
+ for line in network_comment.splitlines():
+ scl_output.append(f" // {line}")
+ scl_output.append("")
+
+ network_has_code = False
+ # Iterar sobre la 'logica' de la red
+ for instruction in network.get('logic', []):
+ instruction_type = instruction.get("type", "")
+ scl_code = instruction.get('scl', "") # Obtener SCL generado por x2
+
+ # Saltar instrucciones agrupadas
+ if instruction.get("grouped", False):
+ continue
+
+ # Escribir SCL si es un tipo procesado y tiene código relevante
+ # (Ignorar comentarios de depuración de SymPy)
+ if instruction_type.endswith(SCL_SUFFIX) and scl_code:
+ is_internal_sympy_comment_only = scl_code.strip().startswith("// SymPy") or \
+ scl_code.strip().startswith("// PBox SymPy processed") or \
+ scl_code.strip().startswith("// NBox SymPy processed")
+ # O podría ser más genérico: ignorar cualquier línea que solo sea comentario SCL
+ is_only_comment = all(line.strip().startswith("//") for line in scl_code.splitlines())
+
+
+ # Escribir solo si NO es un comentario interno de SymPy O si es un bloque IF (que sí debe escribirse)
+ if not is_only_comment or scl_code.strip().startswith("IF"):
+ network_has_code = True
+ for line in scl_code.splitlines():
+ # Añadir indentación estándar
+ scl_output.append(f" {line}")
+
+ # Incluir también tipos especiales directamente
+ elif instruction_type in ["RAW_SCL_CHUNK", "UNSUPPORTED_LANG"] and scl_code:
+ network_has_code = True
+ for line in scl_code.splitlines():
+ scl_output.append(f" {line}") # Indentar
+
+ # Podríamos añadir comentarios para errores si se desea
+ # elif "_error" in instruction_type:
+ # network_has_code = True
+ # scl_output.append(f" // ERROR processing instruction UID {instruction.get('instruction_uid')}: {instruction.get('scl', 'No details')}")
+
+
+ if network_has_code:
+ scl_output.append("") # Línea en blanco después del código de la red
+ else:
+ scl_output.append(f" // Network did not produce printable SCL code.")
+ scl_output.append("")
+
+ # Fin del bloque
+ scl_output.append("END_FUNCTION_BLOCK") # O END_FUNCTION si es FC
+
+ # --- 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}")
+ traceback.print_exc()
+
+
+# --- Ejecución ---
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Generate final SCL file from processed JSON (SymPy version).")
+ parser.add_argument(
+ "source_xml_filepath",
+ nargs="?",
+ default="TestLAD.xml",
+ help="Path to the original source XML file (used to derive input/output names, default: TestLAD.xml)"
+ )
+ args = parser.parse_args()
+
+ xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
+ # Usar directorio del script si no hay ruta, o la ruta del XML si la tiene
+ xml_dir = os.path.dirname(args.source_xml_filepath)
+ base_dir = xml_dir if xml_dir else os.path.dirname(__file__)
+
+ input_json_file = os.path.join(base_dir, f"{xml_filename_base}_simplified_processed.json")
+ output_scl_file = os.path.join(base_dir, f"{xml_filename_base}_simplified_processed.scl")
+
+ if not os.path.exists(input_json_file):
+ print(f"Error: Processed JSON file not found: '{input_json_file}'")
+ print(f"Ensure 'x2_process.py' ran successfully for '{args.source_xml_filepath}'.")
+ sys.exit(1)
+ else:
+ generate_scl(input_json_file, output_scl_file)
\ No newline at end of file
diff --git a/x1_to_json.py b/x1_to_json.py
index 8b1a128..f594e80 100644
--- a/x1_to_json.py
+++ b/x1_to_json.py
@@ -2,6 +2,7 @@
import json
import argparse
import os
+import re
from lxml import etree
import traceback
from collections import defaultdict
@@ -11,7 +12,8 @@ from collections import defaultdict
ns = {
"iface": "http://www.siemens.com/automation/Openness/SW/Interface/v5",
"flg": "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4",
- "st": "http://www.siemens.com/automation/Openness/SW/NetworkSource/StructuredText/v3", # <--- Added SCL namespace
+ "st": "http://www.siemens.com/automation/Openness/SW/NetworkSource/StructuredText/v3",
+ "stl": "http://www.siemens.com/automation/Openness/SW/NetworkSource/StatementList/v4",
}
@@ -44,7 +46,6 @@ def get_multilingual_text(element, default_lang="en-US", fallback_lang="it-IT"):
print(f"Advertencia: Error extrayendo MultilingualText: {e}")
return ""
-
def get_symbol_name(symbol_element):
# (Sin cambios respecto a la versión anterior)
if symbol_element is None:
@@ -56,7 +57,6 @@ def get_symbol_name(symbol_element):
print(f"Advertencia: Excepción en get_symbol_name: {e}")
return None
-
def parse_access(access_element):
# (Sin cambios respecto a la versión anterior)
if access_element is None:
@@ -149,7 +149,6 @@ def parse_access(access_element):
return info
return info
-
def parse_part(part_element):
# (Sin cambios respecto a la versión anterior)
if part_element is None:
@@ -185,7 +184,6 @@ def parse_part(part_element):
"negated_pins": negated_pins,
}
-
def parse_call(call_element):
# (Mantiene la corrección para DB de instancia)
if call_element is None:
@@ -245,23 +243,24 @@ def parse_call(call_element):
call_data["instance_scope"] = instance_scope
return call_data
-
-# EN x1_to_json.py, junto a otras funciones auxiliares
-
+# SCL (Structured Text) Parser
def reconstruct_scl_from_tokens(st_node):
"""
- Intenta reconstruir una cadena SCL a partir de los elementos hijos
- de un nodo . Es una aproximación y puede no ser perfecta.
+ Reconstruye una cadena SCL a partir de los elementos hijos
+ de un nodo , manejando Tokens, Access (Variables y Constantes),
+ saltos de línea, espacios y comentarios.
"""
if st_node is None:
return "// Error: StructuredText node not found.\n"
scl_parts = []
# Obtener todos los elementos hijos directos en orden
+ # Usamos '*' para obtener todos los hijos y luego filtramos por tag
children = st_node.xpath("./st:*", namespaces=ns)
for elem in children:
+ # Obtener el nombre local de la etiqueta sin el namespace
tag = etree.QName(elem.tag).localname
if tag == "Token":
@@ -269,84 +268,311 @@ def reconstruct_scl_from_tokens(st_node):
elif tag == "Blank":
scl_parts.append(" " * int(elem.get("Num", 1)))
elif tag == "NewLine":
- # Añadir un salto de línea real. strip() al final de la línea actual
- # para evitar espacios extra antes del salto.
scl_parts.append("\n")
elif tag == "Access":
- # Reconstruir acceso (simplificado)
- symbol_parts = []
- # Buscar componentes y puntos dentro del símbolo de este acceso
- symbol_children = elem.xpath(
- ".//st:Component | .//st:Token[@Text='.']", namespaces=ns
- )
- for sym_child in symbol_children:
- sym_tag = etree.QName(sym_child.tag).localname
- if sym_tag == "Component":
- comp_name = sym_child.get("Name", "_ERR_")
- # Comprobar si necesita comillas (podríamos necesitar parsear BooleanAttribute)
- # Simplificación: Añadir comillas si el nombre original parece tenerlas (o siempre para DBs?)
- # Aquí usamos el nombre tal cual viene en el XML por ahora
- # Si el XML tiene true podríamos usarlo
- has_quotes_elem = sym_child.xpath(
- "../st:BooleanAttribute[@Name='HasQuotes']/text()",
- namespaces=ns,
- )
- has_quotes = (
- has_quotes_elem and has_quotes_elem[0].lower() == "true"
- )
+ scope = elem.get("Scope")
+ access_str = f"_{scope}_?" # Fallback
- # Reconstrucción básica: poner comillas si HasQuotes es true o si es el primer componente (posible DB)
- # Esto es heurístico y puede fallar.
- # if has_quotes or (len(symbol_parts) == 0 and '.' not in comp_name): # Asumir primer componente es DB
- # symbol_parts.append(f'"{comp_name}"')
- # else:
- # symbol_parts.append(comp_name)
+ if scope == "GlobalVariable" or scope == "LocalVariable":
+ symbol_elem = elem.xpath("./st:Symbol", namespaces=ns)
+ if symbol_elem:
+ components = symbol_elem[0].xpath("./st:Component | ./st:Token[@Text='.']", namespaces=ns)
+ symbol_text_parts = []
+ for comp in components:
+ comp_tag = etree.QName(comp.tag).localname
+ if comp_tag == "Component":
+ name = comp.get("Name", "_ERR_COMP_")
+ # Comprobar si necesita comillas (requiere BooleanAttribute)
+ # Simplificación: Añadir comillas si el nombre original parece tenerlas
+ has_quotes_elem = comp.xpath("../st:BooleanAttribute[@Name='HasQuotes']/text()", namespaces=ns)
+ has_quotes = has_quotes_elem and has_quotes_elem[0].lower() == "true"
+
+ # Heurística: Usar comillas si HasQuotes=true o si es el primer componente y no es TEMP (#)
+ if has_quotes or (len(symbol_text_parts) == 0 and not name.startswith('#')):
+ symbol_text_parts.append(f'"{name}"')
+ else:
+ symbol_text_parts.append(name)
- # Versión más simple: usar nombre tal cual del XML
- symbol_parts.append(comp_name)
+ # Manejar índices de array (simplificado)
+ index_access = comp.xpath("./st:Access", namespaces=ns)
+ if index_access:
+ indices_text = [reconstruct_scl_from_tokens(idx_node) for idx_node in index_access] # Llamada recursiva
+ symbol_text_parts.append(f"[{','.join(indices_text)}]")
- elif sym_tag == "Token": # Solo nos interesa el punto aquí
- symbol_parts.append(".")
- access_str = "".join(symbol_parts)
+ elif comp_tag == "Token": # Es un punto
+ # Asegurarse de no añadir puntos duplicados si ya están en las partes
+ if symbol_text_parts and symbol_text_parts[-1] != ".":
+ symbol_text_parts.append(".")
+ # Limpiar posibles puntos extra al inicio/final o dobles
+ access_str = "".join(symbol_text_parts).strip('.')
+ access_str = re.sub(r'\.+', '.', access_str) # Reemplazar múltiples puntos con uno solo
- # Manejar llamadas a funciones/FB dentro de Access Scope="Call"
- if elem.get("Scope") == "Call":
- instruction_elem = elem.xpath("./st:Instruction", namespaces=ns)
- if instruction_elem:
- instr_name = instruction_elem[0].get("Name", "_UNKNOWN_CALL_")
- # Reconstrucción básica de parámetros (muy simplificada)
- # Necesitaría parsear Parameters, Tokens '(', ')', ':=', '=>', etc.
- # Por ahora, solo añadimos el nombre y paréntesis vacíos
- access_str = f"{instr_name}()" # Placeholder muy básico
+
+ elif scope == "LiteralConstant":
+ constant_elem = elem.xpath("./st:Constant", namespaces=ns)
+ if constant_elem:
+ val_elem = constant_elem[0].xpath("./st:ConstantValue/text()", namespaces=ns)
+ # **CORRECCIÓN CLAVE**: Extraer el valor y usarlo
+ access_str = val_elem[0] if val_elem else "_ERR_CONSTVAL_"
+ else:
+ access_str = "_ERR_NOCONST_"
+ # Añadir manejo para otros scopes si es necesario (Address, Call, etc.)
+ # elif scope == "Call": ...
+ # elif scope == "Address": ...
scl_parts.append(access_str)
elif tag == "Comment" or tag == "LineComment":
- # Añadir comentarios
- comment_text = elem.text if elem.text else ""
- if tag == "Comment" and "\n" in comment_text: # Comentario multi-línea
- scl_parts.append(f"(*{comment_text}*)")
- else: # Comentario de una línea
- scl_parts.append(f"// {comment_text}")
- else:
- # Ignorar otros tipos de nodos por ahora o añadir manejo específico
- pass
+ # Manejo de comentarios (simplificado - asume texto directo)
+ comment_text = "".join(elem.xpath(".//text()")).strip()
+ if tag == "Comment": # Comentario tipo (* *)
+ scl_parts.append(f"(* {comment_text} *)")
+ else: # Comentario tipo //
+ scl_parts.append(f"// {comment_text}")
+ # else: # Ignorar otros tipos de nodos por ahora
+ # pass
- # Unir todas las partes, limpiar espacios extra alrededor de saltos de línea
+ # Unir todas las partes
full_scl = "".join(scl_parts)
- # Limpieza básica de formato
- lines = [line.rstrip() for line in full_scl.split("\n")]
- # Re-ensamblar, asegurando que líneas vacías (solo espacios previos) se mantengan
+ # Limpieza básica de formato (puede necesitar ajustes)
+ lines = [line.rstrip() for line in full_scl.split('\n')]
cleaned_scl = "\n".join(lines)
# Eliminar múltiples líneas vacías consecutivas (opcional)
# import re
# cleaned_scl = re.sub(r'\n\s*\n', '\n\n', cleaned_scl)
- return cleaned_scl.strip() # Quitar espacios/saltos al inicio/final
+ return cleaned_scl.strip() # Quitar espacios/saltos al inicio/final
+# STL (Statement List) Parser
+
+def get_access_text(access_element):
+ """Reconstruye una representación textual simple de un Access en STL."""
+ if access_element is None:
+ return "_ERR_ACCESS_"
+ scope = access_element.get("Scope")
+
+ # Intenta reconstruir el símbolo
+ # CORREGIDO: Añadido namespaces=ns
+ symbol_elem = access_element.xpath("./stl:Symbol", namespaces=ns)
+ if symbol_elem:
+ # CORREGIDO: Añadido namespaces=ns
+ components = symbol_elem[0].xpath("./stl:Component", namespaces=ns)
+ parts = []
+ for comp in components:
+ name = comp.get("Name", "_ERR_COMP_")
+ # CORREGIDO: Añadido namespaces=ns
+ has_quotes_elem = comp.xpath("../stl:BooleanAttribute[@Name='HasQuotes']/text()", namespaces=ns)
+ has_quotes = has_quotes_elem and has_quotes_elem[0].lower() == "true"
+
+ # Usar nombre tal cual por ahora
+ parts.append(name)
+
+ # Añadir índices si existen
+ # CORREGIDO: Añadido namespaces=ns
+ index_access = comp.xpath("./stl:Access", namespaces=ns)
+ if index_access:
+ indices = [get_access_text(ia) for ia in index_access]
+ parts.append(f"[{','.join(indices)}]")
+
+ return ".".join(parts)
+
+ # Intenta reconstruir constante
+ # CORREGIDO: Añadido namespaces=ns
+ constant_elem = access_element.xpath("./stl:Constant", namespaces=ns)
+ if constant_elem:
+ # CORREGIDO: Añadido namespaces=ns
+ val_elem = constant_elem[0].xpath("./stl:ConstantValue/text()", namespaces=ns)
+ type_elem = constant_elem[0].xpath("./stl:ConstantType/text()", namespaces=ns) # Obtener tipo para mejor formato
+ const_type = type_elem[0] if type_elem else ""
+ const_val = val_elem[0] if val_elem else "_ERR_CONST_"
+ # Añadir prefijo de tipo si es necesario (ej. T# , L#) - Simplificado
+ if const_type == "Time": return f"T#{const_val}"
+ if const_type == "ARef": return f"{const_val}" # No necesita prefijo
+ # Añadir más tipos si es necesario
+ return const_val # Valor directo para otros tipos
+
+ # Intenta reconstruir etiqueta
+ # CORREGIDO: Añadido namespaces=ns
+ label_elem = access_element.xpath("./stl:Label", namespaces=ns)
+ if label_elem:
+ name = label_elem[0].get("Name", "_ERR_LABEL_")
+ return name
+
+ # Intenta reconstruir acceso indirecto (simplificado)
+ # CORREGIDO: Añadido namespaces=ns
+ indirect_elem = access_element.xpath("./stl:Indirect", namespaces=ns)
+ if indirect_elem:
+ reg = indirect_elem[0].get("Register", "AR?")
+ offset_str = indirect_elem[0].get("BitOffset", "0")
+ area = indirect_elem[0].get("Area", "DB")
+ width = indirect_elem[0].get("Width", "X")
+
+ # Convertir BitOffset a formato P#Byte.Bit
+ try:
+ bit_offset = int(offset_str)
+ byte_offset = bit_offset // 8
+ bit_in_byte = bit_offset % 8
+ p_format_offset = f"P#{byte_offset}.{bit_in_byte}"
+ except ValueError:
+ p_format_offset = "P#?.?"
+
+ # Formatear ancho
+ width_map = {"Bit": "X", "Byte": "B", "Word": "W", "Double": "D"}
+ width_char = width_map.get(width, width[0] if width else "?") # Usa primera letra si no mapeado
+
+ return f"{area}{width_char}[{reg},{p_format_offset}]"
+
+ # Intenta reconstruir dirección absoluta
+ # CORREGIDO: Añadido namespaces=ns
+ address_elem = access_element.xpath("./stl:Address", namespaces=ns)
+ if address_elem:
+ area = address_elem[0].get("Area", "??")
+ bit_offset_str = address_elem[0].get("BitOffset", "0")
+ addr_type_str = address_elem[0].get("Type", "Bool") # Obtener tipo para ancho
+ try:
+ bit_offset = int(bit_offset_str)
+ byte_offset = bit_offset // 8
+ bit_in_byte = bit_offset % 8
+ # Determinar ancho basado en tipo (simplificación)
+ addr_width = "X" # Default a Bit
+ if addr_type_str == "Byte": addr_width = "B"
+ elif addr_type_str == "Word": addr_width = "W"
+ elif addr_type_str in ["DWord", "DInt"]: addr_width = "D"
+ # Añadir más tipos si es necesario (Real, etc.)
+
+ # Mapear Area para STL estándar
+ area_map = { "Input": "I", "Output": "Q", "Memory": "M",
+ "PeripheryInput": "PI", "PeripheryOutput": "PQ",
+ "DB": "DB", "DI": "DI", "Local": "L", # L no siempre válido aquí
+ "Timer": "T", "Counter": "C" }
+ stl_area = area_map.get(area, area)
+
+ # Manejar DB/DI que necesitan número de bloque
+ if stl_area in ["DB", "DI"]:
+ block_num = address_elem[0].get("BlockNumber")
+ if block_num:
+ return f"{stl_area}{block_num}.{stl_area}{addr_width}{byte_offset}.{bit_in_byte}" # Ej: DB1.DBX0.1
+ else: # Acceso con registro DB/DI
+ return f"{stl_area}{addr_width}{byte_offset}.{bit_in_byte}" # Ej: DBX0.1
+ elif stl_area in ["T", "C"]:
+ return f"{stl_area}{byte_offset}" # Los timers/contadores solo usan el número
+ else: # I, Q, M, L, PI, PQ
+ return f"{stl_area}{addr_width}{byte_offset}.{bit_in_byte}" # Ej: M10.1, I0.0
+
+ except ValueError:
+ return f"{area}?{bit_offset_str}?"
+
+ return f"_{scope}_?" # Fallback
+
+def get_comment_text(comment_element):
+ """Extrae texto de un LineComment o Comment."""
+ if comment_element is None: return ""
+ # Usar get_multilingual_text si los comentarios son multilingües
+ # Si no, extraer texto directamente
+ ml_texts = comment_element.xpath(".//mlt:MultilingualTextItem/mlt:AttributeList/mlt:Text/text()",
+ namespaces={'mlt': "http://www.siemens.com/automation/Openness/SW/Interface/v5"}) # Asumiendo ns
+ if ml_texts:
+ # Podrías intentar obtener un idioma específico o simplemente el primero
+ return ml_texts[0].strip() if ml_texts else ""
+
+ # Fallback a texto directo si no hay estructura multilingüe
+ text_nodes = comment_element.xpath("./text()")
+ return "".join(text_nodes).strip()
+
+def reconstruct_stl_from_statementlist(statement_list_node):
+ """Reconstruye el código STL como una cadena de texto desde ."""
+ if statement_list_node is None:
+ return "// Error: StatementList node not found.\n"
+
+ stl_lines = []
+ # CORREGIDO: Añadido namespaces=ns
+ statements = statement_list_node.xpath("./stl:StlStatement", namespaces=ns)
+
+ for stmt in statements:
+ line_parts = []
+ line_comment = "" # Comentario al final de la línea
+
+ # 1. Comentarios al inicio de la línea (como líneas separadas //)
+ # CORREGIDO: Añadido namespaces=ns
+ initial_comments = stmt.xpath("child::stl:Comment | child::stl:LineComment", namespaces=ns)
+ for comm in initial_comments:
+ comment_text = get_comment_text(comm)
+ if comment_text:
+ # Dividir comentarios multilínea en varias líneas //
+ for comment_line in comment_text.splitlines():
+ stl_lines.append(f"// {comment_line}")
+
+ # 2. Etiqueta (si existe)
+ # CORREGIDO: Añadido namespaces=ns
+ label_decl = stmt.xpath("./stl:LabelDeclaration", namespaces=ns)
+ label_str = ""
+ if label_decl:
+ # CORREGIDO: Añadido namespaces=ns
+ label_name_nodes = label_decl[0].xpath("./stl:Label/@Name", namespaces=ns)
+ if label_name_nodes:
+ label_str = f"{label_name_nodes[0]}:"
+ # Buscar comentarios DENTRO de LabelDeclaration pero después de Label
+ # CORREGIDO: Añadido namespaces=ns
+ label_comments = label_decl[0].xpath("./stl:Comment | ./stl:LineComment", namespaces=ns)
+ for lcomm in label_comments:
+ comment_text = get_comment_text(lcomm)
+ if comment_text: line_comment += f" // {comment_text}" # Añadir al comentario de línea
+
+ # 3. Token de Instrucción STL
+ # CORREGIDO: Añadido namespaces=ns
+ instruction_token = stmt.xpath("./stl:StlToken", namespaces=ns)
+ instruction_str = ""
+ if instruction_token:
+ token_text = instruction_token[0].get("Text", "_ERR_TOKEN_")
+ instruction_str = token_text
+ # Comentarios asociados directamente al token
+ # CORREGIDO: Añadido namespaces=ns
+ token_comments = instruction_token[0].xpath("./stl:Comment | ./stl:LineComment", namespaces=ns)
+ for tcomm in token_comments:
+ comment_text = get_comment_text(tcomm)
+ if comment_text: line_comment += f" // {comment_text}" # Añadir al comentario de línea
+
+ # 4. Acceso/Operando STL
+ # CORREGIDO: Añadido namespaces=ns
+ access_elem = stmt.xpath("./stl:Access", namespaces=ns)
+ access_str = ""
+ if access_elem:
+ access_text = get_access_text(access_elem[0])
+ access_str = access_text
+ # Comentarios DENTRO del Access (pueden ser de línea o bloque)
+ # CORREGIDO: Añadido namespaces=ns
+ access_comments = access_elem[0].xpath("child::stl:LineComment | child::stl:Comment", namespaces=ns)
+ for acc_comm in access_comments:
+ comment_text = get_comment_text(acc_comm)
+ if comment_text: line_comment += f" // {comment_text}" # Añadir al comentario de línea
+
+ # Construir la línea: Etiqueta (si hay) + Tab + Instrucción + Espacio + Operando (si hay) + Comentario(s)
+ current_line = ""
+ if label_str:
+ current_line += label_str
+ if instruction_str:
+ if current_line: # Si ya había etiqueta, añadir tabulador
+ current_line += "\t"
+ current_line += instruction_str
+ if access_str:
+ if current_line: # Si ya había algo, añadir espacio
+ current_line += " "
+ current_line += access_str
+ if line_comment:
+ # Añadir espacio antes del comentario si hay código en la línea
+ if current_line.strip():
+ current_line += f" {line_comment}"
+ else: # Si la línea estaba vacía (solo comentarios iniciales), poner el comentario de línea
+ current_line = line_comment
+
+ # Añadir la línea construida solo si no está vacía
+ if current_line.strip():
+ stl_lines.append(current_line.rstrip()) # Eliminar espacios finales
+
+ return "\n".join(stl_lines)
+
+# --- Main Parsing Function ---
-# --- Función parse_network con XPath corregido para Title/Comment ---
-# --- Función parse_network MODIFICADA (maneja multi-destino en Wire) ---
def parse_network(network_element):
"""
Parsea una red, extrae lógica y añade conexiones EN implícitas.
@@ -792,8 +1018,6 @@ def parse_network(network_element):
"logic": network_logic_final,
}
-
-# --- Función Principal convert_xml_to_json (sin cambios en su flujo general) ---
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):
@@ -989,6 +1213,38 @@ def convert_xml_to_json(xml_filepath, json_filepath):
],
}
+ # --- NUEVO MANEJO STL ---
+ elif programming_language == "STL":
+ statement_list_node = (
+ network_source_node.xpath("./stl:StatementList", namespaces=ns)
+ if network_source_node is not None
+ else None
+ )
+
+ reconstructed_stl = f"// STL extraction failed for Network {network_id}: StatementList node not found.\n"
+ if statement_list_node:
+ print(f" Reconstruyendo STL desde StatementList para red {network_id}...")
+ # Llama a la nueva función de reconstrucción STL
+ reconstructed_stl = reconstruct_stl_from_statementlist(statement_list_node[0])
+ # print(f" ... STL reconstruido (parcial):\n{reconstructed_stl[:200]}...") # Preview opcional
+ else:
+ print(f" Advertencia: No se encontró nodo para red STL {network_id}.")
+
+ # Guardar como un chunk de texto crudo
+ parsed_network_data = {
+ "id": network_id,
+ "title": network_title,
+ "comment": network_comment,
+ "language": "STL", # Indicar que es STL
+ "logic": [
+ {
+ "instruction_uid": f"STL_{network_id}", # UID inventado
+ "type": "RAW_STL_CHUNK", # Nuevo tipo para identificarlo
+ "stl": reconstructed_stl, # Guardar el texto reconstruido
+ }
+ ],
+ }
+
elif programming_language in ["LAD", "FBD"]:
# Para LAD/FBD, llamar a parse_network (que espera FlgNet dentro de NetworkSource)
# parse_network ya maneja su propio título/comentario si es necesario, pero podemos pasar los extraídos
@@ -1066,8 +1322,6 @@ def convert_xml_to_json(xml_filepath, json_filepath):
traceback.print_exc()
print("--- Fin Traceback ---")
-
-# --- Punto de Entrada Principal ---
if __name__ == "__main__":
# Imports necesarios solo para la ejecución como script principal
import argparse
diff --git a/x2_process.py b/x2_process.py
index d7a10a1..bef6a4f 100644
--- a/x2_process.py
+++ b/x2_process.py
@@ -23,8 +23,9 @@ SCL_SUFFIX = "_sympy_processed" # New suffix to indicate processing method
GROUPED_COMMENT = "// Logic included in grouped IF"
SIMPLIFIED_IF_COMMENT = "// Simplified IF condition by script" # May still be useful
-
-# Global data variable
+# Global data dictionary (consider passing 'data' as argument if needed elsewhere)
+# It's currently used by process_group_ifs implicitly via the outer scope,
+# which works but passing it explicitly might be cleaner.
data = {}
def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
@@ -33,6 +34,7 @@ def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
y, si habilitan un grupo (>1) de bloques funcionales (con SCL ya generado),
construye el bloque IF agrupado CON LA CONDICIÓN SIMPLIFICADA.
Modifica el campo 'scl' de la instrucción generadora de condición.
+ (Esta es la implementación de la función como la tenías en el archivo original)
"""
instr_uid = instruction["instruction_uid"]
instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX, "").replace("_error", "")
@@ -215,45 +217,47 @@ def load_processors(processors_dir="processors"):
# Devolver el mapa (para lookup rápido si es necesario) y la lista ordenada
return processor_map, processor_list_sorted
-# --- Bucle Principal de Procesamiento (Modificado) ---
+# --- Bucle Principal de Procesamiento (Modificado para STL) ---
def process_json_to_scl(json_filepath):
"""
Lee el JSON simplificado, aplica los procesadores dinámicamente cargados
- siguiendo un orden de prioridad, y guarda el JSON procesado.
+ siguiendo un orden de prioridad (ignorando redes STL), y guarda el JSON procesado.
"""
- global data # Necesario si process_group_ifs (definido fuera) accede a data globalmente.
- # Si process_group_ifs está definida DENTRO de process_json_to_scl,
- # no necesitarías global, ya que accedería a la 'data' local.
- # Lo más limpio es definir process_group_ifs fuera y pasarle 'data'
- # como argumento (como ya se hace). Así que 'global data' aquí es probablemente innecesario.
- # Eliminémoslo por ahora y aseguremos que data se pasa a process_group_ifs.
+ global data # Necesario para que load_processors y process_group_ifs (definidas fuera) puedan acceder a ella.
+ # Considerar pasar 'data' como argumento si es posible refactorizar.
- if not os.path.exists(json_filepath): print(f"Error: JSON no encontrado: {json_filepath}"); return
+ if not os.path.exists(json_filepath):
+ print(f"Error: JSON no encontrado: {json_filepath}")
+ return
print(f"Cargando JSON desde: {json_filepath}")
try:
- with open(json_filepath, "r", encoding="utf-8") as f: data = json.load(f)
- except Exception as e: print(f"Error al cargar JSON: {e}"); traceback.print_exc(); return
+ with open(json_filepath, "r", encoding="utf-8") as f:
+ data = json.load(f) # Carga en 'data' global
+ except Exception as e:
+ print(f"Error al cargar JSON: {e}")
+ traceback.print_exc()
+ return
- # --- Carga dinámica de procesadores (sin cambios) ---
- script_dir = os.path.dirname(__file__); processors_dir_path = os.path.join(script_dir, 'processors')
+ # --- Carga dinámica de procesadores ---
+ script_dir = os.path.dirname(__file__)
+ processors_dir_path = os.path.join(script_dir, 'processors')
processor_map, sorted_processors = load_processors(processors_dir_path)
- if not processor_map: print("Error crítico: No se cargaron procesadores. Abortando."); return
+ if not processor_map:
+ print("Error crítico: No se cargaron procesadores. Abortando.")
+ return
- # --- Crear mapas de acceso por red (sin cambios) ---
+ # --- Crear mapas de acceso por red ---
network_access_maps = {}
- # ... (logic to populate network_access_maps remains the same) ...
+ # (La lógica para llenar network_access_maps no cambia, puedes copiarla de tu original)
for network in data.get("networks", []):
net_id = network["id"]
current_access_map = {}
- # Extraer todos los 'Access' usados en esta red
for instr in network.get("logic", []):
- # Revisar Inputs
for _, source in instr.get("inputs", {}).items():
sources_to_check = (source if isinstance(source, list) else ([source] if isinstance(source, dict) else []))
for src in sources_to_check:
if (isinstance(src, dict) and src.get("uid") and src.get("type") in ["variable", "constant"]):
current_access_map[src["uid"]] = src
- # Revisar Outputs
for _, dest_list in instr.get("outputs", {}).items():
if isinstance(dest_list, list):
for dest in dest_list:
@@ -261,12 +265,9 @@ def process_json_to_scl(json_filepath):
current_access_map[dest["uid"]] = dest
network_access_maps[net_id] = current_access_map
- # --- Inicializar mapa SymPy y SymbolManager por red ---
- # Cada red puede tener su propio contexto de símbolos si es necesario,
- # pero un SymbolManager global suele ser suficiente si no hay colisiones graves.
- # Usaremos uno global por simplicidad ahora.
+ # --- Inicializar mapa SymPy y SymbolManager ---
symbol_manager = SymbolManager()
- sympy_map = {} # Mapa para resultados SymPy intermedios (expresiones)
+ sympy_map = {}
max_passes = 30
passes = 0
@@ -275,23 +276,27 @@ def process_json_to_scl(json_filepath):
print("\n--- Iniciando Bucle de Procesamiento Iterativo (con SymPy y prioridad) ---")
while passes < max_passes and not processing_complete:
passes += 1
- made_change_in_base_pass = False # Renombrar: made_change_in_sympy_pass
+ made_change_in_base_pass = False
made_change_in_group_pass = False
- # made_change_in_simplify_pass = False # Ya no existe Fase 3
print(f"\n--- Pase {passes} ---")
- num_processed_this_pass = 0 # Renombrar: num_sympy_processed_this_pass
+ num_sympy_processed_this_pass = 0
num_grouped_this_pass = 0
- # num_simplified_this_pass = 0 # Ya no existe Fase 3
- # --- FASE 1: Procesadores Base (Ahora usan SymPy) ---
+ # --- FASE 1: Procesadores Base (Ignorando STL) ---
print(f" Fase 1 (SymPy Base - Orden por Prioridad):")
- num_sympy_processed_this_pass = 0 # Contador específico
+ num_sympy_processed_this_pass = 0
for processor_info in sorted_processors:
current_type_name = processor_info['type_name']
func_to_call = processor_info['func']
for network in data.get("networks", []):
network_id = network["id"]
+ network_lang = network.get("language", "LAD") # Obtener lenguaje de la red
+
+ # *** IGNORAR REDES STL EN ESTA FASE ***
+ if network_lang == "STL":
+ continue # Saltar al siguiente network
+
access_map = network_access_maps.get(network_id, {})
network_logic = network.get("logic", [])
@@ -299,25 +304,26 @@ def process_json_to_scl(json_filepath):
instr_uid = instruction.get("instruction_uid")
instr_type_original = instruction.get("type", "Unknown")
- # Saltar si ya está procesado con el NUEVO método, es error, o agrupado
- if (instr_type_original.endswith(SCL_SUFFIX) # Check new suffix
+ # Saltar si ya procesado, error, agrupado o es chunk STL/SCL/Unsupported
+ if (instr_type_original.endswith(SCL_SUFFIX)
or "_error" in instr_type_original
- or instruction.get("grouped", False)):
+ or instruction.get("grouped", False)
+ or instr_type_original in ["RAW_STL_CHUNK", "RAW_SCL_CHUNK", "UNSUPPORTED_LANG"]):
continue
# Determinar tipo efectivo (como antes)
lookup_key = instr_type_original.lower()
effective_type_name = lookup_key
- if instr_type_original == "Call": # ... (manejo Call FC/FB) ...
+ if instr_type_original == "Call":
block_type = instruction.get("block_type", "").upper()
if block_type == "FC": effective_type_name = "call_fc"
elif block_type == "FB": effective_type_name = "call_fb"
+ # Llamar al procesador si coincide el tipo
if effective_type_name == current_type_name:
try:
- # *** Llamar al procesador refactorizado ***
- # Pasa sympy_map y symbol_manager, no scl_map
- changed = func_to_call(instruction, network_id, sympy_map, symbol_manager, data) # Pasamos SymbolManager
+ # Pasa sympy_map, symbol_manager y data
+ changed = func_to_call(instruction, network_id, sympy_map, symbol_manager, data)
if changed:
made_change_in_base_pass = True
num_sympy_processed_this_pass += 1
@@ -326,24 +332,26 @@ def process_json_to_scl(json_filepath):
traceback.print_exc()
instruction["scl"] = f"// ERROR en SymPy procesador base: {e}"
instruction["type"] = instr_type_original + "_error"
- made_change_in_base_pass = True
- print(f" -> {num_sympy_processed_this_pass} instrucciones procesadas con SymPy.")
+ made_change_in_base_pass = True # Marcar cambio aunque sea error
+ print(f" -> {num_sympy_processed_this_pass} instrucciones (no STL) procesadas con SymPy.")
- # --- FASE 2: Agrupación IF (Ahora usa SymPy para simplificar) ---
- # Ejecutar si hubo cambios en base o es el primer pase
+ # --- FASE 2: Agrupación IF (Ignorando STL) ---
if made_change_in_base_pass or passes == 1:
print(f" Fase 2 (Agrupación IF con Simplificación):")
- num_grouped_this_pass = 0 # Reiniciar contador
+ num_grouped_this_pass = 0
for network in data.get("networks", []):
network_id = network["id"]
- # access_map = network_access_maps.get(network_id, {}) # No usado directamente por group_ifs
+ network_lang = network.get("language", "LAD") # Obtener lenguaje
+
+ # *** IGNORAR REDES STL EN ESTA FASE ***
+ if network_lang == "STL":
+ continue # Saltar red STL
+
network_logic = network.get("logic", [])
- # Iterar sobre instrucciones que *pueden* generar condiciones booleanas
for instruction in network_logic:
- # process_group_ifs ahora verifica internamente si la instr. fue procesada
try:
- # *** Llamar a process_group_ifs adaptado ***
+ # Llama a process_group_ifs (que necesita acceso a 'data' global o pasado)
group_changed = process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data)
if group_changed:
made_change_in_group_pass = True
@@ -351,17 +359,14 @@ def process_json_to_scl(json_filepath):
except Exception as e:
print(f"ERROR(GroupLoop) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}")
traceback.print_exc()
- print(f" -> {num_grouped_this_pass} agrupaciones realizadas.")
+ print(f" -> {num_grouped_this_pass} agrupaciones realizadas (en redes no STL).")
- # --- FASE 3 Eliminada ---
# --- Comprobar si se completó el procesamiento ---
- # Solo considera Fase 1 (SymPy Base) y Fase 2 (Grouping)
if not made_change_in_base_pass and not made_change_in_group_pass:
print(f"\n--- No se hicieron más cambios en el pase {passes}. Proceso iterativo completado. ---")
processing_complete = True
else:
- # Mensaje de fin de pase actualizado
print(f"--- Fin Pase {passes}: {num_sympy_processed_this_pass} proc SymPy, {num_grouped_this_pass} agrup. Continuando...")
# --- Comprobar límite de pases ---
@@ -370,46 +375,45 @@ def process_json_to_scl(json_filepath):
# --- FIN BUCLE ITERATIVO ---
- # --- Verificación Final ---
- # La lógica aquí podría necesitar ajustes si el sufijo cambió o si el SCL final
- # solo se genera en instrucciones terminales.
+ # --- Verificación Final (Ajustada para RAW_STL_CHUNK) ---
print("\n--- Verificación Final de Instrucciones No Procesadas ---")
unprocessed_count = 0
unprocessed_details = []
- ignored_types = ['raw_scl_chunk', 'unsupported_lang']
+ # Añadir RAW_STL_CHUNK a los tipos ignorados
+ ignored_types = ['raw_scl_chunk', 'unsupported_lang', 'raw_stl_chunk'] # Añadido raw_stl_chunk
for network in data.get("networks", []):
network_id = network.get("id", "Unknown ID")
network_title = network.get("title", f"Network {network_id}")
+ network_lang = network.get("language", "LAD") # Obtener lenguaje
+
+ # No verificar instrucciones dentro de redes STL, ya que no se procesan
+ if network_lang == "STL":
+ continue
+
for instruction in network.get("logic", []):
instr_uid = instruction.get("instruction_uid", "Unknown UID")
instr_type = instruction.get("type", "Unknown Type")
is_grouped = instruction.get("grouped", False)
- has_final_scl = bool(instruction.get("scl", "").strip()) and not instruction.get("scl", "").strip().startswith("//")
- # Condición revisada: No tiene el sufijo nuevo Y no es error Y no está agrupada Y no es tipo ignorado
- # Y ADEMÁS, ¿debería tener SCL final si es una instr. terminal?
- # Simplificación: si no tiene sufijo, no es error, no agrupada, no ignorada -> problema
+ # Condición revisada para ignorar los chunks crudos
if (not instr_type.endswith(SCL_SUFFIX) and
"_error" not in instr_type and
not is_grouped and
- instr_type.lower() not in ignored_types):
+ instr_type.lower() not in ignored_types): # Verifica contra lista actualizada
unprocessed_count += 1
unprocessed_details.append(
- f" - Red '{network_title}' (ID: {network_id}), "
- f"Instrucción UID: {instr_uid}, Tipo Original: '{instr_type}'"
+ f" - Red '{network_title}' (ID: {network_id}, Lang: {network_lang}), "
+ f"Instrucción UID: {instr_uid}, Tipo: '{instr_type}'"
)
- # Opcional: añadir si tiene SCL o no
- # unprocessed_details[-1] += f" (Tiene SCL final: {has_final_scl})"
-
if unprocessed_count > 0:
- print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones que no fueron procesadas:")
+ print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones (no STL) que parecen no haber sido procesadas:")
for detail in unprocessed_details: print(detail)
else:
- print("INFO: Todas las instrucciones relevantes parecen haber sido procesadas o agrupadas.")
+ print("INFO: Todas las instrucciones relevantes (no STL) parecen haber sido procesadas o agrupadas.")
- # --- Guardar JSON Final (sin cambios) ---
+ # --- Guardar JSON Final ---
output_filename = json_filepath.replace("_simplified.json", "_simplified_processed.json")
print(f"\nGuardando JSON procesado en: {output_filename}")
try:
@@ -420,8 +424,7 @@ def process_json_to_scl(json_filepath):
print(f"Error Crítico al guardar JSON procesado: {e}")
traceback.print_exc()
-
-# --- Ejecución (igual que antes) ---
+# --- Ejecución (sin cambios) ---
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic.")
parser.add_argument(
@@ -432,6 +435,48 @@ if __name__ == "__main__":
)
args = parser.parse_args()
+ xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
+ xml_dir = os.path.dirname(args.source_xml_filepath)
+ input_dir = xml_dir if xml_dir else os.path.dirname(__file__)
+
+ input_json_file = os.path.join(input_dir, f"{xml_filename_base}_simplified.json")
+
+ if not os.path.exists(input_json_file):
+ print(f"Error Fatal: El archivo de entrada JSON simplificado no existe: '{input_json_file}'")
+ print(f"Asegúrate de haber ejecutado 'x1_to_json.py' primero sobre '{args.source_xml_filepath}'.")
+ sys.exit(1)
+ else:
+ process_json_to_scl(input_json_file)
+ parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic.")
+ parser.add_argument(
+ "source_xml_filepath",
+ nargs="?",
+ default="TestLAD.xml",
+ help="Path to the original source XML file (used to derive JSON input name, default: TestLAD.xml)"
+ )
+ args = parser.parse_args()
+
+ xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
+ xml_dir = os.path.dirname(args.source_xml_filepath)
+ input_dir = xml_dir if xml_dir else os.path.dirname(__file__)
+
+ input_json_file = os.path.join(input_dir, f"{xml_filename_base}_simplified.json")
+
+ if not os.path.exists(input_json_file):
+ print(f"Error Fatal: El archivo de entrada JSON simplificado no existe: '{input_json_file}'")
+ print(f"Asegúrate de haber ejecutado 'x1_to_json.py' primero sobre '{args.source_xml_filepath}'.")
+ sys.exit(1)
+ else:
+ process_json_to_scl(input_json_file)
+ parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic.")
+ parser.add_argument(
+ "source_xml_filepath",
+ nargs="?",
+ default="TestLAD.xml",
+ help="Path to the original source XML file (used to derive JSON input name, default: TestLAD.xml)"
+ )
+ args = parser.parse_args()
+
xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
# Usar directorio del script actual si el XML no tiene ruta, o la ruta del XML si la tiene
xml_dir = os.path.dirname(args.source_xml_filepath)
diff --git a/x3_generate_scl.py b/x3_generate_scl.py
index 66207c4..edea599 100644
--- a/x3_generate_scl.py
+++ b/x3_generate_scl.py
@@ -212,49 +212,73 @@ def generate_scl(processed_json_filepath, output_scl_filepath):
scl_output.append("")
network_has_code = False
- # Iterar sobre la 'logica' de la red
- for instruction in network.get('logic', []):
- instruction_type = instruction.get("type", "")
- scl_code = instruction.get('scl', "") # Obtener SCL generado por x2
- # Saltar instrucciones agrupadas
- if instruction.get("grouped", False):
- continue
+ # --- NUEVO MANEJO STL con formato Markdown ---
+ if network_lang == "STL":
+ network_has_code = True # Marcar que la red tiene contenido
+ if network.get('logic') and isinstance(network['logic'], list) and len(network['logic']) > 0:
+ stl_chunk = network['logic'][0]
+ if stl_chunk.get("type") == "RAW_STL_CHUNK" and "stl" in stl_chunk:
+ raw_stl_code = stl_chunk["stl"]
+ # Añadir marcador de inicio (como comentario SCL para evitar errores)
+ scl_output.append(f" {'//'} ```STL") # Doble '//' para asegurar que sea comentario
+ # Escribir el código STL crudo, indentado
+ for stl_line in raw_stl_code.splitlines():
+ # Añadir indentación estándar de SCL
+ scl_output.append(f" {stl_line}") # <-- STL sin comentar
+ # Añadir marcador de fin (como comentario SCL)
+ scl_output.append(f" {'//'} ```")
+ else:
+ scl_output.append(" // ERROR: Contenido STL inesperado en JSON.")
+ else:
+ scl_output.append(" // ERROR: No se encontró lógica STL en JSON para esta red.")
+ scl_output.append("") # Línea en blanco después de la red STL
+ # --- FIN NUEVO MANEJO STL con formato Markdown ---
+ else:
- # Escribir SCL si es un tipo procesado y tiene código relevante
- # (Ignorar comentarios de depuración de SymPy)
- if instruction_type.endswith(SCL_SUFFIX) and scl_code:
- is_internal_sympy_comment_only = scl_code.strip().startswith("// SymPy") or \
- scl_code.strip().startswith("// PBox SymPy processed") or \
- scl_code.strip().startswith("// NBox SymPy processed")
- # O podría ser más genérico: ignorar cualquier línea que solo sea comentario SCL
- is_only_comment = all(line.strip().startswith("//") for line in scl_code.splitlines())
+ # Iterar sobre la 'logica' de la red
+ for instruction in network.get('logic', []):
+ instruction_type = instruction.get("type", "")
+ scl_code = instruction.get('scl', "") # Obtener SCL generado por x2
+
+ # Saltar instrucciones agrupadas
+ if instruction.get("grouped", False):
+ continue
+
+ # Escribir SCL si es un tipo procesado y tiene código relevante
+ # (Ignorar comentarios de depuración de SymPy)
+ if instruction_type.endswith(SCL_SUFFIX) and scl_code:
+ is_internal_sympy_comment_only = scl_code.strip().startswith("// SymPy") or \
+ scl_code.strip().startswith("// PBox SymPy processed") or \
+ scl_code.strip().startswith("// NBox SymPy processed")
+ # O podría ser más genérico: ignorar cualquier línea que solo sea comentario SCL
+ is_only_comment = all(line.strip().startswith("//") for line in scl_code.splitlines())
- # Escribir solo si NO es un comentario interno de SymPy O si es un bloque IF (que sí debe escribirse)
- if not is_only_comment or scl_code.strip().startswith("IF"):
+ # Escribir solo si NO es un comentario interno de SymPy O si es un bloque IF (que sí debe escribirse)
+ if not is_only_comment or scl_code.strip().startswith("IF"):
+ network_has_code = True
+ for line in scl_code.splitlines():
+ # Añadir indentación estándar
+ scl_output.append(f" {line}")
+
+ # Incluir también tipos especiales directamente
+ elif instruction_type in ["RAW_SCL_CHUNK", "UNSUPPORTED_LANG"] and scl_code:
network_has_code = True
for line in scl_code.splitlines():
- # Añadir indentación estándar
- scl_output.append(f" {line}")
+ scl_output.append(f" {line}") # Indentar
- # Incluir también tipos especiales directamente
- elif instruction_type in ["RAW_SCL_CHUNK", "UNSUPPORTED_LANG"] and scl_code:
- network_has_code = True
- for line in scl_code.splitlines():
- scl_output.append(f" {line}") # Indentar
-
- # Podríamos añadir comentarios para errores si se desea
- # elif "_error" in instruction_type:
- # network_has_code = True
- # scl_output.append(f" // ERROR processing instruction UID {instruction.get('instruction_uid')}: {instruction.get('scl', 'No details')}")
+ # Podríamos añadir comentarios para errores si se desea
+ # elif "_error" in instruction_type:
+ # network_has_code = True
+ # scl_output.append(f" // ERROR processing instruction UID {instruction.get('instruction_uid')}: {instruction.get('scl', 'No details')}")
- if network_has_code:
- scl_output.append("") # Línea en blanco después del código de la red
- else:
- scl_output.append(f" // Network did not produce printable SCL code.")
- scl_output.append("")
+ if network_has_code:
+ scl_output.append("") # Línea en blanco después del código de la red
+ else:
+ scl_output.append(f" // Network did not produce printable SCL code.")
+ scl_output.append("")
# Fin del bloque
scl_output.append("END_FUNCTION_BLOCK") # O END_FUNCTION si es FC