Compare commits
2 Commits
98dbc7a5d7
...
c22f98d865
Author | SHA1 | Date |
---|---|---|
|
c22f98d865 | |
|
9b192a91fd |
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -6,13 +6,8 @@ FUNCTION_BLOCK "BlenderCtrl__Main"
|
|||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
END_VAR
|
||||
|
||||
VAR_IN_OUT
|
||||
VAR_RETURN
|
||||
Ret_Val : Void;
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
|
@ -43,13 +38,12 @@ BEGIN
|
|||
|
||||
// Network 4: Emergency Pressed (Original Language: LAD)
|
||||
|
||||
// Logic moved to Coil 26
|
||||
"gEmergencyPressed" := NOT "gIN_VoltageOk" AND "M19000";
|
||||
"M19000" := "gIN_VoltageOk"; // N_TRIG("gIN_VoltageOk")
|
||||
"gEmergencyPressed" := "M19000" AND NOT "gIN_VoltageOk";
|
||||
"M19000" := "gIN_VoltageOk"; // N_TRIG("gIN_VoltageOk") - Mem: "M19000"
|
||||
|
||||
// Network 5: Air and CO2 pressure ok and auxiliary ok (Original Language: LAD)
|
||||
|
||||
"gBlenderSuppliesOk" := (("gIN_LinePressCO2Ok" OR ("gWorkshopTest" AND (NOT "gWorkshop_Co2_Presence")) AND (NOT "gWorkshop_CIP_Signals")) AND "HMI_Digital"."_PAL_S11"."Filtered") OR ("gIN_LinePressCO2Ok" OR ("gWorkshopTest" AND (NOT "gWorkshop_Co2_Presence")) AND (NOT "gWorkshop_CIP_Signals")) AND (NOT "Disable_Bit") AND "gIN_VoltageOk";
|
||||
"gBlenderSuppliesOk" := ("gIN_VoltageOk" AND "gIN_LinePressCO2Ok" AND "HMI_Digital"."_PAL_S11"."Filtered") OR ("gIN_VoltageOk" AND "gIN_LinePressCO2Ok" AND NOT "Disable_Bit") OR ("gIN_VoltageOk" AND "gWorkshopTest" AND "HMI_Digital"."_PAL_S11"."Filtered" AND NOT "gWorkshop_Co2_Presence" AND NOT "gWorkshop_CIP_Signals") OR ("gIN_VoltageOk" AND "gWorkshopTest" AND NOT "gWorkshop_Co2_Presence" AND NOT "gWorkshop_CIP_Signals" AND NOT "Disable_Bit");
|
||||
|
||||
// Network 6: Blender State Num (Original Language: LAD)
|
||||
|
||||
|
@ -57,7 +51,7 @@ BEGIN
|
|||
|
||||
// Network 7: Delay Power On (Original Language: LAD)
|
||||
|
||||
"mDelayPowerOnTmr"(IN := "FirstScan", PT := S5T#2S); // TODO: Declarar "mDelayPowerOnTmr" : TP; en VAR_STAT o VAR
|
||||
"mDelayPowerOnTmr"(IN := "FirstScan", PT := S5T#2S); // TODO: Declarar "mDelayPowerOnTmr" : TP;
|
||||
|
||||
// Network 8: Production Mode (Original Language: LAD)
|
||||
|
||||
|
@ -65,56 +59,56 @@ BEGIN
|
|||
|
||||
// Network 9: CIp Mode (Original Language: LAD)
|
||||
|
||||
"gBlenderCIPMode" := (NOT "HMI_Variables_Status"."System"."Blender_Prod_CIP");
|
||||
IF (NOT "HMI_Variables_Status"."System"."Blender_Prod_CIP") THEN
|
||||
"gBlenderCIPMode" := NOT "HMI_Variables_Status"."System"."Blender_Prod_CIP";
|
||||
IF NOT "HMI_Variables_Status"."System"."Blender_Prod_CIP" THEN
|
||||
"HMI_Variables_Status"."Procedures"."BlenderStateNum" := 19;
|
||||
END_IF;
|
||||
|
||||
// Network 10: Error Faults (Original Language: LAD)
|
||||
|
||||
IF (NOT "AUX FALSE") THEN
|
||||
IF NOT "AUX FALSE" THEN
|
||||
"HMI_Variables_Status"."Meters"."QTM3012_PRD_Fault" := FALSE;
|
||||
END_IF;
|
||||
IF (NOT "AUX FALSE") THEN
|
||||
IF NOT "AUX FALSE" THEN
|
||||
"gmPDS2000_Error_Fault" := FALSE;
|
||||
END_IF;
|
||||
IF (NOT "AUX FALSE") THEN
|
||||
IF NOT "AUX FALSE" THEN
|
||||
"HMI_Variables_Status"."Meters"."QTM3012_PRD_Run" := FALSE;
|
||||
END_IF;
|
||||
IF (NOT "AUX FALSE") THEN
|
||||
IF NOT "AUX FALSE" THEN
|
||||
"gNoFreezeProductMeter" := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 11: Filler Bottle Count Used to push Product (Original Language: LAD)
|
||||
|
||||
"System_RunOut_Variables"."ProdPipeRunOutFillerBott" := (NOT "System_RunOut_Variables"."ProdPipeRunOutWaterCount");
|
||||
"System_RunOut_Variables"."ProdPipeRunOutFillerBott" := NOT "System_RunOut_Variables"."ProdPipeRunOutWaterCount";
|
||||
|
||||
// Network 12: Water Bypass Enable (Original Language: LAD)
|
||||
|
||||
"gStillWaterByPassEn" := (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND (NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation"))) AND "Blender_Variables_Pers"."gWaterRecipe") AND (NOT "Blender_Variables_Pers"."gCarboStillRecipe");
|
||||
"gStillWaterByPassEn" := ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" AND "Blender_Variables_Pers"."gWaterRecipe" AND NOT "Blender_Variables_Pers"."gCarboStillRecipe") OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_ByPassDeair" AND "Blender_Variables_Pers"."gWaterRecipe" AND NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Deaireation" AND NOT "Blender_Variables_Pers"."gCarboStillRecipe");
|
||||
|
||||
// Network 13: Still Water Bypass (Original Language: LAD)
|
||||
|
||||
"gBlendFiStillWaterByPass" := (("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass") AND "Blender_Variables_Pers"."gWaterRecipe") AND (NOT "Blender_Variables_Pers"."gCarboStillRecipe");
|
||||
"gBlendFiStillWaterByPass" := "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_StillWaterByPass" AND "Blender_Variables_Pers"."gWaterRecipe" AND "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND NOT "Blender_Variables_Pers"."gCarboStillRecipe";
|
||||
|
||||
// Network 14: Manual Syrup Drain Valve Open - Operator Alarm (Original Language: LAD)
|
||||
|
||||
"gHVP301_Open" := (("gSyrupRoomEn" AND (NOT "gIN_HVP301_Aux")) AND (NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled") AND "Procedure_Variables"."FTP302Line_Preparation"."Done") AND (NOT "Procedure_Variables"."Syr_RunOut"."Done") OR (((("gSyrupRoomEn" AND (NOT "gIN_HVP301_Aux")) AND "gBlenderCIPMode") AND "gIN_CIP_CIPRunning") AND "Procedure_Variables"."Blender_Run"."Running");
|
||||
"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 15: Manual Syrup Drain Valve Open - Operator Alarm (Original Language: LAD)
|
||||
|
||||
"mHVM302_Dly"(IN := "gIN_HVM302_Aux", PT := S5T#1S); // TODO: Declarar "mHVM302_Dly" : TON; en VAR_STAT o VAR
|
||||
"mHVM302_Dly"(IN := "gIN_HVM302_Aux", PT := S5T#1S); // TODO: Declarar "mHVM302_Dly" : TON;
|
||||
"gHVM302_Open" := "mHVM302_Dly".Q;
|
||||
|
||||
// Network 16: Maselli Control (Original Language: LAD)
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 6 THEN
|
||||
IF Eq("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType", 6) THEN
|
||||
Maselli_PA_Control();
|
||||
END_IF;
|
||||
|
||||
// Network 17: mPDS Control (Original Language: LAD)
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 5 THEN
|
||||
IF Eq("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType", 5) THEN
|
||||
mPDS_PA_Control();
|
||||
END_IF;
|
||||
|
||||
|
@ -129,7 +123,7 @@ BEGIN
|
|||
// CALL "GetProdBrixCO2_FromAn"
|
||||
// NOP 0
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType" = 3 THEN
|
||||
IF Eq("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_MeterType", 3) THEN
|
||||
GetProdBrixCO2_Anal_Inpt();
|
||||
END_IF;
|
||||
|
||||
|
@ -157,24 +151,21 @@ BEGIN
|
|||
|
||||
// Network 25: Production ONS (Original Language: LAD)
|
||||
|
||||
// PBox Logic moved to consumer Coil
|
||||
"gProductionONS" := ("gBlenderProdMode" AND NOT "M19001") AND (NOT "mDelayPowerOnTmr");
|
||||
"gProductionONS" := "gBlenderProdMode" AND NOT "mDelayPowerOnTmr" AND NOT "M19001";
|
||||
|
||||
// Network 26: Blender Prod Mode Init (Original Language: LAD)
|
||||
|
||||
IF ("gProductionONS" OR "Procedure_Variables"."Blender_Rinse"."ONS_Done") AND (NOT "Blender_Variables_Pers"."gBlenderStarted") THEN
|
||||
IF ("gProductionONS" AND NOT "Blender_Variables_Pers"."gBlenderStarted") OR ("Procedure_Variables"."Blender_Rinse"."ONS_Done" AND NOT "Blender_Variables_Pers"."gBlenderStarted") THEN
|
||||
BlenderCtrl_ProdModeInit();
|
||||
END_IF;
|
||||
|
||||
// Network 27: Rinse ONS (Original Language: LAD)
|
||||
|
||||
// PBox Logic moved to consumer Coil
|
||||
"gRinseONS" := ("HMI_Variables_Status"."System"."Blender_Prod_CIP" AND NOT "M19002") AND (NOT "mDelayPowerOnTmr");
|
||||
"gRinseONS" := "HMI_Variables_Status"."System"."Blender_Prod_CIP" AND NOT "mDelayPowerOnTmr" AND NOT "M19002";
|
||||
|
||||
// Network 28: CIP ONS (Original Language: LAD)
|
||||
|
||||
// PBox Logic moved to consumer Coil
|
||||
"gCIPONS" := ("gBlenderCIPMode" AND NOT "M19003") AND (NOT "mDelayPowerOnTmr");
|
||||
"gCIPONS" := "gBlenderCIPMode" AND NOT "mDelayPowerOnTmr" AND NOT "M19003";
|
||||
|
||||
// Network 29: CIp Mode Init (Original Language: LAD)
|
||||
|
||||
|
@ -228,7 +219,7 @@ BEGIN
|
|||
|
||||
// Network 41: Blend Procedure Data (Original Language: LAD)
|
||||
|
||||
IF (NOT "mDelayPowerOnTmr") THEN
|
||||
IF NOT "mDelayPowerOnTmr" THEN
|
||||
"Blender_Procedure Data"();
|
||||
END_IF;
|
||||
|
||||
|
@ -268,11 +259,11 @@ BEGIN
|
|||
|
||||
// Network 50: ResetTotalizer (Original Language: LAD)
|
||||
|
||||
"mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP;
|
||||
|
||||
// Network 51: ResetWaterTot (Original Language: LAD)
|
||||
|
||||
"mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP;
|
||||
"mResetWaterTot" := "mResetFTN301TotTmr".Q;
|
||||
|
||||
// Network 52: Water VFM Reset Totalizer (Original Language: LAD)
|
||||
|
@ -283,8 +274,8 @@ BEGIN
|
|||
|
||||
// Network 53: ResetCO2Tot (Original Language: LAD)
|
||||
|
||||
"mResetFTP302TotTmr"(IN := "gFTP302_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTP302TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetSyrupTot" := "mResetFTP302TotTmr".Q AND "gSyrupRoomEn";
|
||||
"mResetFTP302TotTmr"(IN := "mResetTotalizerTmr" OR "gFTP302_ResetTot", PT := S5T#2S); // TODO: Declarar "mResetFTP302TotTmr" : TP;
|
||||
"mResetSyrupTot" := "gSyrupRoomEn" AND "mResetFTP302TotTmr".Q;
|
||||
|
||||
// Network 54: Syrup MFM Reset Totalizer (Original Language: LAD)
|
||||
|
||||
|
@ -294,7 +285,7 @@ BEGIN
|
|||
|
||||
// Network 55: ResetProductTot (Original Language: LAD)
|
||||
|
||||
"mResetFTM303TotTmr"(IN := "gFTM303_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTM303TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetFTM303TotTmr"(IN := "mResetTotalizerTmr" OR "gFTM303_ResetTot", PT := S5T#2S); // TODO: Declarar "mResetFTM303TotTmr" : TP;
|
||||
"mResetCO2Tot" := "mResetFTM303TotTmr".Q;
|
||||
|
||||
// Network 56: CO2 MFM Reset Tot (Original Language: LAD)
|
||||
|
@ -305,7 +296,7 @@ BEGIN
|
|||
|
||||
// Network 57: ResetCO2Tot (Original Language: LAD)
|
||||
|
||||
"mResetProductTotTmr"(IN := "gProductMFMResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetProductTotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetProductTotTmr"(IN := "mResetTotalizerTmr" OR "gProductMFMResetTot", PT := S5T#2S); // TODO: Declarar "mResetProductTotTmr" : TP;
|
||||
"mResetProductTot" := "mResetProductTotTmr".Q;
|
||||
|
||||
// Network 58: Reset Totalizer (Original Language: LAD)
|
||||
|
@ -322,7 +313,7 @@ BEGIN
|
|||
|
||||
// Network 60: Blender Ctrl Command (Original Language: LAD)
|
||||
|
||||
IF (NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Simulation") THEN
|
||||
IF NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_Simulation" THEN
|
||||
BlenderCtrl_MFM_Command();
|
||||
END_IF;
|
||||
|
||||
|
@ -340,8 +331,7 @@ BEGIN
|
|||
|
||||
// Network 64: All Auto (Original Language: LAD)
|
||||
|
||||
// NBox Logic moved to consumer Coil
|
||||
IF NOT ("HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Command" AND "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Enable") AND "M19011" THEN
|
||||
IF ("M19011" AND NOT "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Command") OR ("M19011" AND NOT "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Enable") THEN
|
||||
BlenderCtrl_All_Auto();
|
||||
END_IF;
|
||||
"HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Light" := "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Command" AND "HMI_Variables_Cmd"."Commands_From_HMI"."F7_DeviceControl"."Enable";
|
||||
|
@ -352,10 +342,10 @@ BEGIN
|
|||
|
||||
// Network 66: Mod Copy Recipe (Original Language: LAD)
|
||||
|
||||
"mAux_FP_M700_1" := "HMI_Variables_Cmd"."Recipe"."Main_Page" AND (NOT "mFP_Recip_Main_Page");
|
||||
"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; en VAR_STAT o VAR
|
||||
IF "T_Pulse_Recipe_Edit".Q AND "T_Pulse_Recipe_Edit" THEN
|
||||
"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;
|
||||
END_IF;
|
||||
IF "mAux_FP_M700_1" THEN
|
||||
|
|
2328
TestLAD.xml
2328
TestLAD.xml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -6,70 +6,51 @@ FUNCTION_BLOCK "TestLAD"
|
|||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
END_VAR
|
||||
|
||||
VAR_IN_OUT
|
||||
VAR_RETURN
|
||||
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 (NOT "gIN_HVP301_Aux")) AND (NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_FastChangeOverEnabled") AND "Procedure_Variables"."FTP302Line_Preparation"."Done") AND (NOT "Procedure_Variables"."Syr_RunOut"."Done") OR (((("gSyrupRoomEn" AND (NOT "gIN_HVP301_Aux")) AND "gBlenderCIPMode") AND "gIN_CIP_CIPRunning") AND "Procedure_Variables"."Blender_Run"."Running");
|
||||
|
||||
// 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; en VAR_STAT o VAR
|
||||
"gHVM302_Open" := "mHVM302_Dly".Q;
|
||||
|
||||
// Network 3: ResetTotalizer (Original Language: LAD)
|
||||
|
||||
"mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP; en VAR_STAT o VAR
|
||||
|
||||
// Network 4: ResetWaterTot (Original Language: LAD)
|
||||
|
||||
"mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetWaterTot" := "mResetFTN301TotTmr".Q;
|
||||
|
||||
// Network 5: ResetCO2Tot (Original Language: LAD)
|
||||
|
||||
"mResetFTP302TotTmr"(IN := "gFTP302_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTP302TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetSyrupTot" := "mResetFTP302TotTmr".Q AND "gSyrupRoomEn";
|
||||
|
||||
// Network 6: ResetProductTot (Original Language: LAD)
|
||||
|
||||
"mResetFTM303TotTmr"(IN := "gFTM303_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTM303TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetCO2Tot" := "mResetFTM303TotTmr".Q;
|
||||
|
||||
// Network 7: ResetCO2Tot (Original Language: LAD)
|
||||
|
||||
"mResetProductTotTmr"(IN := "gProductMFMResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetProductTotTmr" : TP; en VAR_STAT o VAR
|
||||
"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; en VAR_STAT o VAR
|
||||
IF "T_Pulse_Recipe_Edit".Q AND "T_Pulse_Recipe_Edit" 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
|
||||
|
|
|
@ -0,0 +1,763 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Document>
|
||||
<Engineering version="V18" />
|
||||
<SW.Blocks.FC ID="0">
|
||||
<AttributeList>
|
||||
<Interface><Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v5">
|
||||
<Section Name="Input" />
|
||||
<Section Name="Output" />
|
||||
<Section Name="InOut" />
|
||||
<Section Name="Temp">
|
||||
<Member Name="All_Auto_RETVAL" Datatype="Int" />
|
||||
<Member Name="Reset_SP_Word_RETVAL" Datatype="Int" />
|
||||
<Member Name="mResetWaterTot" Datatype="Bool" />
|
||||
<Member Name="mResetSyrupTot" Datatype="Bool" />
|
||||
<Member Name="mResetCO2Tot" Datatype="Bool" />
|
||||
<Member Name="mResetProductTot" Datatype="Bool" />
|
||||
<Member Name="Block_Move_Err" Datatype="Int" />
|
||||
<Member Name="Dim_HMI_Device" Datatype="Int" />
|
||||
<Member Name="PDim_HMI_Device" Datatype="DWord" />
|
||||
<Member Name="Dim_HMI_PID" Datatype="Int" />
|
||||
<Member Name="PDim_HMI_PID" Datatype="DWord" />
|
||||
</Section>
|
||||
<Section Name="Constant" />
|
||||
<Section Name="Return">
|
||||
<Member Name="Ret_Val" Datatype="Void" />
|
||||
</Section>
|
||||
</Sections></Interface>
|
||||
<MemoryLayout>Optimized</MemoryLayout>
|
||||
<Name>TestLAD</Name>
|
||||
<Namespace />
|
||||
<Number>2</Number>
|
||||
<ProgrammingLanguage>LAD</ProgrammingLanguage>
|
||||
<SetENOAutomatically>false</SetENOAutomatically>
|
||||
</AttributeList>
|
||||
<ObjectList>
|
||||
<MultilingualText ID="1" CompositionName="Comment">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="2" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="3" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="4" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="5" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="6" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="7" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="8" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
<SW.Blocks.CompileUnit ID="9" CompositionName="CompileUnits">
|
||||
<AttributeList>
|
||||
<NetworkSource><StatementList xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/StatementList/v4">
|
||||
<StlStatement UId="24">
|
||||
<StlToken Text="SET" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="25">
|
||||
<StlToken Text="R" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_Alarms" />
|
||||
<Component Name="gH_Message" AccessModifier="Array">
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>DInt</ConstantType>
|
||||
<ConstantValue>3</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</Component>
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
</StatementList></NetworkSource>
|
||||
<ProgrammingLanguage>STL</ProgrammingLanguage>
|
||||
</AttributeList>
|
||||
<ObjectList>
|
||||
<MultilingualText ID="A" CompositionName="Comment">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="B" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text> DEVICE</Text>
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="C" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="D" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="E" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="F" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="10" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="11" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
<MultilingualText ID="12" CompositionName="Title">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="13" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text>Set manual active</Text>
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="14" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="15" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="16" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="17" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="18" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="19" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
</ObjectList>
|
||||
</SW.Blocks.CompileUnit>
|
||||
<SW.Blocks.CompileUnit ID="1A" CompositionName="CompileUnits">
|
||||
<AttributeList>
|
||||
<NetworkSource><StatementList xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/StatementList/v4">
|
||||
<StlStatement UId="24">
|
||||
<StlToken Text="OPEN_DB" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="26">
|
||||
<StlToken Text="L_DBLG" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="27">
|
||||
<StlToken Text="T" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="Dim_HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="29">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#0.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="31">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="Dim_HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="33">
|
||||
<StlToken Text="SLD" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>Int</ConstantType>
|
||||
<ConstantValue>3</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="35">
|
||||
<StlToken Text="ADD_D" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="36">
|
||||
<StlToken Text="T" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="PDim_HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="38">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#0.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="40">
|
||||
<StlToken Text="LAR1_ACCU1" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="41">
|
||||
<LabelDeclaration>
|
||||
<Label Name="M100" />
|
||||
</LabelDeclaration>
|
||||
<StlToken Text="NOP_0" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="43">
|
||||
<StlToken Text="OPEN_DB" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="45">
|
||||
<StlToken Text="A" />
|
||||
<Access Scope="Address">
|
||||
<Indirect Width="Bit" Area="DB" Register="AR1" BitOffset="7" />
|
||||
<LineComment>
|
||||
<Text> config</Text>
|
||||
</LineComment>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="48">
|
||||
<StlToken Text="AN" />
|
||||
<Access Scope="Address">
|
||||
<Indirect Width="Bit" Area="DB" Register="AR1" BitOffset="0" />
|
||||
<LineComment>
|
||||
<Text> auto</Text>
|
||||
</LineComment>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="51">
|
||||
<StlToken Text="S" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_Alarms" />
|
||||
<Component Name="gH_Message" AccessModifier="Array">
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>DInt</ConstantType>
|
||||
<ConstantValue>3</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</Component>
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="53">
|
||||
<StlToken Text="ADDAR1" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#2.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="55">
|
||||
<StlToken Text="TAR1_ACCU1" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="56">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="PDim_HMI_Device" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="58">
|
||||
<StlToken Text="LT_D" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="59">
|
||||
<StlToken Text="JC" />
|
||||
<Access Scope="Label">
|
||||
<Label Name="M100" />
|
||||
</Access>
|
||||
</StlStatement>
|
||||
</StatementList></NetworkSource>
|
||||
<ProgrammingLanguage>STL</ProgrammingLanguage>
|
||||
</AttributeList>
|
||||
<ObjectList>
|
||||
<MultilingualText ID="1B" CompositionName="Comment">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="1C" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="1D" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="1E" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="1F" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="20" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="21" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="22" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
<MultilingualText ID="23" CompositionName="Title">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="24" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="25" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="26" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="27" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="28" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="29" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="2A" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
</ObjectList>
|
||||
</SW.Blocks.CompileUnit>
|
||||
<SW.Blocks.CompileUnit ID="2B" CompositionName="CompileUnits">
|
||||
<AttributeList>
|
||||
<NetworkSource><StatementList xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/StatementList/v4">
|
||||
<StlStatement UId="24">
|
||||
<LineComment>
|
||||
<Text> PID</Text>
|
||||
</LineComment>
|
||||
<StlToken Text="COMMENT" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="26">
|
||||
<StlToken Text="OPEN_DB" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="28">
|
||||
<StlToken Text="L_DBLG" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="29">
|
||||
<StlToken Text="T" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="Dim_HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="31">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#0.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="33">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="Dim_HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="35">
|
||||
<StlToken Text="SLD" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>Int</ConstantType>
|
||||
<ConstantValue>3</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="37">
|
||||
<StlToken Text="ADD_D" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="38">
|
||||
<StlToken Text="T" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="PDim_HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="40">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#0.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="42">
|
||||
<StlToken Text="LAR1_ACCU1" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="43">
|
||||
<LabelDeclaration>
|
||||
<Label Name="M101" />
|
||||
</LabelDeclaration>
|
||||
<StlToken Text="NOP_0" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="45">
|
||||
<StlToken Text="OPEN_DB" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="47">
|
||||
<StlToken Text="A" />
|
||||
<Access Scope="Address">
|
||||
<Indirect Width="Bit" Area="DB" Register="AR1" BitOffset="7" />
|
||||
<LineComment>
|
||||
<Text> config</Text>
|
||||
</LineComment>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="50">
|
||||
<StlToken Text="A_BRACK" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="51">
|
||||
<StlToken Text="A" />
|
||||
<Access Scope="Address">
|
||||
<Indirect Width="Bit" Area="DB" Register="AR1" BitOffset="16" />
|
||||
<LineComment>
|
||||
<Text> ManOut</Text>
|
||||
</LineComment>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="54">
|
||||
<StlToken Text="O" />
|
||||
<Access Scope="Address">
|
||||
<Indirect Width="Bit" Area="DB" Register="AR1" BitOffset="24" />
|
||||
<LineComment>
|
||||
<Text> LocalSP</Text>
|
||||
</LineComment>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="57">
|
||||
<StlToken Text="BRACKET" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="58">
|
||||
<StlToken Text="S" />
|
||||
<Access Scope="GlobalVariable">
|
||||
<Symbol>
|
||||
<Component Name="HMI_Alarms" />
|
||||
<Component Name="gH_Message" AccessModifier="Array">
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>DInt</ConstantType>
|
||||
<ConstantValue>3</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</Component>
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="60">
|
||||
<StlToken Text="ADDAR1" />
|
||||
<Access Scope="LiteralConstant">
|
||||
<Constant>
|
||||
<ConstantType>ARef</ConstantType>
|
||||
<ConstantValue>P#76.0</ConstantValue>
|
||||
</Constant>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="62">
|
||||
<StlToken Text="TAR1_ACCU1" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="63">
|
||||
<StlToken Text="L" />
|
||||
<Access Scope="LocalVariable">
|
||||
<Symbol>
|
||||
<Component Name="PDim_HMI_PID" />
|
||||
</Symbol>
|
||||
</Access>
|
||||
</StlStatement>
|
||||
<StlStatement UId="65">
|
||||
<StlToken Text="LT_D" />
|
||||
</StlStatement>
|
||||
<StlStatement UId="66">
|
||||
<StlToken Text="JC" />
|
||||
<Access Scope="Label">
|
||||
<Label Name="M101" />
|
||||
</Access>
|
||||
</StlStatement>
|
||||
</StatementList></NetworkSource>
|
||||
<ProgrammingLanguage>STL</ProgrammingLanguage>
|
||||
</AttributeList>
|
||||
<ObjectList>
|
||||
<MultilingualText ID="2C" CompositionName="Comment">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="2D" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="2E" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="2F" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="30" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="31" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="32" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="33" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
<MultilingualText ID="34" CompositionName="Title">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="35" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="36" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="37" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="38" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="39" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="3A" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="3B" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
</ObjectList>
|
||||
</SW.Blocks.CompileUnit>
|
||||
<MultilingualText ID="3C" CompositionName="Title">
|
||||
<ObjectList>
|
||||
<MultilingualTextItem ID="3D" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>it-IT</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="3E" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>de-DE</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="3F" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>en-US</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="40" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>es-ES</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="41" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>fr-FR</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="42" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>zh-CN</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
<MultilingualTextItem ID="43" CompositionName="Items">
|
||||
<AttributeList>
|
||||
<Culture>ja-JP</Culture>
|
||||
<Text />
|
||||
</AttributeList>
|
||||
</MultilingualTextItem>
|
||||
</ObjectList>
|
||||
</MultilingualText>
|
||||
</ObjectList>
|
||||
</SW.Blocks.FC>
|
||||
</Document>
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,284 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="AttributeList" type="AttributeList_T"/>
|
||||
<xs:complexType name="AttributeList_T">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="BooleanAttribute"/>
|
||||
<xs:element ref="IntegerAttribute"/>
|
||||
<xs:element ref="RealAttribute"/>
|
||||
<xs:element ref="StringAttribute"/>
|
||||
<xs:element ref="DateAttribute"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:element name="Blank" type="Blank_T"/>
|
||||
<xs:complexType name="Blank_T">
|
||||
<xs:attribute name="Num" type="xs:positiveInteger" use="optional" default="1"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="BooleanAttribute" type="BooleanAttribute_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A member attribute with a type restriction of boolean.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="BooleanAttribute_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:boolean">
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SystemDefined" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="Comment" type="Comment_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:group name="Comment_G">
|
||||
<xs:annotation>
|
||||
<xs:documentation>LAD/FBD: Only for Parts</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="Comment"/>
|
||||
<xs:element ref="LineComment"/>
|
||||
<xs:element ref="Blank"/>
|
||||
<xs:element ref="NewLine"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:group>
|
||||
<xs:complexType name="Comment_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>For NumBLs in STL. NumBLs is the count of the blank spaces before the actual text in the Comment. This is informative.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="MultiLanguageText" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Inserted" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Denotes if the comment is at the end of the line (using /*/) or inside the line (using (/* */) )</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="DateAttribute" type="DateAttribute_T"/>
|
||||
<xs:complexType name="DateAttribute_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:dateTime">
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SystemDefined" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="GUID_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="IntegerAttribute" type="IntegerAttribute_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A member attribute with a type restriction of integer.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="IntegerAttribute_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not for LAD/FBD.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:integer">
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SystemDefined" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Lang_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-zA-Z]{2}-[a-zA-Z]{2}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="LineComment" type="LineComment_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not for LAD/FBD </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="LineComment_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>For NumBLs in STL. NumBLs is the count of the blank spaces before the actual text in the LineComment. This is informative.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="Text"/>
|
||||
<xs:element ref="Comment"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Inserted" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Denotes if the comment is at the end of the line (using //) or inside the line (using /* */)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="NoClosingBracket" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="MultiLanguageText" type="MultiLanguageText_T"/>
|
||||
<xs:complexType name="MultiLanguageText_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Lang" type="Lang_TP" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="NewLine" type="NewLine_T"/>
|
||||
<xs:complexType name="NewLine_T">
|
||||
<xs:attribute name="Num" type="xs:positiveInteger" use="optional" default="1"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="RealAttribute" type="RealAttribute_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A member attribute with a type restriction of real.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="RealAttribute_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:double">
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SystemDefined" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="SectionName_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Input"/>
|
||||
<xs:enumeration value="Return"/>
|
||||
<xs:enumeration value="Output"/>
|
||||
<xs:enumeration value="InOut"/>
|
||||
<xs:enumeration value="Static"/>
|
||||
<xs:enumeration value="Temp"/>
|
||||
<xs:enumeration value="Constant"/>
|
||||
<xs:enumeration value="Base"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="SimaticName_TP">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="SimaticType_TE">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:element name="StringAttribute" type="StringAttribute_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A member attribute with a type restriction of string.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="StringAttribute_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Exported only with ReadOnly option, ignored during import.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SystemDefined" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An attribute of attribute, denotes if it is defined by a user or the system itself. In V14, if exists it is always true.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="Text" type="Text_T"/>
|
||||
<xs:complexType name="Text_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="Token" type="Token_T"/>
|
||||
<xs:group name="Token_G">
|
||||
<xs:choice>
|
||||
<xs:element ref="Token" minOccurs="0"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:complexType name="Token_T">
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>For NumBLs. NumBLs is the count of the blank spaces at the start.This is informative.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Text" type="Token_TE" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Token_TE">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="VersionString_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[0-9]+(.[0-9]+){0,3}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="ViewInfo" type="ViewInfo_T"/>
|
||||
<xs:complexType name="ViewInfo_T">
|
||||
<xs:attribute name="Start" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="XPos" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="YPos" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="Width" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="Height" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2018. All rights reserved. -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="SnapshotValues" type="SnapshotValues_T"/>
|
||||
<xs:complexType name="SnapshotValues_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Value" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Value" type="Value_T"/>
|
||||
<xs:complexType name="Value_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Path" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Type" type="xs:string" use="required"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,162 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2020. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.Common_v3.xsd"/>
|
||||
<xs:simpleType name="Accessibility_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Public"/>
|
||||
<xs:enumeration value="Internal"/>
|
||||
<xs:enumeration value="Protected"/>
|
||||
<xs:enumeration value="Private"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="IndexPath_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="-?\d+(,-?\d+)*(;(-?\d+(,-?\d+)*))?"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Member" type="Member_T"/>
|
||||
<xs:complexType name="Member_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="AttributeList" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Member"/>
|
||||
<xs:element ref="Sections"/>
|
||||
<xs:element ref="StartValue"/>
|
||||
<xs:element ref="Comment"/>
|
||||
<xs:element ref="AssignedProDiagFB"/>
|
||||
<xs:element ref="Subelement"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="Datatype" type="SimaticType_TE" use="required"/>
|
||||
<xs:attribute name="Version" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>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!).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Remanence" type="Remanence_TE" default="NonRetain"/>
|
||||
<xs:attribute name="Accessibility" type="Accessibility_TE" default="Public"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="MemberAttributes_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="CodeReadOnly">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Write acces only inside function</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="At">
|
||||
<xs:annotation>
|
||||
<xs:documentation>string: Member shares offset with another member in this structure</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SetPoint">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: Member can be synchronized with work memory</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UserVisible">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: Editor does not show the member</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UserReadOnly">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: User cannot change member name</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UserDeletable">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: Editor does not allow to delete the member</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HmiAccessible">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: No HMI access, no structure item</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HmiVisible">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: Filter to reduce the number of members shown in the first place</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Offset">
|
||||
<xs:annotation>
|
||||
<xs:documentation>integer: </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="PaddedSize">
|
||||
<xs:annotation>
|
||||
<xs:documentation>integer: </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HiddenAssignment">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: Hide assignement at call if matches with PredefinedAssignment</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="PredefinedAssignment">
|
||||
<xs:annotation>
|
||||
<xs:documentation>string: Input for the paramter used when call is placed</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ReadOnlyAssignment">
|
||||
<xs:annotation>
|
||||
<xs:documentation>boolean: The user cannot change the predefined assignement at the call</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Remanence_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="SetInIDB"/>
|
||||
<xs:enumeration value="NonRetain"/>
|
||||
<xs:enumeration value="Retain"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Section" type="Section_T"/>
|
||||
<xs:complexType name="Section_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Sections" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Base Class</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Member" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="SectionName_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Sections" type="Sections_T"/>
|
||||
<xs:complexType name="Sections_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="AttributeList" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element ref="Section" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Datatype" type="SimaticType_TE"/>
|
||||
<xs:attribute name="Version" type="xs:string" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="StartValue" type="StartValue_T"/>
|
||||
<xs:element name="AssignedProDiagFB" type="xs:string"/>
|
||||
<xs:complexType name="StartValue_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="ConstantName" type="SimaticName_TP"/>
|
||||
<xs:attribute name="IsBulkValue" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="Subelement" type="Subelement_T"/>
|
||||
<xs:complexType name="Subelement_T">
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="StartValue"/>
|
||||
<xs:element ref="Comment"/>
|
||||
<xs:element ref="AssignedProDiagFB"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Path" type="IndexPath_TP"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,593 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.Common_v3.xsd"/>
|
||||
<xs:element name="AbsoluteOffset" type="AbsoluteOffset_T"/>
|
||||
<xs:complexType name="AbsoluteOffset_T">
|
||||
<xs:attribute name="BitOffset" type="xs:int" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Byte * 8 + Bit</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Type" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Access" type="Access_T"/>
|
||||
<xs:group name="Access_G">
|
||||
<xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="Access"/>
|
||||
<xs:element ref="Token"/>
|
||||
<xs:group ref="Comment_G"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:group>
|
||||
<xs:complexType name="Access_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for NumBLs. NumBLs is informative. Not for LAD/FBD.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:choice>
|
||||
<xs:element ref="Label"/>
|
||||
<xs:element ref="Constant"/>
|
||||
<xs:element ref="CallInfo">
|
||||
<xs:annotation>
|
||||
<xs:documentation>call of a user block. Not in Graph ActionList.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Instruction">
|
||||
<xs:annotation>
|
||||
<xs:documentation>call of an instruction. Not for LAD/FBD, Graph ActionList.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Indirect">
|
||||
<xs:annotation>
|
||||
<xs:documentation>STL specific</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Statusword"/>
|
||||
<xs:element ref="PredefinedVariable">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Only in SCL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Expression">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL specific</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Symbol"/>
|
||||
<xs:element ref="Address">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for absolute addresses</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="DataType"/>
|
||||
<xs:element ref="Reference"/>
|
||||
</xs:choice>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Scope" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="Scope_TE"/>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="AccessModifier_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Array"/>
|
||||
<xs:enumeration value="Reference"/>
|
||||
<xs:enumeration value="ReferenceToArray"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Address" type="Address_T"/>
|
||||
<xs:complexType name="Address_T">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="BooleanAttribute"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="Area" type="Area_TE" use="required"/>
|
||||
<xs:attribute name="Type" type="SimaticName_TP" use="optional"/>
|
||||
<xs:attribute name="BlockNumber" type="xs:int" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for DB access</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="BitOffset" type="xs:int" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>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).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>if true, the import unnoted it</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Area_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="PeripheryInput"/>
|
||||
<xs:enumeration value="PeripheryOutput"/>
|
||||
<xs:enumeration value="Input"/>
|
||||
<xs:enumeration value="Output"/>
|
||||
<xs:enumeration value="Memory"/>
|
||||
<xs:enumeration value="FB"/>
|
||||
<xs:enumeration value="FC"/>
|
||||
<xs:enumeration value="DB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>partly qualified access with DB register</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>partly qualified access with DI register</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Timer"/>
|
||||
<xs:enumeration value="Counter"/>
|
||||
<xs:enumeration value="Local">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Classic Local Stack</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="BlockType_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="DB"/>
|
||||
<xs:enumeration value="FB"/>
|
||||
<xs:enumeration value="FC"/>
|
||||
<xs:enumeration value="OB"/>
|
||||
<xs:enumeration value="UDT"/>
|
||||
<xs:enumeration value="FBT"/>
|
||||
<xs:enumeration value="FCT"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="CallInfo" type="CallInfo_T"/>
|
||||
<xs:complexType name="CallInfo_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not for LAD/FBD. </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="IntegerAttribute">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for BlockNumber. BlockNumber is informative.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="DateAttribute">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for ParameterModifiedTS. ParameterModifiedTS is informative</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Instance"/>
|
||||
<xs:element ref="NamelessParameter" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="Token_G" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="Parameter" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="Name" type="SimaticName_TP" use="optional"/>
|
||||
<xs:attribute name="BlockType" type="BlockType_TE" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Component" type="Component_T"/>
|
||||
<xs:complexType name="Component_T">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:group ref="Comment_G" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:group>
|
||||
<xs:element ref="Token" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Access">
|
||||
<xs:annotation>
|
||||
<xs:documentation>For the indices of an array</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="BooleanAttribute" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
<xs:attribute name="SliceAccessModifier" type="SliceAccessModifier_TP" default="undef"/>
|
||||
<xs:attribute name="SimpleAccessModifier" type="SimpleAccessModifier_TP" default="None"/>
|
||||
<xs:attribute name="AccessModifier" type="AccessModifier_TE" default="None">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If component has child AccessModifier is Array else AccessModifier is None</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Constant" type="Constant_T"/>
|
||||
<xs:complexType name="Constant_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="ConstantType" minOccurs="0"/>
|
||||
<xs:element ref="ConstantValue" minOccurs="0"/>
|
||||
<xs:element ref="StringAttribute" minOccurs="0" maxOccurs="2">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for Format and FormatFlags. They are informative..</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="BooleanAttribute" minOccurs="0" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="SimaticName_TP"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="ConstantType" type="ConstantType_T"/>
|
||||
<xs:complexType name="ConstantType_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Informative" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="ConstantValue" type="ConstantValue_T"/>
|
||||
<xs:complexType name="ConstantValue_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Informative" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="Expression" type="Expression_T"/>
|
||||
<xs:complexType name="Expression_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:choice>
|
||||
<xs:element ref="Access"/>
|
||||
<xs:element ref="Token"/>
|
||||
</xs:choice>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="DataType" type="DataType_T"/>
|
||||
<xs:complexType name="DataType_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Informative" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Format_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Real"/>
|
||||
<xs:enumeration value="Bin"/>
|
||||
<xs:enumeration value="DecSigned"/>
|
||||
<xs:enumeration value="DecUnsigned"/>
|
||||
<xs:enumeration value="Pointer"/>
|
||||
<xs:enumeration value="CharSequence"/>
|
||||
<xs:enumeration value="DecSequence"/>
|
||||
<xs:enumeration value="Hex"/>
|
||||
<xs:enumeration value="S5Count"/>
|
||||
<xs:enumeration value="Time"/>
|
||||
<xs:enumeration value="Date"/>
|
||||
<xs:enumeration value="TimeOfDay"/>
|
||||
<xs:enumeration value="S5Time"/>
|
||||
<xs:enumeration value="Bool"/>
|
||||
<xs:enumeration value="Oct"/>
|
||||
<xs:enumeration value="Bcd"/>
|
||||
<xs:enumeration value="DateAndTime"/>
|
||||
<xs:enumeration value="String"/>
|
||||
<xs:enumeration value="Any"/>
|
||||
<xs:enumeration value="Number"/>
|
||||
<xs:enumeration value="Char"/>
|
||||
<xs:enumeration value="HexSequence"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="FormatFlags_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="None"/>
|
||||
<xs:pattern value="((Lower|Format|Size|Under|Exp|TypeQualifier)(,\s*)?)*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Indirect" type="Indirect_T"/>
|
||||
<xs:complexType name="Indirect_T">
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Token"/>
|
||||
<xs:group ref="Comment_G"/>
|
||||
</xs:choice>
|
||||
<xs:element ref="Access" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Width" type="Width_TE" use="required"/>
|
||||
<xs:attribute name="Area" type="Area_TE" use="optional"/>
|
||||
<xs:attribute name="Register" type="Register_TE" use="optional"/>
|
||||
<xs:attribute name="BitOffset" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Instance" type="Instance_T"/>
|
||||
<xs:complexType name="Instance_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:choice>
|
||||
<xs:element ref="Component"/>
|
||||
<xs:element ref="AbsoluteOffset"/>
|
||||
<xs:element ref="Token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>the DOT; only if separated. Not in Graph ActionList, not in LAD/FBD.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Address"/>
|
||||
</xs:choice>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Scope" type="Scope_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Instruction" type="Instruction_T"/>
|
||||
<xs:complexType name="Instruction_T">
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:group ref="Token_G"/>
|
||||
<xs:element ref="TemplateValue"/>
|
||||
|
||||
<xs:element ref="Instance"/>
|
||||
<xs:element ref="NamelessParameter" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="Parameter" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" use="optional">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="SimaticName_TP">
|
||||
<xs:minLength value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Version" type="VersionString_TP"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="UId" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="InterfaceFlags_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="None"/>
|
||||
<xs:pattern value="((Mandatory|S7_Visible)(,\s*)?)*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Label" type="Label_T"/>
|
||||
<xs:complexType name="Label_T">
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL only</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element ref="BooleanAttribute" minOccurs="0"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:element ref="Token" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="SimaticName_TP" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="NamelessParameter" type="NamelessParameter_T"/>
|
||||
<xs:complexType name="NamelessParameter_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element ref="StringAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for InterfaceFlags. InterfaceFlags is informative</xs:documentation>
|
||||
<xs:documentation>The type of the value should be InterfaceFlags_TP</xs:documentation>
|
||||
<xs:documentation>The default value is "S7_Visible"</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:group ref="Access_G"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Parameter" type="Parameter_T"/>
|
||||
|
||||
<xs:complexType name="Parameter_T">
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for NumBLs. NumBLs is informative</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="StringAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for InterfaceFlags. InterfaceFlags is informative</xs:documentation>
|
||||
<xs:documentation>The type of the value should be InterfaceFlags_TP</xs:documentation>
|
||||
<xs:documentation>The default value is "S7_Visible"</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="BooleanAttribute" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="Access_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="SimaticName_TP" use="required"/>
|
||||
<xs:attribute name="Section" type="SectionName_TE" use="optional"/>
|
||||
<xs:attribute name="Type" type="SimaticType_TE"/>
|
||||
<xs:attribute name="TemplateReference" type="xs:string"/>
|
||||
<xs:attribute name="Informative" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="UId" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="PredefinedVariable" type="PredefinedVariable_T"/>
|
||||
<xs:complexType name="PredefinedVariable_T">
|
||||
<xs:attribute name="Name" type="PredefinedVariable_TE" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="PredefinedVariable_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="ENO"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Reference" type="Reference_T"/>
|
||||
<xs:complexType name="Reference_T">
|
||||
<xs:sequence>
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:element ref="Token"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:element ref="Access"/>
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:element ref="Token"/>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Register_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="AR1"/>
|
||||
<xs:enumeration value="AR2"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Scope_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Undef">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Symbols we do not know what they are</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="GlobalConstant"/>
|
||||
<xs:enumeration value="LocalConstant"/>
|
||||
<xs:enumeration value="GlobalVariable"/>
|
||||
<xs:enumeration value="LocalVariable"/>
|
||||
<xs:enumeration value="Instruction"/>
|
||||
<xs:enumeration value="Label"/>
|
||||
<xs:enumeration value="TypedConstant"/>
|
||||
<xs:enumeration value="AddressConstant"/>
|
||||
<xs:enumeration value="LiteralConstant"/>
|
||||
<xs:enumeration value="AlarmConstant"/>
|
||||
<xs:enumeration value="Address"/>
|
||||
<xs:enumeration value="Statusword"/>
|
||||
<xs:enumeration value="Expression"/>
|
||||
<xs:enumeration value="Unnamed"/>
|
||||
<xs:enumeration value="Call"/>
|
||||
<xs:enumeration value="CallWithType"/>
|
||||
<xs:enumeration value="UserType"/>
|
||||
<xs:enumeration value="SystemType"/>
|
||||
<xs:enumeration value="Reference"/>
|
||||
<xs:enumeration value="PredefinedVariable"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="SimpleAccessModifier_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="None|((Periphery|QualityInformation)(,\s*)?)*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="SimpleType_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="undef"/>
|
||||
<xs:enumeration value="Bool"/>
|
||||
<xs:enumeration value="Byte"/>
|
||||
<xs:enumeration value="Char"/>
|
||||
<xs:enumeration value="Word"/>
|
||||
<xs:enumeration value="Int"/>
|
||||
<xs:enumeration value="DWord"/>
|
||||
<xs:enumeration value="DInt"/>
|
||||
<xs:enumeration value="Real"/>
|
||||
<xs:enumeration value="LReal"/>
|
||||
<xs:enumeration value="Timer"/>
|
||||
<xs:enumeration value="S5Time"/>
|
||||
<xs:enumeration value="ARef"/>
|
||||
<xs:enumeration value="Any"/>
|
||||
<xs:enumeration value="Time"/>
|
||||
<xs:enumeration value="S5Count"/>
|
||||
<xs:enumeration value="Counter"/>
|
||||
<xs:enumeration value="Block_DB"/>
|
||||
<xs:enumeration value="Block_FB"/>
|
||||
<xs:enumeration value="Block_FC"/>
|
||||
<xs:enumeration value="Block_SFB"/>
|
||||
<xs:enumeration value="Block_UDT"/>
|
||||
<xs:enumeration value="Multi_FB"/>
|
||||
<xs:enumeration value="Multi_SFB"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="SliceAccessModifier_TP">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="([xbwdXBWD]\d+)|undef"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Statusword" type="Statusword_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Only for S7-300/400/WinAC</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="Statusword_T">
|
||||
<xs:attribute name="Combination" type="Statusword_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Statusword_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="BR"/>
|
||||
<xs:enumeration value="OV"/>
|
||||
<xs:enumeration value="OS"/>
|
||||
<xs:enumeration value="EQ"/>
|
||||
<xs:enumeration value="NE"/>
|
||||
<xs:enumeration value="GT"/>
|
||||
<xs:enumeration value="LT"/>
|
||||
<xs:enumeration value="GE"/>
|
||||
<xs:enumeration value="LE"/>
|
||||
<xs:enumeration value="UO"/>
|
||||
<xs:enumeration value="NU"/>
|
||||
<xs:enumeration value="STW"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Symbol" type="Symbol_T"/>
|
||||
<xs:complexType name="Symbol_T">
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="Component"/>
|
||||
<xs:element ref="Address"/>
|
||||
<xs:element ref="AbsoluteOffset"/>
|
||||
<xs:element ref="Token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SCL.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Access"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="UId" type="xs:int">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Scope" type="Scope_TE"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="TemplateType_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Cardinality"/>
|
||||
<xs:enumeration value="Type"/>
|
||||
<xs:enumeration value="Operation"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="TemplateValue" type="TemplateValue_T"/>
|
||||
<xs:complexType name="TemplateValue_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="Name" type="SimaticName_TP" use="required"/>
|
||||
<xs:attribute name="Type" type="TemplateType_TE" use="required"/>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Width_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Bit"/>
|
||||
<xs:enumeration value="Byte"/>
|
||||
<xs:enumeration value="Word"/>
|
||||
<xs:enumeration value="Offset"/>
|
||||
<xs:enumeration value="Double"/>
|
||||
<xs:enumeration value="Pointer"/>
|
||||
<xs:enumeration value="Long"/>
|
||||
<xs:enumeration value="Any"/>
|
||||
<xs:enumeration value="Block"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.PlcBlocks.Access_v4.xsd"/>
|
||||
<xs:element name="LabelDeclaration" type="LabelDeclaration_T"/>
|
||||
<xs:complexType name="LabelDeclaration_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for NumBLs. NumBLs is informative</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Label"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element ref="Token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>the COLON; only if separated</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,350 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.PlcBlocks.Access_v4.xsd"/>
|
||||
<xs:include schemaLocation="SW.PlcBlocks.LADFBD_v4.xsd"/>
|
||||
<xs:complexType name="Action_T">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Access"/>
|
||||
<xs:element ref="Token"/>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="Event" type="Event_TE"/>
|
||||
<xs:attribute name="Interlock" type="xs:boolean"/>
|
||||
<xs:attribute name="Qualifier" type="Qualifier_TE"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Event_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value=""/>
|
||||
<xs:enumeration value="A1"/>
|
||||
<xs:enumeration value="L0"/>
|
||||
<xs:enumeration value="L1"/>
|
||||
<xs:enumeration value="R1"/>
|
||||
<xs:enumeration value="S0"/>
|
||||
<xs:enumeration value="S1"/>
|
||||
<xs:enumeration value="V0"/>
|
||||
<xs:enumeration value="V1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Qualifier_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value=""/>
|
||||
<xs:enumeration value="CD"/>
|
||||
<xs:enumeration value="CR"/>
|
||||
<xs:enumeration value="CS"/>
|
||||
<xs:enumeration value="CU"/>
|
||||
<xs:enumeration value="D"/>
|
||||
<xs:enumeration value="L"/>
|
||||
<xs:enumeration value="N"/>
|
||||
<xs:enumeration value="ON"/>
|
||||
<xs:enumeration value="OFF"/>
|
||||
<xs:enumeration value="R"/>
|
||||
<xs:enumeration value="S"/>
|
||||
<xs:enumeration value="TD"/>
|
||||
<xs:enumeration value="TF"/>
|
||||
<xs:enumeration value="TL"/>
|
||||
<xs:enumeration value="TR"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Actions" type="Actions_T"/>
|
||||
<xs:complexType name="Actions_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Title" minOccurs="0"/>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Action"/>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AlarmSupportingLanguageModule_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Title" minOccurs="0"/>
|
||||
<xs:element ref="AlarmText" minOccurs="0"/>
|
||||
<xs:element ref="FlgNet"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="ProgrammingLanguage" type="ProgrammingLanguage_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="AlarmText" type="AlarmText_T"/>
|
||||
<xs:complexType name="AlarmText_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Temporary change for enable of empty alarm text because of the graph alarm handling reconstruction.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element ref="MultiLanguageText"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Branch_T">
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
<xs:attribute name="Type" type="Branch_TE" use="required"/>
|
||||
<xs:attribute name="Cardinality" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Branch_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="SimBegin"/>
|
||||
<xs:enumeration value="SimEnd"/>
|
||||
<xs:enumeration value="AltBegin"/>
|
||||
<xs:enumeration value="AltEnd"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Branches" type="Branches_T"/>
|
||||
<xs:complexType name="Branches_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Branch"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="BranchRef" type="BranchRef_T"/>
|
||||
<xs:complexType name="BranchRef_T">
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
<xs:attribute name="In" type="xs:int"/>
|
||||
<xs:attribute name="Out" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Connection" type="Connection_T"/>
|
||||
<xs:complexType name="Connection_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="NodeFrom"/>
|
||||
<xs:element ref="NodeTo"/>
|
||||
<xs:element ref="LinkType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="EndConnection"/>
|
||||
<xs:element name="Graph" type="Graph_T"/>
|
||||
<xs:complexType name="AlarmsSettings_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="AlarmSupervisionCategories"/>
|
||||
<xs:element ref="AlarmInterlockCategory"/>
|
||||
<xs:element ref="AlarmSubcategory1Interlock"/>
|
||||
<xs:element ref="AlarmSubcategory2Interlock"/>
|
||||
<xs:element ref="AlarmCategorySupervision"/>
|
||||
<xs:element ref="AlarmSubcategory1Supervision"/>
|
||||
<xs:element ref="AlarmSubcategory2Supervision"/>
|
||||
<xs:element ref="AlarmWarningCategory"/>
|
||||
<xs:element ref="AlarmSubcategory1Warning"/>
|
||||
<xs:element ref="AlarmSubcategory2Warning"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="AlarmsSettings" type="AlarmsSettings_T"/>
|
||||
<xs:complexType name="Graph_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="PreOperations"/>
|
||||
<xs:element ref="Sequence" maxOccurs="unbounded"/>
|
||||
<xs:element ref="PostOperations"/>
|
||||
<xs:element ref="AlarmsSettings"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="IdentRef" type="IdentRef_T"/>
|
||||
<xs:complexType name="IdentRef_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
<xs:element ref="ViewInfo" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="PartAttribute_G"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Interlock" type="AlarmSupportingLanguageModule_T"/>
|
||||
<xs:element name="Interlocks" type="Interlocks_T"/>
|
||||
<xs:complexType name="Interlocks_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Interlock"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Link_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Direct"/>
|
||||
<xs:enumeration value="Jump"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Node_T">
|
||||
<xs:choice>
|
||||
<xs:element ref="StepRef"/>
|
||||
<xs:element ref="TransitionRef"/>
|
||||
<xs:element ref="BranchRef"/>
|
||||
<xs:element ref="EndConnection"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:element name="NodeFrom" type="Node_T"/>
|
||||
<xs:element name="NodeTo" type="Node_T"/>
|
||||
<xs:element name="LinkType" type="Link_TE"/>
|
||||
<xs:element name="PermanentOperation" type="PermanentOperation_T"/>
|
||||
<xs:complexType name="PermanentOperation_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Title" minOccurs="0"/>
|
||||
<xs:element ref="FlgNet" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="ProgrammingLanguage" type="ProgrammingLanguage_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="PermanentOperations_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Title" minOccurs="0"/>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
<xs:element ref="PermanentOperation" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="PostOperations" type="PermanentOperations_T"/>
|
||||
<xs:element name="PreOperations" type="PermanentOperations_T"/>
|
||||
<xs:simpleType name="ProgrammingContext_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Plain"/>
|
||||
<xs:enumeration value="GraphTransition"/>
|
||||
<xs:enumeration value="GraphSupervision"/>
|
||||
<xs:enumeration value="GraphInterlock"/>
|
||||
<xs:enumeration value="GraphActions"/>
|
||||
<xs:enumeration value="PreOperation"/>
|
||||
<xs:enumeration value="PostOperation"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="ProgrammingLanguage_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="STL"/>
|
||||
<xs:enumeration value="FBD"/>
|
||||
<xs:enumeration value="LAD"/>
|
||||
<xs:enumeration value="FBD_IEC"/>
|
||||
<xs:enumeration value="LAD_IEC"/>
|
||||
<xs:enumeration value="GRAPH"/>
|
||||
<xs:enumeration value="DB"/>
|
||||
<xs:enumeration value="SDB"/>
|
||||
<xs:enumeration value="DB_CPU"/>
|
||||
<xs:enumeration value="FB_IDB"/>
|
||||
<xs:enumeration value="SFB_IDB"/>
|
||||
<xs:enumeration value="DT_DB"/>
|
||||
<xs:enumeration value="SCL"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Sequence" type="Sequence_T"/>
|
||||
<xs:complexType name="Sequence_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Title" minOccurs="0"/>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
<xs:element ref="Steps"/>
|
||||
<xs:element ref="Transitions"/>
|
||||
<xs:element ref="Branches"/>
|
||||
<xs:element ref="Connections"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Step">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Step_T"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="Step_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="StepName" minOccurs="0"/>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
<xs:element ref="Actions"/>
|
||||
<xs:element ref="Supervisions"/>
|
||||
<xs:element ref="Interlocks"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="IsMissing" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
<xs:attribute name="Init" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="Name" use="required"/>
|
||||
<xs:attribute name="MaximumStepTime" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="WarningTime" type="xs:string" use="optional"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="StepRef" type="StepRef_T"/>
|
||||
<xs:complexType name="StepRef_T">
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Steps" type="Steps_T"/>
|
||||
<xs:complexType name="Steps_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element ref="Step"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Connections" type="Connections_T"/>
|
||||
<xs:complexType name="Connections_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element ref="Connection"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Supervision" type="AlarmSupportingLanguageModule_T"/>
|
||||
<xs:element name="Supervisions" type="Supervisions_T"/>
|
||||
<xs:complexType name="Supervisions_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Supervision"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Title" type="Comment_T"/>
|
||||
<xs:complexType name="Transition_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="TransitionName" minOccurs="0"/>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
<xs:element ref="FlgNet"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="IsMissing" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="Name" use="required"/>
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
<xs:attribute name="ProgrammingLanguage" type="ProgrammingLanguage_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="TransitionRef" type="TransitionRef_T"/>
|
||||
<xs:complexType name="TransitionRef_T">
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Transitions" type="Transitions_T"/>
|
||||
<xs:complexType name="Transitions_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Transition"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="Action" type="Action_T"/>
|
||||
<xs:element name="Transition" type="Transition_T"/>
|
||||
<xs:element name="Branch" type="Branch_T"/>
|
||||
<xs:element name="AlarmSupervisionCategories" type="AlarmSupervisionCategories_T"/>
|
||||
<xs:complexType name="AlarmSupervisionCategories_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="AlarmSupervisionCategory"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="AlarmSupervisionCategory" type="AlarmSupervisionCategory_T"/>
|
||||
<xs:complexType name="AlarmSupervisionCategory_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Token" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Enabler token</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Id" type="xs:unsignedShort" use="required"/>
|
||||
<xs:attribute name="DisplayClass" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:unsignedShort">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:maxInclusive value="16"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:element name="AlarmInterlockCategory" type="AlarmCategory_T"/>
|
||||
<xs:element name="AlarmSubcategory1Interlock" type="AlarmSubcategory_T"/>
|
||||
<xs:element name="AlarmSubcategory2Interlock" type="AlarmSubcategory_T"/>
|
||||
<xs:element name="AlarmCategorySupervision" type="AlarmCategory_T"/>
|
||||
<xs:element name="AlarmSubcategory1Supervision" type="AlarmSubcategory_T"/>
|
||||
<xs:element name="AlarmSubcategory2Supervision" type="AlarmSubcategory_T"/>
|
||||
<xs:element name="AlarmWarningCategory" type="AlarmCategory_T"/>
|
||||
<xs:element name="AlarmSubcategory1Warning" type="AlarmSubcategory_T"/>
|
||||
<xs:element name="AlarmSubcategory2Warning" type="AlarmSubcategory_T"/>
|
||||
<xs:complexType name="AlarmCategory_T">
|
||||
<xs:attribute name="Id" type="xs:unsignedShort" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="TransitionName" type="TransitionName_T"/>
|
||||
<xs:complexType name="TransitionName_T">
|
||||
<xs:sequence>
|
||||
<xs:annotation>
|
||||
<xs:documentation>For translated transiton names</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element ref="MultiLanguageText" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="StepName" type="StepName_T"/>
|
||||
<xs:complexType name="StepName_T">
|
||||
<xs:sequence>
|
||||
<xs:annotation>
|
||||
<xs:documentation>For translated step names</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element ref="MultiLanguageText" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AlarmSubcategory_T">
|
||||
<xs:attribute name="Id" type="xs:unsignedShort" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- <xs:include schemaLocation="SW.Common_v3.xsd"/>-->
|
||||
<xs:element name="SupervisionFB">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="StateStruct">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Number" type="xs:int"/>
|
||||
<xs:element name="Multiinstance">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="BlockTypeSupervisionNumber" type="xs:int"/>
|
||||
<xs:element name="BlockInstSupervisionGroups" type="BlockInstSupervisionGroupsType"/>
|
||||
<xs:element name="BlockInstSupervisionGroup">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Multiinstance" minOccurs="0"/>
|
||||
<xs:element ref="BlockInstSupervision" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="BlockInstSupervision">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Number"/>
|
||||
<xs:element ref="StateStruct"/>
|
||||
<xs:element ref="BlockTypeSupervisionNumber"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="BlockInstSupervisionGroupsType">
|
||||
<xs:sequence>
|
||||
<xs:element ref="BlockInstSupervisionGroup" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.PlcBlocks.CompileUnitCommon_v4.xsd"/>
|
||||
<xs:element name="Powerrail" type="Powerrail_T"/>
|
||||
<xs:complexType name="Powerrail_T"/>
|
||||
<xs:element name="Openbranch" type="Openbranch_T"/>
|
||||
<xs:complexType name="Openbranch_T"/>
|
||||
<xs:element name="OpenCon" type="OpenCon_T"/>
|
||||
<xs:complexType name="OpenCon_T">
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Call" type="Call_T"/>
|
||||
<xs:complexType name="Call_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="CallInfo"/>
|
||||
<xs:group ref="PartSequence_G"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="PartAttribute_G"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Equation_T">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string"/>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="FlgNet" type="FlgNet_T"/>
|
||||
<xs:complexType name="FlgNet_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="Labels" minOccurs="0"/>
|
||||
<xs:element ref="Parts"/>
|
||||
<xs:element ref="Wires" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="GateName_TE">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:element name="IdentCon" type="IdentCon_T"/>
|
||||
<xs:complexType name="IdentCon_T">
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Invisible" type="Invisible_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The invisible pins of this part.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="Invisible_T">
|
||||
<xs:attribute name="Name" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the invisible pin.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:element name="Labels" type="Labels_T"/>
|
||||
<xs:complexType name="Labels_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="LabelDeclaration"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Neg_T">
|
||||
<xs:attribute name="Name" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the negated pin.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:element name="Negated" type="Neg_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The negated pins of this part.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="AutomaticTyped_T">
|
||||
<xs:attribute name="Name" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the automatic chosen template parameter. Not for InstructionRef</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:element name="AutomaticTyped" type="AutomaticTyped_T"/>
|
||||
<xs:element name="Part" type="Part_T"/>
|
||||
<xs:element name="Equation" type="Equation_T"/>
|
||||
<xs:complexType name="Part_T">
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0">
|
||||
<xs:element ref="Equation" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The equation of this part. This is only used for the Calculate box.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element ref="Instance" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
<xs:group ref="PartSequence_G"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="PartAttribute_G"/>
|
||||
<xs:attribute name="Name" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="SimaticName_TP">
|
||||
<xs:minLength value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Version" type="VersionString_TP"/>
|
||||
<xs:attribute name="DisabledENO" type="xs:boolean" default="false"/>
|
||||
</xs:complexType>
|
||||
<xs:attributeGroup name="PartAttribute_G">
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="NameCon" type="NameCon_T"/>
|
||||
<xs:complexType name="NameCon_T">
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
<xs:attribute name="Name" type="PinName_TE" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Parts" type="Parts_T"/>
|
||||
<xs:complexType name="Parts_T">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Access"/>
|
||||
<xs:element ref="Part"/>
|
||||
<xs:element ref="Call"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:group name="PartSequence_G">
|
||||
<xs:sequence>
|
||||
<xs:element ref="TemplateValue" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="AutomaticTyped" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="Invisible" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="Negated" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element ref="Comment" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:group>
|
||||
<xs:simpleType name="PinName_TE">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:element name="Wire" type="Wire_T"/>
|
||||
<xs:complexType name="Wire_T">
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="Powerrail"/>
|
||||
<xs:element ref="NameCon"/>
|
||||
<xs:element ref="IdentCon"/>
|
||||
<xs:element ref="Openbranch"/>
|
||||
<xs:element ref="OpenCon"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="UId" type="xs:int" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="Wires" type="Wires_T"/>
|
||||
<xs:complexType name="Wires_T">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element ref="Wire"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.PlcBlocks.Access_v4.xsd"/>
|
||||
<xs:element name="StructuredText" type="StructuredText_T"/>
|
||||
<xs:complexType name="StructuredText_T">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:choice>
|
||||
<xs:element ref="Access"/>
|
||||
<xs:element ref="Token"/>
|
||||
<xs:element ref="Parameter"/>
|
||||
<xs:element ref="Text"/>
|
||||
<xs:group ref="Comment_G"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,482 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.PlcBlocks.CompileUnitCommon_v4.xsd"/>
|
||||
<xs:element name="StlStatement" type="StlStatement_T"/>
|
||||
<xs:complexType name="StlStatement_T">
|
||||
<xs:sequence>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:element ref="LabelDeclaration" minOccurs="0"/>
|
||||
<xs:sequence>
|
||||
<xs:element ref="StlToken">
|
||||
<xs:annotation>
|
||||
<xs:documentation>missing for empty lines</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:element ref="Access" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:element name="StlToken" type="StlToken_T"/>
|
||||
<xs:complexType name="StlToken_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="IntegerAttribute" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>for NumBLs. NumBLs is informative</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:group ref="Comment_G" minOccurs="0"/>
|
||||
<xs:element ref="Token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>e.g 0 1 for NOP 0, NOP 1; STW for L STW or DILG for L DILG; only if separated by comment</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="UId" type="xs:int" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Not allowed in STL</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Text" type="STL_TE" use="required"/>
|
||||
<!--<xs:attribute name="NumBLs" type="xs:int" default="0"/>-->
|
||||
</xs:complexType>
|
||||
<xs:element name="StatementList" type="StatementList_T"/>
|
||||
<xs:complexType name="StatementList_T">
|
||||
<xs:sequence>
|
||||
<xs:element ref="StlStatement" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="STL_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="A"/>
|
||||
<xs:enumeration value="AN"/>
|
||||
<xs:enumeration value="O"/>
|
||||
<xs:enumeration value="ON"/>
|
||||
<xs:enumeration value="X"/>
|
||||
<xs:enumeration value="XN"/>
|
||||
<xs:enumeration value="S"/>
|
||||
<xs:enumeration value="R"/>
|
||||
<xs:enumeration value="Assign"/>
|
||||
<xs:enumeration value="Rise"/>
|
||||
<xs:enumeration value="Fall"/>
|
||||
<xs:enumeration value="L"/>
|
||||
<xs:enumeration value="T"/>
|
||||
<xs:enumeration value="LAR1"/>
|
||||
<xs:enumeration value="LAR2"/>
|
||||
<xs:enumeration value="TAR1"/>
|
||||
<xs:enumeration value="TAR2"/>
|
||||
<xs:enumeration value="Extend">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SE, SV</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Free"/>
|
||||
<xs:enumeration value="LC"/>
|
||||
<xs:enumeration value="OffDelay">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SF, SA</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Retentive">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SS</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OnDelay">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SD, SE</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Pulse">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SP, SI</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CD"/>
|
||||
<xs:enumeration value="CU"/>
|
||||
<xs:enumeration value="CALL"/>
|
||||
<xs:enumeration value="CC"/>
|
||||
<xs:enumeration value="UC"/>
|
||||
<xs:enumeration value="OPEN_DB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>AUF</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OPEN_DI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>AUF DI</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="LT_I"/>
|
||||
<xs:enumeration value="LT_R"/>
|
||||
<xs:enumeration value="LT_D"/>
|
||||
<xs:enumeration value="LE_I"/>
|
||||
<xs:enumeration value="LE_R"/>
|
||||
<xs:enumeration value="LE_D"/>
|
||||
<xs:enumeration value="EQ_I"/>
|
||||
<xs:enumeration value="EQ_R"/>
|
||||
<xs:enumeration value="EQ_D"/>
|
||||
<xs:enumeration value="GE_I"/>
|
||||
<xs:enumeration value="GE_R"/>
|
||||
<xs:enumeration value="GE_D"/>
|
||||
<xs:enumeration value="GT_I"/>
|
||||
<xs:enumeration value="GT_R"/>
|
||||
<xs:enumeration value="GT_D"/>
|
||||
<xs:enumeration value="NE_I"/>
|
||||
<xs:enumeration value="NE_R"/>
|
||||
<xs:enumeration value="NE_D"/>
|
||||
<xs:enumeration value="JU">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPA</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPB</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JO">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPO</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JZ">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPZ</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JP">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPP</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JM">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPM</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JN">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPN</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JCN">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPBN</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JCB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPBB</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JNB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPBNB</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JBI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPBI</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JNBI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPBNI</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JOS">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPS</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JUN">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPU</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JMZ">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPMZ</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="JPZ">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SPZ</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="LOOP"/>
|
||||
<xs:enumeration value="JL"/>
|
||||
<xs:enumeration value="ADD"/>
|
||||
<xs:enumeration value="SLD"/>
|
||||
<xs:enumeration value="SLW"/>
|
||||
<xs:enumeration value="SRD"/>
|
||||
<xs:enumeration value="SRW"/>
|
||||
<xs:enumeration value="SRSD">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SSD, SVD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SRSW">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SSW, SVW</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="RLD"/>
|
||||
<xs:enumeration value="RRD"/>
|
||||
<xs:enumeration value="BLD"/>
|
||||
<xs:enumeration value="ADDAR1"/>
|
||||
<xs:enumeration value="ADDAR2"/>
|
||||
<xs:enumeration value="INC"/>
|
||||
<xs:enumeration value="DEC"/>
|
||||
<xs:enumeration value="AW"/>
|
||||
<xs:enumeration value="OW"/>
|
||||
<xs:enumeration value="XW"/>
|
||||
<xs:enumeration value="AD"/>
|
||||
<xs:enumeration value="OD"/>
|
||||
<xs:enumeration value="XD"/>
|
||||
<xs:enumeration value="A_BRACK"/>
|
||||
<xs:enumeration value="AN_BRACK"/>
|
||||
<xs:enumeration value="O_BRACK"/>
|
||||
<xs:enumeration value="ON_BRACK"/>
|
||||
<xs:enumeration value="X_BRACK"/>
|
||||
<xs:enumeration value="XN_BRACK"/>
|
||||
<xs:enumeration value="INV_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>KEW, INV_F</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NEG_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>KZW, NEG_F</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="INV_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>KED</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NEG_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>KZD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NEG_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>NEG_G, ND</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ABS_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ABS_G</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SQRT"/>
|
||||
<xs:enumeration value="SQR"/>
|
||||
<xs:enumeration value="LN"/>
|
||||
<xs:enumeration value="EXP"/>
|
||||
<xs:enumeration value="SIN"/>
|
||||
<xs:enumeration value="ASIN"/>
|
||||
<xs:enumeration value="COS"/>
|
||||
<xs:enumeration value="ACOS"/>
|
||||
<xs:enumeration value="TAN"/>
|
||||
<xs:enumeration value="ATAN"/>
|
||||
<xs:enumeration value="RLDA"/>
|
||||
<xs:enumeration value="RRDA"/>
|
||||
<xs:enumeration value="BTI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DEF</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ITB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DUF</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BTD">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DED</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DTB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>DUD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DTR">
|
||||
<xs:annotation>
|
||||
<xs:documentation>FDG</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="RND">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GFDN</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="RND_M">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GFDM</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="RND_P">
|
||||
<xs:annotation>
|
||||
<xs:documentation>GFDP</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TRUNC"/>
|
||||
<xs:enumeration value="ITD">
|
||||
<xs:annotation>
|
||||
<xs:documentation>FD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CAW">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TAW</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CAD">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TAD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TAR1_ACCU1"/>
|
||||
<xs:enumeration value="TAR2_ACCU1"/>
|
||||
<xs:enumeration value="ADD_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>+F</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SUB_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>-F</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MUL_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>xF</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DIV_I">
|
||||
<xs:annotation>
|
||||
<xs:documentation>:F</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ADD_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>+D</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SUB_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>-D</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MUL_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>xD</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DIV_D">
|
||||
<xs:annotation>
|
||||
<xs:documentation>:D</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MOD_D"/>
|
||||
<xs:enumeration value="L_DBLG"/>
|
||||
<xs:enumeration value="L_DILG"/>
|
||||
<xs:enumeration value="L_DBNO"/>
|
||||
<xs:enumeration value="L_DINO"/>
|
||||
<xs:enumeration value="ADD_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>+G</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SUB_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>-G</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MUL_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>xG</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="DIV_R">
|
||||
<xs:annotation>
|
||||
<xs:documentation>:G</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CAC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TAK</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="LEAVE"/>
|
||||
<xs:enumeration value="PUSH"/>
|
||||
<xs:enumeration value="POP"/>
|
||||
<xs:enumeration value="SET"/>
|
||||
<xs:enumeration value="NEG"/>
|
||||
<xs:enumeration value="CLR"/>
|
||||
<xs:enumeration value="BEC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BEB</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BRACKET">
|
||||
<xs:annotation>
|
||||
<xs:documentation>)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SAVE"/>
|
||||
<xs:enumeration value="NOP_0"/>
|
||||
<xs:enumeration value="NOP_1"/>
|
||||
<xs:enumeration value="MCR_BRACK">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MCR(</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BRACK_MCR">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MCR)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MCRA"/>
|
||||
<xs:enumeration value="MCRD"/>
|
||||
<xs:enumeration value="ENT"/>
|
||||
<xs:enumeration value="LAR1_ACCU1"/>
|
||||
<xs:enumeration value="LAR1_AR2"/>
|
||||
<xs:enumeration value="LAR2_ACCU1"/>
|
||||
<xs:enumeration value="TAR1_AR2"/>
|
||||
<xs:enumeration value="CAR">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TAR</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CDB">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TDB</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="COMMENT"/>
|
||||
<xs:enumeration value="EMPTY_LINE"/>
|
||||
<xs:enumeration value="PSEUDO"/>
|
||||
<xs:enumeration value="MOVE"/>
|
||||
<xs:enumeration value="MOVE_BLOCK"/>
|
||||
<xs:enumeration value="BE">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BEA</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BEU"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,154 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:include schemaLocation="SW.Common_v3.xsd"/>
|
||||
<xs:element name="Type">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Action"/>
|
||||
<xs:enumeration value="Interlock"/>
|
||||
<xs:enumeration value="Operand"/>
|
||||
<xs:enumeration value="Position"/>
|
||||
<xs:enumeration value="Reaction"/>
|
||||
<xs:enumeration value="MessageText"/>
|
||||
<xs:enumeration value="MessageError"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="TriggeringStatus" type="xs:boolean"/>
|
||||
<xs:element name="SupervisedStatus" type="xs:boolean"/>
|
||||
<xs:element name="SupervisedOperand">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="SubCategory2Number" type="xs:int"/>
|
||||
<xs:element name="SubCategory1Number" type="xs:int"/>
|
||||
<xs:element name="Number" type="xs:int"/>
|
||||
<xs:element name="DelayOperand">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Conditions">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Condition" maxOccurs="3"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ConditionOperand">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Number" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:int">
|
||||
<xs:enumeration value="1"/>
|
||||
<xs:enumeration value="2"/>
|
||||
<xs:enumeration value="3"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Condition">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="ConditionOperand"/>
|
||||
<xs:element ref="TriggeringStatus"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="CategoryNumber">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:int">
|
||||
<xs:minInclusive value="1"/>
|
||||
<xs:maxInclusive value="8"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="BlockTypeSupervisions">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BlockTypeSupervisionsType"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="BlockTypeSupervision">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="SupervisedOperand"/>
|
||||
<xs:element ref="SupervisedStatus"/>
|
||||
<xs:element ref="DelayOperand" minOccurs="0"/>
|
||||
<xs:element ref="Conditions" minOccurs="0"/>
|
||||
<xs:element ref="CategoryNumber"/>
|
||||
<xs:element ref="SubCategory1Number" minOccurs="0"/>
|
||||
<xs:element ref="SubCategory2Number" minOccurs="0"/>
|
||||
<xs:element ref="SpecificField" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Number" type="xs:int" use="required"/>
|
||||
<xs:attribute name="Type" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Action"/>
|
||||
<xs:enumeration value="Interlock"/>
|
||||
<xs:enumeration value="Operand"/>
|
||||
<xs:enumeration value="Position"/>
|
||||
<xs:enumeration value="Reaction"/>
|
||||
<xs:enumeration value="MessageText"/>
|
||||
<xs:enumeration value="MessageError"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="AssociatedValues">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="AssociatedValue" maxOccurs="3"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="AssociatedValue">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="AssociatedValueOperand" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="SpecificFieldText">
|
||||
<xs:complexType>
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element ref="MultiLanguageText"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="SpecificField">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element ref="AssociatedValues" minOccurs="0"/>
|
||||
<xs:element ref="SpecificFieldText" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="BlockTypeSupervisionsType">
|
||||
<xs:sequence>
|
||||
<xs:element ref="BlockTypeSupervision" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="AssociatedValueOperand">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Number" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:int">
|
||||
<xs:enumeration value="1"/>
|
||||
<xs:enumeration value="2"/>
|
||||
<xs:enumeration value="3"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Motion/Axis/v1"
|
||||
xmlns:tns="http://www.siemens.com/automation/Openness/SW/Motion/Axis/v1"
|
||||
elementFormDefault="qualified">
|
||||
<!-- Root element AdditionalData-->
|
||||
<xs:element name="AdditionalData" type="tns:AdditionalData_T"/>
|
||||
|
||||
<!-- Complex type AdditionalData_T-->
|
||||
<xs:complexType name="AdditionalData_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes additional data, such as Connections, for Axis and ExternalEncoder TOs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="Connection" type="tns:Connection_T" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="SynchronousAxisMasterValues" type="tns:SynchronousAxisMasterValues_T" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type Connection_T-->
|
||||
<xs:complexType name="Connection_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a connection of a TO interface.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Interface" type="tns:Interface_TE" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the Interface of the TO that is connected.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="InputAddress" type="xs:nonNegativeInteger" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Input bit address.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="OutputAddress" type="xs:nonNegativeInteger" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Output bit address.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ConnectOption" type="tns:ConnectOption_TE" use="optional" default="Default">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Connect option used when the connection has been created.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SensorIndexInActorTelegram" type="xs:nonNegativeInteger" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Index of sensor in actor telegram if connected to same telegram.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="PathToDBMember" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Path to a DB member.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="OutputTag" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name of a connected tag for analog connection.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Enumeration type Interface_TE-->
|
||||
<xs:simpleType name="Interface_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Actor"/>
|
||||
<xs:enumeration value="Sensor"/>
|
||||
<xs:enumeration value="Sensor[1]"/>
|
||||
<xs:enumeration value="Sensor[2]"/>
|
||||
<xs:enumeration value="Sensor[3]"/>
|
||||
<xs:enumeration value="Sensor[4]"/>
|
||||
<xs:enumeration value="Torque"/>
|
||||
<xs:enumeration value="OutputCam"/>
|
||||
<xs:enumeration value="MeasuringInput"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- Enumeration type ConnectOption_TE-->
|
||||
<xs:simpleType name="ConnectOption_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Default"/>
|
||||
<xs:enumeration value="AllowAllModules"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- Complex type SynchronousAxisMasterValues_T-->
|
||||
<xs:complexType name="SynchronousAxisMasterValues_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Contains a list of master values for TO_SynchronousAxis.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="SetPointCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a master value TO that is coupled via set points.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="ActualValueCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a master value TO that is coupled via actual values.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="DelayedCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a master value TO that is coupled via delayed values.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="LeadingAxisProxy" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a master value TO of type LeadingAxisProxy.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type TechnologicalObjectReference_T-->
|
||||
<xs:complexType name="TechnologicalObjectReference_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Ref" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the name of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Type" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the type of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2019. All rights reserved. -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Motion/Kinematics/v1"
|
||||
xmlns:tns="http://www.siemens.com/automation/Openness/SW/Motion/Kinematics/v1"
|
||||
elementFormDefault="qualified">
|
||||
<!-- Root element AdditionalData-->
|
||||
<xs:element name="AdditionalData" type="tns:AdditionalData_T"/>
|
||||
|
||||
<!-- Complex type AdditionalData_T-->
|
||||
<xs:complexType name="AdditionalData_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes additional data, such as connected axes, for Kinematics TOs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="KinematicsAxis" type="tns:TechnologicalObjectReferenceWithIndex_T" minOccurs="0" maxOccurs="4"/>
|
||||
<xs:element name="ConveyorTrackingLeadingValues" type="tns:ConveyorTrackingLeadingValues_T" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type TechnologicalObjectReferenceWithIndex_T-->
|
||||
<xs:complexType name="TechnologicalObjectReferenceWithIndex_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Index" type="tns:Integer1to4_T" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the name of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Ref" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the name of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Type" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the type of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Simple type Integer1to4_T-->
|
||||
<xs:simpleType name="Integer1to4_T">
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="1"/>
|
||||
<xs:maxInclusive value="4"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- Complex type ConveyorTrackingLeadingValues_T-->
|
||||
<xs:complexType name="ConveyorTrackingLeadingValues_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Contains a list of leading values for conveyor tracking.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="SetPointCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a leading value TO that is coupled via set points.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="ActualValueCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a leading value TO that is coupled via actual values.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="DelayedCoupling" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a leading value TO that is coupled via delayed values.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="LeadingAxisProxy" type="tns:TechnologicalObjectReference_T" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a leading value TO of type LeadingAxisProxy.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type TechnologicalObjectReference_T-->
|
||||
<xs:complexType name="TechnologicalObjectReference_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a reference to a Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Ref" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the name of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Type" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the type of the referenced Technological Object.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2018. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Motion/MeasuringInput/v1"
|
||||
xmlns:tns="http://www.siemens.com/automation/Openness/SW/Motion/MeasuringInput/v1"
|
||||
elementFormDefault="qualified">
|
||||
<!-- Root element AdditionalData-->
|
||||
<xs:element name="AdditionalData" type="tns:AdditionalData_T"/>
|
||||
|
||||
<!-- Complex type AdditionalData_T-->
|
||||
<xs:complexType name="AdditionalData_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes additional data, such as Connections, for MeasuringInput TOs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="Connection" type="tns:Connection_T" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type Connection_T-->
|
||||
<xs:complexType name="Connection_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a connection of a TO interface.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Interface" type="tns:Interface_TE" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the Interface of the TO that is connected.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="InputAddress" type="xs:nonNegativeInteger" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Input bit address.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Enumeration type Interface_TE-->
|
||||
<xs:simpleType name="Interface_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="MeasuringInput"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
</xs:schema>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2018. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Motion/OutputCam/v1"
|
||||
xmlns:tns="http://www.siemens.com/automation/Openness/SW/Motion/OutputCam/v1"
|
||||
elementFormDefault="qualified">
|
||||
<!-- Root element AdditionalData-->
|
||||
<xs:element name="AdditionalData" type="tns:AdditionalData_T"/>
|
||||
|
||||
<!-- Complex type AdditionalData_T-->
|
||||
<xs:complexType name="AdditionalData_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes additional data, such as Connections, for OutputCam and CamTrack TOs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="Connection" type="tns:Connection_T" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type Connection_T-->
|
||||
<xs:complexType name="Connection_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a connection of a TO interface.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Interface" type="tns:Interface_TE" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the Interface of the TO that is connected.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="OutputAddress" type="xs:nonNegativeInteger" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Output bit address.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="OutputTag" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name of a connected tag.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Enumeration type Interface_TE-->
|
||||
<xs:simpleType name="Interface_TE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="OutputCam"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2018. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Parameters/v1"
|
||||
xmlns:tns="http://www.siemens.com/automation/Openness/SW/Parameters/v1"
|
||||
elementFormDefault="qualified">
|
||||
<!-- Root element Parameters-->
|
||||
<xs:element name="Parameters" type="tns:Parameters_T"/>
|
||||
|
||||
<!-- Complex type Parameters_T-->
|
||||
<xs:complexType name="Parameters_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a list of parameters.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="Parameter" type="tns:Parameter_T" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Complex type Parameter_T-->
|
||||
<xs:complexType name="Parameter_T">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a single parameter, having Name and Value. If the Value is missing, the default value of the Parameter is used.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name of the Parameter</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Value" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Value of the Parameter</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,345 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright © Siemens AG 2008-2018. All rights reserved. -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.siemens.com/automation/Openness/SW/Motion/Cam/v1"
|
||||
xmlns:cam="http://www.siemens.com/automation/Openness/SW/Motion/Cam/v1"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!-- Global types -->
|
||||
|
||||
<xs:simpleType name="ProfileContinuity">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Position"/>
|
||||
<xs:enumeration value="Velocity"/>
|
||||
<xs:enumeration value="Acceleration"/>
|
||||
<xs:enumeration value="Jerk"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ElementContinuity">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="AsProfile"/>
|
||||
<xs:enumeration value="Position"/>
|
||||
<xs:enumeration value="Velocity"/>
|
||||
<xs:enumeration value="Acceleration"/>
|
||||
<xs:enumeration value="Jerk"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ProfileOptimizationGoal">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Velocity"/>
|
||||
<xs:enumeration value ="Acceleration"/>
|
||||
<xs:enumeration value ="Jerk"/>
|
||||
<xs:enumeration value ="DynamicMoment"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ElementOptimizationGoal">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="AsProfile"/>
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Velocity"/>
|
||||
<xs:enumeration value="Acceleration"/>
|
||||
<xs:enumeration value="Jerk"/>
|
||||
<xs:enumeration value="DynamicMoment"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ValueMode">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Relative"/>
|
||||
<xs:enumeration value="Absolute"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ProfileInterpolationMode">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Linear"/>
|
||||
<xs:enumeration value="CubicSpline"/>
|
||||
<xs:enumeration value="BezierSpline"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="BoundaryConditions">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="NoConstraint"/>
|
||||
<xs:enumeration value="FirstDerivative"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="DefinitionRange">
|
||||
<xs:attribute name="Start" type="xs:float"/>
|
||||
<xs:attribute name="End" type="xs:float"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="TrigonometricValues">
|
||||
<xs:attribute name="Amplitude" type="xs:float" default="1" />
|
||||
|
||||
<!-- two of four are required and at least StartPhase or EndPhase is required -->
|
||||
<xs:attribute name="StartPhase" type="xs:float" default="0" />
|
||||
<xs:attribute name="EndPhase" type="xs:float" default="6.2831853071795862" />
|
||||
<xs:attribute name="Frequency" type="xs:float" default="1" />
|
||||
<xs:attribute name="PeriodLength" type="xs:float" default="1" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="SegmentBase">
|
||||
<xs:attribute name="StartX" type="xs:float" use="required" />
|
||||
<xs:attribute name="EndX" type="xs:float" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="PointGroupApproximationMode">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="PointApproximation" />
|
||||
<xs:enumeration value="SegmentApproximation" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="PointGroupInterpolationMode">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="CubicSpline" />
|
||||
<xs:enumeration value="BezierSpline" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- Due to multiple usage, we place that element type explicitly here -->
|
||||
<xs:complexType name="PointElementType">
|
||||
<xs:attribute name="X" type="xs:float" use="required" />
|
||||
<xs:attribute name="Y" type="xs:float" use="required" />
|
||||
<xs:attribute name="Velocity" type="xs:float" />
|
||||
<xs:attribute name="Acceleration" type="xs:float" />
|
||||
<xs:attribute name="Jerk" type="xs:float" />
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Begin of our actual nodes -->
|
||||
<xs:element name="ProfileData">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
|
||||
<xs:element name="GeneralConfiguration" minOccurs="1" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<!-- Requires start < end validity - default is [0;360] -->
|
||||
<xs:element name="DesignLeadingRange" type="cam:DefinitionRange" minOccurs="1" maxOccurs="1" />
|
||||
<!-- Requires start < end validity - default is [-1;1] -->
|
||||
<xs:element name="DesignFollowingRange" type="cam:DefinitionRange" minOccurs="1" maxOccurs="1" />
|
||||
</xs:sequence>
|
||||
|
||||
<!-- Attributes -->
|
||||
<xs:attribute name="StandardContinuity" type="cam:ProfileContinuity" default="Acceleration" />
|
||||
<xs:attribute name="StandardOptimizationGoal" type="cam:ProfileOptimizationGoal" default="None" />
|
||||
<xs:attribute name="InterpolationMode" type="cam:ProfileInterpolationMode" default="CubicSpline" />
|
||||
<xs:attribute name="BoundaryConditions" type="cam:BoundaryConditions" default="NoConstraint" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<!-- The node of listed elements -->
|
||||
<xs:element name="Elements" minOccurs="1" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
|
||||
<xs:element name="Point" type="cam:PointElementType" />
|
||||
|
||||
<xs:element name="Line">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
<!-- Two out of three are required here -->
|
||||
<xs:attribute name="StartY" type="xs:float" />
|
||||
<xs:attribute name="EndY" type="xs:float" />
|
||||
<xs:attribute name="Gradient" type="xs:float" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="Polynomial">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
<xs:sequence>
|
||||
|
||||
<!-- With or without trigonometric portion ? If node doesn't exist, we simply use a non-trig. polynomial -->
|
||||
<!-- 2 out of 4 : startPhase, endPhase, frequency, periodLength -->
|
||||
<xs:element name="TrigonometricValues" type="cam:TrigonometricValues" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
<!-- For V3, one of these nodes is required. -->
|
||||
|
||||
<xs:choice>
|
||||
<xs:element name="Coefficients" >
|
||||
<xs:complexType>
|
||||
<xs:attribute name="C0" type="xs:float" default="0" />
|
||||
<xs:attribute name="C1" type="xs:float" default="0" />
|
||||
<xs:attribute name="C2" type="xs:float" default="0" />
|
||||
<xs:attribute name="C3" type="xs:float" default="0" />
|
||||
<xs:attribute name="C4" type="xs:float" default="0" />
|
||||
<xs:attribute name="C5" type="xs:float" default="0" />
|
||||
<xs:attribute name="C6" type="xs:float" default="0" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="Constraints" >
|
||||
<xs:complexType>
|
||||
<xs:attribute name="LeftValue" type="xs:float" />
|
||||
<xs:attribute name="RightValue" type="xs:float" />
|
||||
<xs:attribute name="LeftVelocity" type="xs:float" />
|
||||
<xs:attribute name="RightVelocity" type="xs:float" />
|
||||
<xs:attribute name="LeftAcceleration" type="xs:float" />
|
||||
<xs:attribute name="RightAcceleration" type="xs:float" />
|
||||
|
||||
<!-- For V3, only one of these three parameters is allowed and lambda is always assumed to be absolute there. -->
|
||||
<xs:attribute name="LeftJerk" type="xs:float" />
|
||||
<xs:attribute name="RightJerk" type="xs:float" />
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="VDITransition">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
|
||||
<!-- The VDI-rules we know so far -->
|
||||
<!-- If a VDI-rule occurs, the transition is handled as a VDI transition -->
|
||||
<!-- For V3, lambda is always expected to be absolute -> mode is ignored there. -->
|
||||
<xs:choice minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="DoubleHarmonicTransition" />
|
||||
<xs:element name="InclinedSine">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Linear" />
|
||||
<xs:element name="HarmonicCombination">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ModifiedAccelerationTrapezoid">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ModifiedSine">
|
||||
<xs:complexType>
|
||||
<!-- Choose one of three -->
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="Ca" type="xs:float" />
|
||||
<xs:attribute name="CaStar" type="xs:float" />
|
||||
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Polynomial">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="QuadraticParabola">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Sine">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="SineLineCombination">
|
||||
<xs:complexType>
|
||||
<!-- Both parameters can be used -->
|
||||
<xs:attribute name="Lambda" type="xs:float" />
|
||||
<xs:attribute name="C" type="xs:float" />
|
||||
|
||||
<xs:attribute name="LambdaMode" type="cam:ValueMode" default="Absolute" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
|
||||
<xs:attribute name="LeftContinuity" type="cam:ElementContinuity" default="AsProfile" />
|
||||
<xs:attribute name="RightContinuity" type="cam:ElementContinuity" default="AsProfile" />
|
||||
<xs:attribute name="OptimizationGoal" type="cam:ElementOptimizationGoal" default="AsProfile" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="InverseSine">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
<xs:attribute name="InterpolationPointCount" type="xs:integer" default="32" />
|
||||
<xs:attribute name="MaxFollowingValueTolerance" type="xs:float" default="0.01" />
|
||||
<xs:attribute name="MathStartX" type="xs:float" default="-0.95" />
|
||||
<xs:attribute name="MathEndX" type="xs:float" default="0.95" />
|
||||
<xs:attribute name="Minimum" type="xs:float" />
|
||||
<xs:attribute name="Maximum" type="xs:float" />
|
||||
<xs:attribute name="Inversed" type="xs:boolean" default="false" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="Sine">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
<xs:attribute name="Amplitude" type="xs:float" default="1" />
|
||||
<!-- two of four are required and at least StartPhase or EndPhase is required -->
|
||||
<xs:attribute name="StartPhase" type="xs:float" default="0" />
|
||||
<xs:attribute name="EndPhase" type="xs:float" default="6.2831853071795862" />
|
||||
<xs:attribute name="Frequency" type="xs:float" default="1" />
|
||||
<xs:attribute name="PeriodLength" type="xs:float" default="1" />
|
||||
<!-- Two out of three are required here -->
|
||||
<xs:attribute name="Inclination" type="xs:float" default="0" />
|
||||
<xs:attribute name="StartOffset" type="xs:float" default="0" />
|
||||
<xs:attribute name="EndOffset" type="xs:float" default="0" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="PointGroup">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="cam:SegmentBase">
|
||||
|
||||
<!-- Allow points to be sub-elements of the group-->
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="Point" type="cam:PointElementType" />
|
||||
</xs:sequence>
|
||||
|
||||
<xs:attribute name="ApproximationDataPoints" type="xs:integer" default="32" />
|
||||
<xs:attribute name="ApproximationTolerance" type="xs:float" default="0.01" />
|
||||
<xs:attribute name="LeadingValueMode" type="cam:ValueMode" default="Absolute" />
|
||||
<xs:attribute name="FollowingValueMode" type="cam:ValueMode" default="Absolute" />
|
||||
<xs:attribute name="ApproximationMode" type="cam:PointGroupApproximationMode" default="SegmentApproximation" />
|
||||
<xs:attribute name="InterpolationMode" type="cam:PointGroupInterpolationMode" default="CubicSpline" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
||||
]
|
|
@ -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}
|
|
@ -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
|
||||
]
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
||||
]
|
|
@ -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}
|
||||
]
|
|
@ -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}
|
|
@ -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}
|
||||
]
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
|
@ -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}
|
||||
]
|
|
@ -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}
|
||||
]
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -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)
|
|
@ -1,75 +1,87 @@
|
|||
# processors/process_add.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
SCL_SUFFIX = "_sympy_processed" # Usar el nuevo sufijo
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_add(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
in1_info = instruction["inputs"].get("in1")
|
||||
in2_info = instruction["inputs"].get("in2")
|
||||
in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map)
|
||||
in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if en_scl is None or in1_scl is None or in2_scl is None:
|
||||
# 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
|
||||
|
||||
target_scl = get_target_scl_name(
|
||||
instruction, "out", network_id, default_to_temp=True
|
||||
)
|
||||
if target_scl is None:
|
||||
print(f"Error: Sin destino ADD {instr_uid}")
|
||||
instruction["scl"] = f"// ERROR: Add {instr_uid} sin destino"
|
||||
instruction["type"] += "_error"
|
||||
return True
|
||||
# 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)
|
||||
|
||||
# Formatear operandos si son variables
|
||||
op1 = (
|
||||
format_variable_name(in1_scl)
|
||||
if in1_info and in1_info.get("type") == "variable"
|
||||
else in1_scl
|
||||
)
|
||||
op2 = (
|
||||
format_variable_name(in2_scl)
|
||||
if in2_info and in2_info.get("type") == "variable"
|
||||
else in2_scl
|
||||
)
|
||||
# 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
|
||||
|
||||
# Añadir paréntesis si es necesario
|
||||
op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1
|
||||
op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2
|
||||
# Generar SCL Core
|
||||
scl_core = f"{target_scl_name} := {op1_scl_formatted} + {op2_scl_formatted};"
|
||||
|
||||
scl_core = f"{target_scl} := {op1} + {op2};"
|
||||
scl_final = (
|
||||
f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
)
|
||||
# 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)
|
||||
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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")
|
||||
scl_map[map_key_out] = target_scl
|
||||
sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino (string)
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
return True
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
# --- Function code ends ---
|
||||
return True
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador Add."""
|
||||
return {'type_name': 'add', 'processor_func': process_add, 'priority': 4}
|
||||
# Asegurar que la clave coincida con el tipo en JSON ('add')
|
||||
return {'type_name': 'add', 'processor_func': process_add, 'priority': 4}
|
|
@ -1,112 +1,118 @@
|
|||
# processors/process_blkmov.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
SCL_SUFFIX = "_sympy_processed" # Usar el nuevo sufijo
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_blkmov(instruction, network_id, scl_map, access_map, data):
|
||||
def process_blkmov(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera SCL usando BLKMOV directamente como nombre de función,
|
||||
sin COUNT y con formato específico, según solicitud del usuario.
|
||||
ADVERTENCIA: Es MUY PROBABLE que esto NO compile en TIA Portal estándar,
|
||||
ya que BLKMOV no es una función SCL y MOVE_BLK requiere COUNT.
|
||||
simplificando la condición EN.
|
||||
ADVERTENCIA: Sintaxis BLKMOV probablemente no compile en TIA estándar.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
return False # Ya procesado o con error
|
||||
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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
# 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")
|
||||
# ¡IMPORTANTE! Obtenemos el nombre RAW antes de formatearlo para usarlo como pide el usuario
|
||||
# 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 de entrada (solo necesitamos que EN esté resuelto)
|
||||
if en_scl is None:
|
||||
return False # Dependencia EN no lista
|
||||
# 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"] += "_error"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# --- Obtener Destinos (Salidas) ---
|
||||
# RET_VAL (Usamos get_target_scl_name para manejar variables temporales si es necesario)
|
||||
retval_target_scl = get_target_scl_name(
|
||||
instruction, "RET_VAL", network_id, default_to_temp=True
|
||||
)
|
||||
if retval_target_scl is None:
|
||||
print(f"Error: BLKMOV {instr_uid} sin destino claro para RET_VAL.")
|
||||
instruction["scl"] = f"// ERROR: BLKMOV {instr_uid} sin destino RET_VAL"
|
||||
instruction["type"] += "_error"
|
||||
# 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 (Obtenemos el nombre RAW para usarlo como pide el usuario)
|
||||
# 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") # Nombre raw del JSON
|
||||
else:
|
||||
print(f"Advertencia: Destino DSTBLK de BLKMOV {instr_uid} no es una variable (Tipo: {dest_access.get('type')}).")
|
||||
else:
|
||||
print(f"Error: No se encontró un destino único y válido para DSTBLK en BLKMOV {instr_uid}.")
|
||||
|
||||
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"] += "_error"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# --- Formateo especial para SRCBLK/DSTBLK como pidió el usuario ---
|
||||
# Asume formato "DB".Variable o "Struct".Variable del JSON y lo mantiene
|
||||
# (Esto anula la limpieza normal de format_variable_name para estos parámetros)
|
||||
srcblk_final_str = raw_srcblk_name if raw_srcblk_name else "_ERROR_SRC_"
|
||||
dstblk_final_str = raw_dstblk_name if raw_dstblk_name else "_ERROR_DST_"
|
||||
# --- 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 Exacto Solicitado ---
|
||||
# --- 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}); "
|
||||
f"DSTBLK => {dstblk_final_str}); " # Usar => para Out/InOut
|
||||
f"// ADVERTENCIA: BLKMOV usado directamente, probablemente no compile!"
|
||||
)
|
||||
|
||||
# Añadir condición EN (usando la representación SCL obtenida para EN)
|
||||
scl_final = (
|
||||
f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
)
|
||||
# --- 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)
|
||||
|
||||
# --- Actualizar Instrucción y Mapa SCL ---
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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
|
||||
|
||||
# Propagar ENO (igual que EN)
|
||||
# --- 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")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
sympy_map[map_key_eno] = sympy_en_expr
|
||||
|
||||
# Propagar el valor de retorno (el contenido de la variable asignada a RET_VAL)
|
||||
# Propagar el valor de retorno (nombre SCL string del destino de RET_VAL)
|
||||
map_key_ret_val = (network_id, instr_uid, "RET_VAL")
|
||||
scl_map[map_key_ret_val] = retval_target_scl # El valor es lo que sea que se asigne
|
||||
sympy_map[map_key_ret_val] = retval_target_scl
|
||||
|
||||
return True
|
||||
|
||||
# ... (Asegúrate de que esta función está registrada en processor_map como antes) ...
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador BLKMOV."""
|
||||
# Asumiendo que 'BLKMOV' es el type en el JSON simplificado
|
||||
return {'type_name': 'blkmov', 'processor_func': process_blkmov, 'priority': 6}
|
||||
# Asegurarse que el type_name coincida con el JSON ('blkmov' parece probable)
|
||||
return {'type_name': 'blkmov', 'processor_func': process_blkmov, 'priority': 6}
|
|
@ -1,141 +1,131 @@
|
|||
# processors/process_call.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
# Definir sufijo globalmente o importar
|
||||
SCL_SUFFIX = "_sympy_processed"
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_call(instruction, network_id, scl_map, access_map, data):
|
||||
def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type = instruction.get("type", "") # Usar get con default
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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
|
||||
# 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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
if en_scl is None:
|
||||
return False # Dependencia EN no resuelta
|
||||
sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
|
||||
|
||||
# --- Procesar Parámetros de Entrada/Salida ---
|
||||
# Necesitamos iterar sobre los pines definidos en la interfaz del bloque llamado.
|
||||
# Esta información no está directamente en la instrucción 'Call' del JSON simplificado.
|
||||
# ¡Limitación! Sin la interfaz del bloque llamado, solo podemos manejar EN/ENO
|
||||
# y asumir una llamada sin parámetros o con parámetros conectados implícitamente.
|
||||
if sympy_en_expr is None:
|
||||
# print(f"DEBUG Call {instr_uid}: EN dependency not ready.")
|
||||
return False # Dependencia EN no resuelta
|
||||
|
||||
# Solución temporal: Buscar conexiones en 'inputs' y 'outputs' que NO sean 'en'/'eno'
|
||||
# y construir la llamada basándose en eso. Esto es muy heurístico.
|
||||
# --- Procesar Parámetros de Entrada ---
|
||||
scl_call_params = []
|
||||
processed_inputs = {"en"} # Marcar 'en' como ya procesado
|
||||
for pin_name, source_info in instruction.get("inputs", {}).items():
|
||||
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:
|
||||
param_scl = get_scl_representation(
|
||||
source_info, network_id, scl_map, access_map
|
||||
)
|
||||
if param_scl is None:
|
||||
# print(f"DEBUG: Call {instr_uid} esperando parámetro de entrada {pin_name}")
|
||||
return False # Dependencia de parámetro no resuelta
|
||||
# Formatear si es variable
|
||||
param_scl_formatted = (
|
||||
format_variable_name(param_scl)
|
||||
if source_info.get("type") == "variable"
|
||||
else param_scl
|
||||
)
|
||||
scl_call_params.append(
|
||||
f"{format_variable_name(pin_name)} := {param_scl_formatted}"
|
||||
)
|
||||
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)
|
||||
|
||||
# Procesar parámetros de salida (asignaciones después de la llamada o pasados como VAR_IN_OUT/VAR_OUTPUT)
|
||||
# Esto es aún más complejo. SCL normalmente asigna salidas después o usa punteros/referencias.
|
||||
# Simplificación: Asumir que las salidas se manejan por asignación posterior si es necesario,
|
||||
# o que son VAR_OUTPUT y se acceden como instancia.salida.
|
||||
# Por ahora, no generamos asignaciones explícitas para las salidas aquí.
|
||||
if not dependencies_resolved:
|
||||
return False
|
||||
|
||||
# --- Construcción de la Llamada SCL ---
|
||||
# --- 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: Llamada a FB '{block_name_scl}' (UID {instr_uid}) sin DB de instancia especificado."
|
||||
)
|
||||
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"] = "Call_FB_error"
|
||||
return True # Procesado con error
|
||||
# Llamada a FB con DB de instancia
|
||||
instruction["type"] = f"Call_FB_error"
|
||||
return True
|
||||
scl_call_body = f"{instance_db_scl}({param_string});"
|
||||
elif block_type == "FC":
|
||||
# Llamada a 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}"
|
||||
)
|
||||
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 parcial
|
||||
instruction["type"] = f"Call_{block_type}_error" # Marcar como error
|
||||
|
||||
# --- Aplicar Condición EN ---
|
||||
# --- Aplicar Condición EN (usando la expresión SymPy EN) ---
|
||||
scl_final = ""
|
||||
if en_scl != "TRUE":
|
||||
# Indentar la llamada dentro del IF
|
||||
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_scl} THEN\n{indented_call}\nEND_IF;"
|
||||
scl_final = f"IF {en_condition_scl} THEN\n{indented_call}\nEND_IF;"
|
||||
else:
|
||||
scl_final = scl_call_body
|
||||
|
||||
# --- Actualizar JSON y Mapa SCL ---
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = (
|
||||
f"Call_{block_type}_scl"
|
||||
if "_error" not in instruction["type"]
|
||||
else instruction["type"]
|
||||
)
|
||||
# --- 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 scl_map con el estado ENO (igual a EN para llamadas simples sin manejo explícito de ENO)
|
||||
# Actualizar sympy_map con el estado ENO (es la expresión SymPy de EN)
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
# Propagar valores de salida (si pudiéramos determinarlos)
|
||||
# Ejemplo: Si supiéramos que hay una salida 'Out1' de tipo INT
|
||||
# map_key_out1 = (network_id, instr_uid, "Out1")
|
||||
# if block_type == "FB" and instance_db_scl:
|
||||
# scl_map[map_key_out1] = f"{instance_db_scl}.Out1" # Acceso a salida de instancia
|
||||
# else:
|
||||
# # Para FCs, necesitaríamos una variable temporal o asignación explícita
|
||||
# temp_out1 = generate_temp_var_name(network_id, instr_uid, "Out1")
|
||||
# # Modificar scl_call_body para incluir la asignación: Out1 => temp_out1
|
||||
# scl_map[map_key_out1] = temp_out1
|
||||
# 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
|
||||
|
||||
# --- Procesador de Temporizadores (TON, TOF) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para las llamadas a FC y FB."""
|
||||
# Esta función maneja tanto FC como FB. El despachador en x2_process.py
|
||||
# usará 'call_fc' o 'call_fb' como clave basada en block_type.
|
||||
return [
|
||||
{'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6},
|
||||
{'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6}
|
||||
]
|
||||
]
|
|
@ -1,79 +1,82 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
# 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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_coil(instruction, network_id, scl_map, access_map, data):
|
||||
"""Genera la asignación para Coil. Si la entrada viene de PBox/NBox,
|
||||
añade la actualización de memoria del flanco DESPUÉS de la asignación."""
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
operand_info = instruction["inputs"].get("operand")
|
||||
sympy_expr_in = get_sympy_representation(coil_input_info, network_id, sympy_map, symbol_manager)
|
||||
|
||||
in_rlo_scl = get_scl_representation(coil_input_info, network_id, scl_map, access_map)
|
||||
operand_scl = get_scl_representation(operand_info, network_id, scl_map, access_map)
|
||||
# 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
|
||||
|
||||
if in_rlo_scl is None or operand_scl is None: return False
|
||||
# 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
|
||||
|
||||
if not (operand_info and operand_info.get("type") == "variable"):
|
||||
instruction["scl"] = f"// ERROR: Coil {instr_uid} operando no es variable o falta info"
|
||||
instruction["type"] = instr_type + "_error"
|
||||
return True
|
||||
# *** 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
|
||||
|
||||
operand_scl_formatted = format_variable_name(operand_scl)
|
||||
if in_rlo_scl == "(TRUE)": in_rlo_scl = "TRUE"
|
||||
elif in_rlo_scl == "(FALSE)": in_rlo_scl = "FALSE"
|
||||
# *** Convert simplified expression back to SCL string ***
|
||||
condition_scl = sympy_expr_to_scl(simplified_expr, symbol_manager)
|
||||
|
||||
# Generar la asignación SCL principal de la bobina
|
||||
scl_assignment = f"{operand_scl_formatted} := {in_rlo_scl};"
|
||||
scl_final = scl_assignment # Inicializar SCL final
|
||||
# Generate the final SCL assignment
|
||||
scl_assignment = f"{target_scl_name} := {condition_scl};"
|
||||
scl_final = scl_assignment
|
||||
|
||||
# --- Lógica para añadir actualización de memoria de flancos ---
|
||||
# --- 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")
|
||||
|
||||
# Buscar la instrucción fuente PBox/NBox
|
||||
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:
|
||||
source_type = source_instruction.get("type","").replace('_scl','').replace('_error','')
|
||||
# Si la fuente es PBox o NBox y tiene el campo temporal con la actualización
|
||||
if source_type in ["PBox", "NBox"] and '_edge_mem_update_scl' in source_instruction:
|
||||
mem_update_scl_combined = source_instruction.get('_edge_mem_update_scl') # Obtener update+comment
|
||||
|
||||
# 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:
|
||||
# Añadir la actualización DESPUÉS de la asignación de la bobina, USANDO \n
|
||||
scl_final = f"{scl_assignment}\n{mem_update_scl_combined}"
|
||||
# Marcar la instrucción PBox/NBox para que x3 no escriba su SCL (que ahora está vacío/comentario)
|
||||
source_instruction['scl'] = f"// Logic moved to Coil {instr_uid}" # Actualizar PBox/NBox SCL
|
||||
# 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 + SCL_SUFFIX
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
# Coil typically doesn't output to scl_map
|
||||
|
||||
return True
|
||||
|
||||
# EN x2_process.py, junto a otras funciones process_xxx
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- 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}
|
||||
return {'type_name': 'coil', 'processor_func': process_coil, 'priority': 3}
|
|
@ -1,81 +1,87 @@
|
|||
# processors/process_comparison.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_comparison(instruction, network_id, scl_map, access_map, data):
|
||||
def process_comparison(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera la expresión SCL para Comparadores (GT, LT, GE, LE, NE).
|
||||
El resultado se propaga por scl_map['out'].
|
||||
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 = instruction["type"] # GT, LT, GE, LE, NE
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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 operadores SCL
|
||||
op_map = {"GT": ">", "LT": "<", "GE": ">=", "LE": "<=", "NE": "<>"}
|
||||
scl_operator = op_map.get(instr_type)
|
||||
if not scl_operator:
|
||||
instruction["scl"] = f"// ERROR: Tipo de comparación no soportado: {instr_type}"
|
||||
instruction["type"] += "_error"
|
||||
# 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
|
||||
# Obtener operandos como expresiones SymPy o constantes/strings
|
||||
in1_info = instruction["inputs"].get("in1")
|
||||
in2_info = instruction["inputs"].get("in2")
|
||||
in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map)
|
||||
in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if in1_scl is None or in2_scl is None:
|
||||
return False # Dependencias no listas
|
||||
# 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
|
||||
|
||||
# Formatear operandos si son variables
|
||||
op1 = format_variable_name(in1_scl) if in1_info and in1_info.get("type") == "variable" else in1_scl
|
||||
op2 = format_variable_name(in2_scl) if in2_info and in2_info.get("type") == "variable" else in2_scl
|
||||
# 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
|
||||
|
||||
# Añadir paréntesis si contienen espacios (poco probable tras formatear)
|
||||
op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1
|
||||
op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2
|
||||
# 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
|
||||
|
||||
comparison_scl = f"{op1} {scl_operator} {op2}"
|
||||
|
||||
# Guardar resultado en el mapa para 'out'
|
||||
# Guardar resultado en el mapa para 'out' (es una expresión booleana SymPy)
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
scl_map[map_key_out] = f"({comparison_scl})" # Poner paréntesis por seguridad
|
||||
|
||||
# Manejar entrada 'pre'/RLO -> ENO (como en EQ)
|
||||
pre_input = instruction["inputs"].get("pre") # Asumir 'pre' como en EQ
|
||||
en_scl = get_scl_representation(pre_input, network_id, scl_map, access_map) if pre_input else "TRUE"
|
||||
if en_scl is None:
|
||||
return False # Dependencia 'pre'/'en' no lista
|
||||
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")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
sympy_map[map_key_eno] = sympy_pre_rlo
|
||||
|
||||
instruction["scl"] = f"// Comparison {instr_type} {instr_uid}: {comparison_scl}"
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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
|
||||
|
||||
# --- Procesador de Matemáticas (ADD ya existe, añadir otros) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para los comparadores (excepto EQ)."""
|
||||
# Esta función maneja múltiples tipos de comparación.
|
||||
"""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} # <>
|
||||
]
|
||||
{'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
|
||||
]
|
|
@ -1,73 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
# 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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_contact(instruction, network_id, scl_map, access_map, data):
|
||||
"""Traduce Contact (normal o negado) a una expresión booleana SCL."""
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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
|
||||
|
||||
# --- INICIO LEER NEGACIÓN ---
|
||||
# Verificar si el pin 'operand' está marcado como negado en el JSON
|
||||
is_negated = instruction.get("negated_pins", {}).get("operand", False)
|
||||
# --- FIN LEER NEGACIÓN ---
|
||||
|
||||
# print(f"DEBUG: Intentando procesar CONTACT{' (N)' if is_negated else ''} - UID: {instr_uid} en Red: {network_id}")
|
||||
|
||||
# Get incoming SymPy expression (RLO)
|
||||
in_input = instruction["inputs"].get("in")
|
||||
in_rlo_scl = (
|
||||
"TRUE"
|
||||
if in_input is None
|
||||
else get_scl_representation(in_input, network_id, scl_map, access_map)
|
||||
)
|
||||
operand_scl = get_scl_representation(
|
||||
instruction["inputs"].get("operand"), network_id, scl_map, access_map
|
||||
)
|
||||
sympy_expr_in = get_sympy_representation(in_input, network_id, sympy_map, symbol_manager)
|
||||
|
||||
if in_rlo_scl is None or operand_scl is None:
|
||||
return False
|
||||
# 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
|
||||
|
||||
# Usar is_negated para aplicar NOT
|
||||
term = f"NOT {operand_scl}" if is_negated else operand_scl
|
||||
if not (term.startswith('"') and term.endswith('"')):
|
||||
# Añadir paréntesis si es NOT o si contiene espacios/operadores
|
||||
if is_negated or (
|
||||
" " in term and not (term.startswith("(") and term.endswith(")"))
|
||||
):
|
||||
term = f"({term})"
|
||||
# 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
|
||||
|
||||
new_rlo_scl = (
|
||||
term
|
||||
if in_rlo_scl == "TRUE"
|
||||
else (
|
||||
f"({in_rlo_scl}) AND {term}"
|
||||
if ("AND" in in_rlo_scl or "OR" in in_rlo_scl)
|
||||
and not (in_rlo_scl.startswith("(") and in_rlo_scl.endswith(")"))
|
||||
else f"{in_rlo_scl} AND {term}"
|
||||
)
|
||||
)
|
||||
# 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')
|
||||
|
||||
map_key = (network_id, instr_uid, "out")
|
||||
scl_map[map_key] = new_rlo_scl
|
||||
instruction["scl"] = f"// RLO: {new_rlo_scl}"
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
return True
|
||||
|
||||
# --- process_edge_detector MODIFICADA ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador Contact."""
|
||||
return {'type_name': 'contact', 'processor_func': process_contact, 'priority': 1}
|
||||
# Ensure 'data' argument is added if needed by the processor function signature change
|
||||
return {'type_name': 'contact', 'processor_func': process_contact, 'priority': 1}
|
|
@ -1,81 +1,90 @@
|
|||
# processors/process_convert.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_convert(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
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")
|
||||
in_scl = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
sympy_or_const_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
|
||||
if en_scl is None or in_scl is None:
|
||||
return False # Esperar si dependencias no listas
|
||||
# Obtener destino SCL
|
||||
target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
|
||||
|
||||
target_scl = get_target_scl_name(
|
||||
instruction, "out", network_id, default_to_temp=True
|
||||
)
|
||||
if target_scl is None:
|
||||
print(f"Error: Sin destino claro para CONVERT {instr_uid}")
|
||||
instruction["scl"] = f"// ERROR: Convert {instr_uid} sin destino"
|
||||
instruction["type"] += "_error"
|
||||
return True # Procesado con error
|
||||
# Verificar dependencias
|
||||
if sympy_en_expr is None or sympy_or_const_in is None or target_scl_name is None:
|
||||
return False
|
||||
|
||||
# Formatear entrada si es variable
|
||||
in_scl_formatted = (
|
||||
format_variable_name(in_scl)
|
||||
if in_info and in_info.get("type") == "variable"
|
||||
else in_scl
|
||||
)
|
||||
# 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 (simplificado, necesitaría info del XML original)
|
||||
# Asumimos que el tipo está en TemplateValues o inferirlo del nombre/contexto
|
||||
target_type = instruction.get("template_values", {}).get(
|
||||
"destType", "VARIANT"
|
||||
) # Ejemplo, ajustar según XML real
|
||||
conversion_func = f"{target_type}_TO_" # Necesita el tipo de origen también
|
||||
# Esta parte es compleja sin saber los tipos exactos. Usaremos una conversión genérica o MOVE.
|
||||
# Para una conversión real, necesitaríamos algo como:
|
||||
# conversion_expr = f"CONVERT(IN := {in_scl_formatted}, OUT => {target_scl})" # Sintaxis inventada
|
||||
# O usar funciones específicas: INT_TO_REAL, etc.
|
||||
# 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
|
||||
|
||||
# Simplificación: Usar asignación directa (MOVE implícito) o función genérica si existe
|
||||
# Asumiremos asignación directa por ahora.
|
||||
conversion_expr = in_scl_formatted
|
||||
scl_core = f"{target_scl} := {conversion_expr};"
|
||||
# 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}"
|
||||
|
||||
# Añadir IF EN si es necesario
|
||||
scl_final = (
|
||||
f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
)
|
||||
# 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}"
|
||||
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
|
||||
# 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")
|
||||
scl_map[map_key_out] = target_scl # El valor de salida es el contenido del destino
|
||||
# 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")
|
||||
scl_map[map_key_eno] = en_scl # ENO sigue a EN
|
||||
return True
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
# --- Function code ends ---
|
||||
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}
|
||||
return {'type_name': 'convert', 'processor_func': process_convert, 'priority': 4}
|
|
@ -1,94 +1,110 @@
|
|||
# processors/process_counter.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_counter(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"] # CTU, CTD, CTUD
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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. Obtener Inputs (varía según tipo)
|
||||
params = []
|
||||
resolved = True
|
||||
# 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
|
||||
|
||||
input_pins = []
|
||||
if instr_type == "CTU": input_pins = ["CU", "R", "PV"]
|
||||
elif instr_type == "CTD": input_pins = ["CD", "LD", "PV"]
|
||||
elif instr_type == "CTUD": input_pins = ["CU", "CD", "R", "LD", "PV"]
|
||||
else:
|
||||
instruction["scl"] = f"// ERROR: Tipo de contador no soportado: {instr_type}"
|
||||
instruction["type"] += "_error"
|
||||
return True # Procesado con error
|
||||
# 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 is None and pin not in ["R", "LD"]: # R y LD pueden no estar conectados
|
||||
print(f"Error: Falta entrada requerida '{pin}' para {instr_type} UID {instr_uid}.")
|
||||
# Permitir continuar si solo faltan R o LD opcionales? Por ahora no.
|
||||
instruction["scl"] = f"// ERROR: Falta entrada requerida '{pin}' para {instr_type} UID {instr_uid}."
|
||||
instruction["type"] += "_error"
|
||||
return True # Error
|
||||
elif pin_info: # Si el pin existe en el JSON
|
||||
scl_pin = get_scl_representation(pin_info, network_id, scl_map, access_map)
|
||||
if scl_pin is None:
|
||||
resolved = False
|
||||
break # Salir si una dependencia no está lista
|
||||
scl_pin_formatted = format_variable_name(scl_pin) if pin_info.get("type") == "variable" else scl_pin
|
||||
params.append(f"{pin} := {scl_pin_formatted}")
|
||||
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 resolved: return False
|
||||
if not dependencies_resolved:
|
||||
return False
|
||||
|
||||
# 2. Obtener Nombre de Instancia (NECESITA MEJORA EN x1.py)
|
||||
instance_name = instruction.get("instance_db")
|
||||
if not instance_name:
|
||||
instance_name = f"#COUNTER_INSTANCE_{instr_uid}" # Placeholder
|
||||
print(f"Advertencia: No se encontró instancia para {instr_type} UID {instr_uid}. Usando placeholder '{instance_name}'. Ajustar x1.py y declarar en x3.py.")
|
||||
else:
|
||||
instance_name = format_variable_name(instance_name)
|
||||
# 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)
|
||||
|
||||
# 3. Generar la llamada SCL
|
||||
param_string = ", ".join(params)
|
||||
scl_call = f"{instance_name}({param_string}); // TODO: Declarar {instance_name} : {instr_type}; en VAR_STAT o VAR"
|
||||
# 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"
|
||||
|
||||
instruction["scl"] = scl_call
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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 scl_map para las salidas (QU, QD, CV)
|
||||
output_pins = []
|
||||
if instr_type == "CTU": output_pins = ["QU", "CV"]
|
||||
elif instr_type == "CTD": output_pins = ["QD", "CV"]
|
||||
elif instr_type == "CTUD": output_pins = ["QU", "QD", "CV"]
|
||||
# 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)
|
||||
scl_map[map_key] = f"{instance_name}.{pin}"
|
||||
# Contadores no tienen ENO estándar en LAD/FBD
|
||||
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
|
||||
|
||||
# --- Procesador de Comparadores (EQ ya existe, añadir otros) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para los contadores CTU, CTD, CTUD."""
|
||||
# Asumiendo que los tipos en el JSON son 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}
|
||||
]
|
||||
]
|
|
@ -1,91 +1,85 @@
|
|||
# processors/process_edge_detector.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_edge_detector(instruction, network_id, scl_map, access_map, data):
|
||||
"""Genera SCL para PBox (P_TRIG) o NBox (N_TRIG).
|
||||
Guarda la expresión del pulso en scl_map['out'] y la actualización
|
||||
de memoria en un campo temporal '_edge_mem_update_scl'.
|
||||
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.
|
||||
Usa el nombre de memoria original sin renombrar.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type_original = instruction["type"] # PBox o NBox
|
||||
|
||||
instr_type_original = instruction.get("type", "") # PBox o NBox
|
||||
if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
|
||||
return False # Ya procesado o error
|
||||
return False
|
||||
|
||||
# 1. Obtener CLK y MemBit original
|
||||
# 1. Obtener CLK (como SymPy expr) y MemBit (como SymPy Symbol)
|
||||
clk_input = instruction["inputs"].get("in")
|
||||
mem_bit_input = instruction["inputs"].get("bit")
|
||||
clk_scl = get_scl_representation(clk_input, network_id, scl_map, access_map)
|
||||
mem_bit_scl_original = get_scl_representation(mem_bit_input, network_id, scl_map, access_map) # Ej: "M19001"
|
||||
|
||||
# 2. Verificar dependencias y tipo de MemBit
|
||||
if clk_scl is None: return False
|
||||
if mem_bit_scl_original is None:
|
||||
instruction["scl"] = f"// ERROR: {instr_type_original} {instr_uid} MemBit no resuelto."
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
if not (mem_bit_input and mem_bit_input.get("type") == "variable"):
|
||||
instruction["scl"] = f"// ERROR: {instr_type_original} {instr_uid} 'bit' no es variable."
|
||||
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. Formatear CLK (usa memoria original)
|
||||
clk_scl_formatted = clk_scl
|
||||
if clk_scl not in ["TRUE", "FALSE"] and \
|
||||
(' ' in clk_scl or 'AND' in clk_scl or 'OR' in clk_scl or ':=' in clk_scl) and \
|
||||
not (clk_scl.startswith('(') and clk_scl.endswith(')')):
|
||||
clk_scl_formatted = f"({clk_scl})"
|
||||
|
||||
# 4. Generar Lógica SCL del *pulso*
|
||||
result_pulse_expression = "FALSE"
|
||||
scl_comment = ""
|
||||
if instr_type_original == "PBox": # P_TRIG
|
||||
result_pulse_expression = f"{clk_scl_formatted} AND NOT {mem_bit_scl_original}"
|
||||
scl_comment = f"// P_TRIG({clk_scl_formatted})"
|
||||
elif instr_type_original == "NBox": # N_TRIG
|
||||
result_pulse_expression = f"NOT {clk_scl_formatted} AND {mem_bit_scl_original}"
|
||||
scl_comment = f"// N_TRIG({clk_scl_formatted})"
|
||||
# 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
|
||||
|
||||
# 5. Generar la actualización del bit de memoria
|
||||
scl_mem_update = f"{mem_bit_scl_original} := {clk_scl_formatted};"
|
||||
# 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}"
|
||||
|
||||
# 6. Almacenar Resultados
|
||||
# 5. Almacenar Resultados
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
scl_map[map_key_out] = result_pulse_expression # Guardar EXPRESIÓN del pulso
|
||||
sympy_map[map_key_out] = result_pulse_sympy_expr # Guardar EXPRESIÓN SymPy del pulso
|
||||
|
||||
instruction['_edge_mem_update_scl'] = f"{scl_mem_update} {scl_comment}" # Guardar UPDATE + Comentario en campo temporal
|
||||
instruction['scl'] = f"// {instr_type_original} Logic moved to consumer Coil" # Dejar SCL principal vacío/comentario
|
||||
# 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
|
||||
|
||||
# 7. Propagar ENO
|
||||
# 6. Propagar ENO (es la expresión SymPy de CLK)
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = clk_scl
|
||||
sympy_map[map_key_eno] = sympy_clk_expr
|
||||
|
||||
return True
|
||||
|
||||
# --- process_coil MODIFICADA (con \n correcto) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para los detectores de flanco PBox (P_TRIG) y NBox (N_TRIG)."""
|
||||
"""Devuelve la info para los detectores de flanco PBox y NBox."""
|
||||
return [
|
||||
{'type_name': 'pbox', 'processor_func': process_edge_detector, 'priority': 2}, # Flanco positivo
|
||||
{'type_name': 'nbox', 'processor_func': process_edge_detector, 'priority': 2} # Flanco negativo
|
||||
]
|
||||
{'type_name': 'pbox', 'processor_func': process_edge_detector, 'priority': 2},
|
||||
{'type_name': 'nbox', 'processor_func': process_edge_detector, 'priority': 2}
|
||||
]
|
|
@ -1,74 +1,64 @@
|
|||
# processors/process_eq.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_eq(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map)
|
||||
in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if in1_scl is None or in2_scl is None:
|
||||
return False # Dependencias no listas
|
||||
# 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
|
||||
|
||||
# Formatear operandos si son variables
|
||||
op1 = (
|
||||
format_variable_name(in1_scl)
|
||||
if in1_info and in1_info.get("type") == "variable"
|
||||
else in1_scl
|
||||
)
|
||||
op2 = (
|
||||
format_variable_name(in2_scl)
|
||||
if in2_info and in2_info.get("type") == "variable"
|
||||
else in2_scl
|
||||
)
|
||||
# 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
|
||||
|
||||
# Añadir paréntesis si los operandos contienen espacios (poco probable después de formatear)
|
||||
op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1
|
||||
op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2
|
||||
# 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
|
||||
|
||||
comparison_scl = f"{op1} = {op2}"
|
||||
|
||||
# Guardar el resultado booleano de la comparación en el mapa SCL para la salida 'out'
|
||||
# Guardar resultado (Expresión SymPy booleana) en el mapa para 'out'
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
scl_map[map_key_out] = comparison_scl
|
||||
sympy_map[map_key_out] = comparison_expr
|
||||
|
||||
# Procesar la entrada 'pre' (RLO anterior) para determinar ENO
|
||||
pre_input = instruction["inputs"].get("pre")
|
||||
pre_scl = (
|
||||
"TRUE"
|
||||
if pre_input is None
|
||||
else get_scl_representation(pre_input, network_id, scl_map, access_map)
|
||||
)
|
||||
if pre_scl is None:
|
||||
return False # Dependencia 'pre' no lista
|
||||
|
||||
# Guardar el estado de 'pre' como ENO
|
||||
# Guardar el RLO de entrada ('pre') como ENO en el mapa SymPy
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = pre_scl
|
||||
sympy_map[map_key_eno] = sympy_pre_rlo
|
||||
|
||||
# El SCL generado es solo un comentario indicando la condición,
|
||||
# ya que la lógica se propaga a través de scl_map['out']
|
||||
instruction["scl"] = f"// Comparison Eq {instr_uid}: {comparison_scl}"
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- 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}
|
||||
return {'type_name': 'eq', 'processor_func': process_eq, 'priority': 2}
|
|
@ -1,82 +1,90 @@
|
|||
# processors/process_math.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_math(instruction, network_id, scl_map, access_map, data):
|
||||
def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera SCL para operaciones matemáticas (SUB, MUL, DIV).
|
||||
Genera SCL para operaciones matemáticas (SUB, MUL, DIV), simplificando EN.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type = instruction["type"] # SUB, MUL, DIV
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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
|
||||
# Mapa de tipos a operadores SCL string
|
||||
op_map = {"SUB": "-", "MUL": "*", "DIV": "/"}
|
||||
scl_operator = op_map.get(instr_type)
|
||||
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}"
|
||||
instruction["type"] += "_error"
|
||||
instruction["scl"] = f"// ERROR: Operación matemática no soportada: {instr_type_original}"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# Obtener EN, IN1, IN2
|
||||
# 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")
|
||||
en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE"
|
||||
in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map)
|
||||
in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if en_scl is None or in1_scl is None or in2_scl is None:
|
||||
return False # Dependencias no listas
|
||||
# Obtener destino SCL
|
||||
target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
|
||||
|
||||
# Obtener destino 'out'
|
||||
target_scl = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
|
||||
if target_scl is None:
|
||||
instruction["scl"] = f"// ERROR: {instr_type} {instr_uid} sin destino 'out'."
|
||||
instruction["type"] += "_error"
|
||||
return 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
|
||||
|
||||
# Formatear operandos si son variables
|
||||
op1 = format_variable_name(in1_scl) if in1_info and in1_info.get("type") == "variable" else in1_scl
|
||||
op2 = format_variable_name(in2_scl) if in2_info and in2_info.get("type") == "variable" else in2_scl
|
||||
# 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 es necesario (especialmente para expresiones)
|
||||
op1 = f"({op1})" if (" " in op1 or "+" in op1 or "-" in op1 or "*" in op1 or "/" in op1) and not op1.startswith("(") else op1
|
||||
op2 = f"({op2})" if (" " in op2 or "+" in op2 or "-" in op2 or "*" in op2 or "/" in op2) and not op2.startswith("(") else op2
|
||||
# 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
|
||||
scl_core = f"{target_scl} := {op1} {scl_operator} {op2};"
|
||||
scl_final = f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
# Generar SCL Core
|
||||
scl_core = f"{target_scl_name} := {op1_scl_formatted} {scl_operator} {op2_scl_formatted};"
|
||||
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# 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)
|
||||
|
||||
# Actualizar mapa SCL
|
||||
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")
|
||||
scl_map[map_key_out] = target_scl
|
||||
sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
return True
|
||||
|
||||
# --- Procesador NOT ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para operaciones matemáticas (excepto ADD, MOD)."""
|
||||
# Esta función maneja SUB, MUL, DIV.
|
||||
"""Devuelve info para SUB, MUL, DIV."""
|
||||
return [
|
||||
{'type_name': 'sub', 'processor_func': process_math, 'priority': 4}, # Resta
|
||||
{'type_name': 'mul', 'processor_func': process_math, 'priority': 4}, # Multiplicación
|
||||
{'type_name': 'div', 'processor_func': process_math, 'priority': 4} # División
|
||||
]
|
||||
{'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}
|
||||
]
|
|
@ -1,76 +1,75 @@
|
|||
# processors/process_mod.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_mod(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
in1_info = instruction["inputs"].get("in1")
|
||||
in2_info = instruction["inputs"].get("in2")
|
||||
in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map)
|
||||
in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if en_scl is None or in1_scl is None or in2_scl is None:
|
||||
# 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
|
||||
|
||||
target_scl = get_target_scl_name(
|
||||
instruction, "out", network_id, default_to_temp=True
|
||||
)
|
||||
if target_scl is None:
|
||||
print(f"Error: Sin destino MOD {instr_uid}")
|
||||
instruction["scl"] = f"// ERROR: Mod {instr_uid} sin destino"
|
||||
instruction["type"] += "_error"
|
||||
return True
|
||||
# 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)
|
||||
|
||||
# Formatear operandos si son variables
|
||||
op1 = (
|
||||
format_variable_name(in1_scl)
|
||||
if in1_info and in1_info.get("type") == "variable"
|
||||
else in1_scl
|
||||
)
|
||||
op2 = (
|
||||
format_variable_name(in2_scl)
|
||||
if in2_info and in2_info.get("type") == "variable"
|
||||
else in2_scl
|
||||
)
|
||||
# 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
|
||||
|
||||
# Añadir paréntesis si es necesario (poco probable tras formatear)
|
||||
op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1
|
||||
op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2
|
||||
# Generar SCL Core
|
||||
scl_core = f"{target_scl_name} := {op1_scl_formatted} MOD {op2_scl_formatted};"
|
||||
|
||||
scl_core = f"{target_scl} := {op1} MOD {op2};"
|
||||
scl_final = (
|
||||
f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
)
|
||||
# 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)
|
||||
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
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")
|
||||
scl_map[map_key_out] = target_scl
|
||||
sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
return True
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
# --- Function code ends ---
|
||||
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}
|
||||
return {'type_name': 'mod', 'processor_func': process_mod, 'priority': 4}
|
|
@ -1,83 +1,74 @@
|
|||
# processors/process_move.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_move(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
en_scl = (
|
||||
get_scl_representation(en_input, network_id, scl_map, access_map)
|
||||
if en_input
|
||||
else "TRUE"
|
||||
)
|
||||
in_info = instruction["inputs"].get("in")
|
||||
in_scl = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if en_scl is None or in_scl is None:
|
||||
# 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
|
||||
|
||||
# Intentar obtener el destino de 'out1' (o 'out' si es el estándar)
|
||||
target_scl = get_target_scl_name(
|
||||
instruction, "out1", network_id, default_to_temp=False
|
||||
)
|
||||
if target_scl is None:
|
||||
target_scl = get_target_scl_name(
|
||||
instruction, "out", network_id, default_to_temp=False
|
||||
)
|
||||
# Convertir la entrada (SymPy o Constante) a SCL string
|
||||
input_scl = sympy_expr_to_scl(input_sympy_or_const, symbol_manager)
|
||||
|
||||
if target_scl is None:
|
||||
# Si no hay destino explícito, podríamos usar una temp si la lógica lo requiere,
|
||||
# pero MOVE generalmente necesita un destino claro. Marcar como advertencia/error.
|
||||
print(
|
||||
f"Advertencia/Error: MOVE {instr_uid} sin destino claro en 'out' o 'out1'. Se requiere destino explícito."
|
||||
)
|
||||
# Decidir si generar error o simplemente no hacer nada. No hacer nada es más seguro.
|
||||
# instruction["scl"] = f"// ERROR: MOVE {instr_uid} sin destino"
|
||||
# instruction["type"] += "_error"
|
||||
# return True
|
||||
return False # No procesar si no hay destino claro
|
||||
# Generar SCL Core
|
||||
scl_core = f"{target_scl_name} := {input_scl};"
|
||||
|
||||
# Formatear entrada si es variable
|
||||
in_scl_formatted = (
|
||||
format_variable_name(in_scl)
|
||||
if in_info and in_info.get("type") == "variable"
|
||||
else in_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)
|
||||
|
||||
scl_core = f"{target_scl} := {in_scl_formatted};"
|
||||
scl_final = (
|
||||
f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core
|
||||
)
|
||||
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
|
||||
|
||||
instruction["scl"] = scl_final
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# Actualizar instrucción y mapa
|
||||
instruction["scl"] = scl_final # SCL final generado
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
|
||||
# Propagar el valor movido a través de scl_map si se usa 'out' o 'out1' como fuente
|
||||
map_key_out = (network_id, instr_uid, "out") # Asumir 'out' como estándar
|
||||
scl_map[map_key_out] = target_scl # El valor es lo que está en el destino
|
||||
map_key_out1 = (network_id, instr_uid, "out1") # Si existe out1
|
||||
scl_map[map_key_out1] = target_scl
|
||||
# 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
|
||||
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
scl_map[map_key_eno] = en_scl
|
||||
return True
|
||||
|
||||
# EN x2_process.py
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para la operación Move."""
|
||||
|
|
|
@ -1,47 +1,54 @@
|
|||
# processors/process_not.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_not(instruction, network_id, scl_map, access_map, data):
|
||||
"""Genera la expresión SCL para la inversión lógica NOT."""
|
||||
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 = instruction["type"] # Not
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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")
|
||||
in_scl = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
|
||||
if in_scl is None:
|
||||
return False # Dependencia no lista
|
||||
# Verificar dependencias
|
||||
if sympy_expr_in is None:
|
||||
# print(f"DEBUG Not {instr_uid}: Dependency not ready")
|
||||
return False
|
||||
|
||||
# Formatear entrada (añadir paréntesis si es complejo)
|
||||
in_scl_formatted = in_scl
|
||||
if (" " in in_scl or "AND" in in_scl or "OR" in in_scl) and not (in_scl.startswith("(") and in_scl.endswith(")")):
|
||||
in_scl_formatted = f"({in_scl})"
|
||||
# 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
|
||||
|
||||
result_scl = f"NOT {in_scl_formatted}"
|
||||
|
||||
# Guardar resultado en mapa para 'out'
|
||||
# Guardar resultado (Expresión SymPy) en el mapa para 'out'
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
scl_map[map_key_out] = result_scl
|
||||
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')
|
||||
|
||||
instruction["scl"] = f"// Logic NOT {instr_uid}: {result_scl}"
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# NOT no tiene EN/ENO explícito en LAD, modifica el RLO
|
||||
return True
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- 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}
|
||||
return {'type_name': 'not', 'processor_func': process_not, 'priority': 1}
|
|
@ -1,80 +1,69 @@
|
|||
# processors/process_o.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_o(instruction, network_id, scl_map, access_map, data):
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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["inputs"] if pin.startswith("in")])
|
||||
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"] += "_error"
|
||||
return True # Procesado con error
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
scl_parts = []
|
||||
sympy_parts = []
|
||||
all_resolved = True
|
||||
for pin in input_pins:
|
||||
in_scl = get_scl_representation(
|
||||
instruction["inputs"][pin], network_id, scl_map, access_map
|
||||
)
|
||||
if in_scl is None:
|
||||
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 del bucle for si una entrada no está lista
|
||||
break # Salir si una dependencia no está lista
|
||||
|
||||
# Formatear término (añadir paréntesis si es necesario)
|
||||
term = in_scl
|
||||
if (" " in term or "AND" in term) and not (
|
||||
term.startswith("(") and term.endswith(")")
|
||||
):
|
||||
term = f"({term})"
|
||||
scl_parts.append(term)
|
||||
# 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 a que todas las entradas estén resueltas
|
||||
return False # Esperar dependencias
|
||||
|
||||
# Construir la expresión OR
|
||||
result_scl = "FALSE" # Valor por defecto si no hay entradas válidas (raro)
|
||||
if scl_parts:
|
||||
result_scl = " OR ".join(scl_parts)
|
||||
# Simplificar si solo hay un término
|
||||
if len(scl_parts) == 1:
|
||||
result_scl = scl_parts[0]
|
||||
# Quitar paréntesis redundantes si solo hay un término y está entre paréntesis
|
||||
if result_scl.startswith("(") and result_scl.endswith(")"):
|
||||
# Comprobar si los paréntesis son necesarios (contienen operadores de menor precedencia)
|
||||
# Simplificación: quitar siempre si solo hay un término. Podría ser incorrecto en casos complejos.
|
||||
# result_scl = result_scl[1:-1] # Comentado por seguridad
|
||||
pass
|
||||
# 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)
|
||||
|
||||
# Actualizar mapa SCL y la instrucción
|
||||
|
||||
# Guardar la expresión SymPy resultante en el mapa para 'out'
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
scl_map[map_key_out] = result_scl
|
||||
instruction["scl"] = (
|
||||
f"// Logic O {instr_uid}: {result_scl}" # Comentario informativo
|
||||
)
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
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
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- 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}
|
||||
return {'type_name': 'o', 'processor_func': process_o, 'priority': 1}
|
|
@ -1,59 +1,74 @@
|
|||
# processors/process_rcoil.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_rcoil(instruction, network_id, scl_map, access_map, data ):
|
||||
"""Genera SCL para Reset Coil (RCoil): IF condition THEN variable := FALSE; END_IF;"""
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
return False # Ya procesado o con error
|
||||
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 (RLO)
|
||||
# Obtener condición de entrada (SymPy expr)
|
||||
in_info = instruction["inputs"].get("in")
|
||||
condition_scl = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
|
||||
# Obtener operando (variable a poner a FALSE)
|
||||
operand_info = instruction["inputs"].get("operand")
|
||||
variable_scl = get_scl_representation(operand_info, network_id, scl_map, access_map)
|
||||
# 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 condition_scl is None or variable_scl is None:
|
||||
return False # Dependencias no listas
|
||||
|
||||
# Verificar que el operando sea una variable
|
||||
if not (operand_info and operand_info.get("type") == "variable"):
|
||||
print(f"Error: RCoil {instr_uid} operando no es variable o falta info (Tipo: {operand_info.get('type')}).")
|
||||
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"] += "_error"
|
||||
return True # Procesado con error
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# Formatear nombre de variable
|
||||
variable_name_formatted = format_variable_name(variable_scl)
|
||||
# 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
|
||||
scl_core = f"{variable_name_formatted} := FALSE;"
|
||||
scl_final = (
|
||||
f"IF {condition_scl} THEN\n {scl_core}\nEND_IF;" if condition_scl != "TRUE" else scl_core
|
||||
)
|
||||
# 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
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# RCoil no genera salida 'out' ni 'eno' significativas para propagar
|
||||
return True
|
||||
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
|
||||
|
||||
# --- Function code ends ---
|
||||
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}
|
||||
return {'type_name': 'rcoil', 'processor_func': process_rcoil, 'priority': 3}
|
|
@ -1,59 +1,74 @@
|
|||
# processors/process_scoil.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_scoil(instruction, network_id, scl_map, access_map, data):
|
||||
"""Genera SCL para Set Coil (SCoil): IF condition THEN variable := TRUE; END_IF;"""
|
||||
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 = instruction["type"]
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
return False # Ya procesado o con error
|
||||
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 (RLO)
|
||||
# Obtener condición de entrada (SymPy expr)
|
||||
in_info = instruction["inputs"].get("in")
|
||||
condition_scl = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
sympy_expr_in = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
|
||||
# Obtener operando (variable a poner a TRUE)
|
||||
operand_info = instruction["inputs"].get("operand")
|
||||
variable_scl = get_scl_representation(operand_info, network_id, scl_map, access_map)
|
||||
# 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 condition_scl is None or variable_scl is None:
|
||||
return False # Dependencias no listas
|
||||
|
||||
# Verificar que el operando sea una variable
|
||||
if not (operand_info and operand_info.get("type") == "variable"):
|
||||
print(f"Error: SCoil {instr_uid} operando no es variable o falta info (Tipo: {operand_info.get('type')}).")
|
||||
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"] += "_error"
|
||||
return True # Procesado con error
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# Formatear nombre de variable
|
||||
variable_name_formatted = format_variable_name(variable_scl)
|
||||
# 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
|
||||
scl_core = f"{variable_name_formatted} := TRUE;"
|
||||
scl_final = (
|
||||
f"IF {condition_scl} THEN\n {scl_core}\nEND_IF;" if condition_scl != "TRUE" else scl_core
|
||||
)
|
||||
# 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
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# SCoil no genera salida 'out' ni 'eno' significativas para propagar
|
||||
return True
|
||||
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
|
||||
|
||||
# --- Function code ends ---
|
||||
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}
|
||||
return {'type_name': 'scoil', 'processor_func': process_scoil, 'priority': 3}
|
|
@ -1,73 +1,77 @@
|
|||
# processors/process_sd.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_sd(instruction, network_id, scl_map, access_map, data):
|
||||
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 = "Sd" # Tipo original LAD
|
||||
if instruction["type"].endswith(SCL_SUFFIX) or "_error" in instruction["type"]:
|
||||
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)
|
||||
# El pin 'r' (reset) no tiene equivalente directo en TON, se ignora aquí.
|
||||
# 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") # Esperando que x1 lo extraiga
|
||||
timer_instance_info = instruction["inputs"].get("timer")
|
||||
|
||||
scl_s = get_scl_representation(s_info, network_id, scl_map, access_map)
|
||||
scl_tv = get_scl_representation(tv_info, network_id, scl_map, access_map)
|
||||
scl_instance_name = get_scl_representation(timer_instance_info, network_id, scl_map, access_map)
|
||||
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)
|
||||
|
||||
if scl_s is None or scl_tv is None:
|
||||
return False # Dependencias no listas
|
||||
# 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
|
||||
|
||||
# 2. Validar y obtener Nombre de Instancia
|
||||
instance_name = None
|
||||
if timer_instance_info and timer_instance_info.get("type") == "variable":
|
||||
instance_name = scl_instance_name
|
||||
elif timer_instance_info:
|
||||
print(f"Advertencia: Pin 'timer' de {instr_type} UID {instr_uid} conectado a algo inesperado: {timer_instance_info.get('type')}")
|
||||
instance_name = f"#TON_INSTANCE_{instr_uid}"
|
||||
else:
|
||||
instance_name = f"#TON_INSTANCE_{instr_uid}"
|
||||
print(f"Advertencia: No se encontró conexión al pin 'timer' para {instr_type} UID {instr_uid}. Usando placeholder '{instance_name}'. ¡Revisar x1.py y XML!")
|
||||
# Formatear nombre de instancia
|
||||
instance_name_scl = format_variable_name(instance_plc_name)
|
||||
|
||||
# 3. Formatear entradas si son variables
|
||||
scl_s_formatted = format_variable_name(scl_s) if s_info and s_info.get("type") == "variable" else scl_s
|
||||
scl_tv_formatted = format_variable_name(scl_tv) if tv_info and tv_info.get("type") == "variable" else scl_tv
|
||||
# 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)
|
||||
|
||||
# 4. Generar la llamada SCL (TON usa IN, PT, Q, ET)
|
||||
scl_call = f"{instance_name}(IN := {scl_s_formatted}, PT := {scl_tv_formatted}); // TODO: Declarar {instance_name} : TON; en VAR_STAT o VAR"
|
||||
# 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;"
|
||||
|
||||
instruction["scl"] = scl_call
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
# Actualizar instrucción
|
||||
instruction["scl"] = scl_call # SCL final generado
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
|
||||
# 5. Actualizar scl_map para las salidas Q y RT (mapeado a ET de TON)
|
||||
# 7. Actualizar sympy_map para las salidas Q y RT
|
||||
map_key_q = (network_id, instr_uid, "q")
|
||||
scl_map[map_key_q] = f"{instance_name}.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")
|
||||
scl_map[map_key_rt] = f"{instance_name}.ET"
|
||||
# ET is TIME, store SCL access string
|
||||
sympy_map[map_key_rt] = f"{instance_name_scl}.ET"
|
||||
|
||||
return True
|
||||
|
||||
# --- NUEVO: Procesador de Agrupación (Refinado) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el temporizador On-Delay (Sd -> TON)."""
|
||||
# Asumiendo que el tipo en el JSON es 'Sd'
|
||||
return {'type_name': 'sd', 'processor_func': process_sd, 'priority': 5}
|
||||
return {'type_name': 'sd', 'processor_func': process_sd, 'priority': 5}
|
|
@ -1,91 +1,112 @@
|
|||
# processors/process_se.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_se(instruction, network_id, scl_map, access_map, data):
|
||||
def process_se(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera SCL para Temporizador de Pulso (Se -> TP).
|
||||
Requiere datos de instancia (DB o STAT/TEMP).
|
||||
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"]
|
||||
instr_type = "Se" # Tipo original LAD
|
||||
if instruction["type"].endswith(SCL_SUFFIX) or "_error" in instruction["type"]:
|
||||
# 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
|
||||
|
||||
# 1. Obtener Inputs: s (start), tv (time value)
|
||||
# El pin 'r' (reset) no tiene equivalente directo en TP, se ignora aquí.
|
||||
s_info = instruction["inputs"].get("s")
|
||||
tv_info = instruction["inputs"].get("tv")
|
||||
timer_instance_info = instruction["inputs"].get("timer") # Esperando que x1 lo extraiga
|
||||
# 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
|
||||
|
||||
scl_s = get_scl_representation(s_info, network_id, scl_map, access_map)
|
||||
scl_tv = get_scl_representation(tv_info, network_id, scl_map, access_map)
|
||||
# Obtenemos el nombre de la variable instancia, crucial!
|
||||
scl_instance_name = get_scl_representation(timer_instance_info, network_id, scl_map, access_map)
|
||||
# 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
|
||||
|
||||
if scl_s is None or scl_tv is None:
|
||||
return False # Dependencias no listas
|
||||
# 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)
|
||||
|
||||
# 2. Validar y obtener Nombre de Instancia
|
||||
instance_name = None
|
||||
if timer_instance_info and timer_instance_info.get("type") == "variable":
|
||||
instance_name = scl_instance_name # Ya debería estar formateado por get_scl_repr
|
||||
elif timer_instance_info: # Si está conectado pero no es variable directa? Raro.
|
||||
print(f"Advertencia: Pin 'timer' de {instr_type} UID {instr_uid} conectado a algo inesperado: {timer_instance_info.get('type')}")
|
||||
instance_name = f"#TP_INSTANCE_{instr_uid}" # Usar placeholder
|
||||
else: # Si no hay pin 'timer' conectado (no debería pasar si x1 funciona)
|
||||
instance_name = f"#TP_INSTANCE_{instr_uid}" # Usar placeholder
|
||||
print(f"Advertencia: No se encontró conexión al pin 'timer' para {instr_type} UID {instr_uid}. Usando placeholder '{instance_name}'. ¡Revisar x1.py y XML!")
|
||||
# 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)
|
||||
|
||||
# 3. Formatear entradas si son variables (aunque get_scl_representation ya debería hacerlo)
|
||||
scl_s_formatted = format_variable_name(scl_s) if s_info and s_info.get("type") == "variable" else scl_s
|
||||
scl_tv_formatted = format_variable_name(scl_tv) if tv_info and tv_info.get("type") == "variable" else scl_tv
|
||||
# 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!")
|
||||
|
||||
# 4. Generar la llamada SCL (TP usa IN, PT, Q, ET)
|
||||
scl_call = f"{instance_name}(IN := {scl_s_formatted}, PT := {scl_tv_formatted}); // TODO: Declarar {instance_name} : TP; en VAR_STAT o VAR"
|
||||
# 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 + SCL_SUFFIX
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX # Marcar como procesado
|
||||
|
||||
# 5. Actualizar scl_map usando los nombres de pin ORIGINALES mapeados si existen
|
||||
output_pin_mapping_reverse = {v: k for k, v in instruction.get("_output_pin_mapping", {}).items()} # Necesitaríamos guardar el mapeo en x1
|
||||
# 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
|
||||
|
||||
q_original_pin = "q" # Default
|
||||
rt_original_pin = "rt" # Default
|
||||
|
||||
# Intentar encontrar los pines originales si x1 guardó el mapeo (MEJORA NECESARIA en x1)
|
||||
# Por ahora, para SdCoil que mapeaba out->q, usaremos 'out' directamente
|
||||
if instruction.get("type") == "Se_scl" and instruction.get("original_type") == "SdCoil": # Necesitamos guardar original_type en x1
|
||||
q_original_pin = "out"
|
||||
# rt no existe en SdCoil
|
||||
|
||||
map_key_q = (network_id, instr_uid, q_original_pin)
|
||||
scl_map[map_key_q] = f"{instance_name}.Q"
|
||||
|
||||
if rt_original_pin: # Solo añadir rt si corresponde
|
||||
map_key_rt = (network_id, instr_uid, rt_original_pin)
|
||||
scl_map[map_key_rt] = f"{instance_name}.ET"
|
||||
# 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
|
||||
|
||||
# --- Procesador para Sd (On-Delay Timer -> TON SCL) ---
|
||||
|
||||
# --- Function code ends ---
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el temporizador de Pulso (Se -> TP) y maneja SdCoil."""
|
||||
# Asumiendo que el tipo en el JSON es 'Se'
|
||||
# Y que SdCoil también se mapea aquí según el análisis previo
|
||||
"""Devuelve la info para Se (-> TP) y SdCoil (-> TON, manejado aquí)."""
|
||||
return [
|
||||
{'type_name': 'se', 'processor_func': process_se, 'priority': 5},
|
||||
{'type_name': 'sdcoil', 'processor_func': process_se, 'priority': 5} # SdCoil también se procesa como TP
|
||||
]
|
||||
# Asegurarse que x1.py mapea SdCoil a este procesador o a uno específico
|
||||
{'type_name': 'sdcoil', 'processor_func': process_se, 'priority': 5}
|
||||
]
|
|
@ -1,70 +1,85 @@
|
|||
# processors/process_timer.py
|
||||
# -*- coding: utf-8 -*-
|
||||
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
|
||||
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"
|
||||
|
||||
# TODO: Import necessary functions from processor_utils
|
||||
# Example: from .processor_utils import get_scl_representation, format_variable_name
|
||||
# Or: import processors.processor_utils as utils
|
||||
|
||||
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||
SCL_SUFFIX = "_scl"
|
||||
|
||||
# --- Function code starts ---
|
||||
def process_timer(instruction, network_id, scl_map, access_map, data):
|
||||
def process_timer(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera SCL para Temporizadores (TON, TOF).
|
||||
Requiere datos de instancia (DB o STAT).
|
||||
Genera SCL para Temporizadores (TON, TOF) directamente.
|
||||
Requiere datos de instancia.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type = instruction["type"] # Será "TON" o "TOF"
|
||||
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
|
||||
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
|
||||
|
||||
# 1. Obtener Inputs
|
||||
in_info = instruction["inputs"].get("IN") # Entrada booleana
|
||||
pt_info = instruction["inputs"].get("PT") # Preset Time (Tipo TIME)
|
||||
scl_in = get_scl_representation(in_info, network_id, scl_map, access_map)
|
||||
scl_pt = get_scl_representation(pt_info, network_id, scl_map, access_map)
|
||||
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
|
||||
|
||||
if scl_in is None or scl_pt is None:
|
||||
return False # Dependencias no listas
|
||||
# 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}'.")
|
||||
|
||||
# 2. Obtener Nombre de Instancia (NECESITA MEJORA EN x1.py)
|
||||
instance_name = instruction.get("instance_db") # Reutilizar campo si x1 lo llena
|
||||
if not instance_name:
|
||||
# Generar placeholder si x1 no extrajo la instancia para Part
|
||||
instance_name = f"#TIMER_INSTANCE_{instr_uid}" # Placeholder para VAR_TEMP o VAR_STAT
|
||||
print(f"Advertencia: No se encontró instancia para {instr_type} UID {instr_uid}. Usando placeholder '{instance_name}'. Ajustar x1.py y declarar en x3.py.")
|
||||
|
||||
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:
|
||||
instance_name = format_variable_name(instance_name) # Limpiar si viene de x1
|
||||
print(f"Error: Could not create symbol for {q_output_scl_access} in {instr_type_original} {instr_uid}")
|
||||
sympy_map[map_key_q] = None
|
||||
|
||||
# 3. Formatear entradas si son variables
|
||||
scl_in_formatted = format_variable_name(scl_in) if in_info and in_info.get("type") == "variable" else scl_in
|
||||
scl_pt_formatted = format_variable_name(scl_pt) if pt_info and pt_info.get("type") == "variable" else scl_pt
|
||||
|
||||
# 4. Generar la llamada SCL
|
||||
# Nota: Las salidas Q y ET se acceden directamente desde la instancia.
|
||||
scl_call = f"{instance_name}(IN := {scl_in_formatted}, PT := {scl_pt_formatted}); // TODO: Declarar {instance_name} : {instr_type}; en VAR_STAT o VAR"
|
||||
|
||||
instruction["scl"] = scl_call
|
||||
instruction["type"] = instr_type + SCL_SUFFIX
|
||||
|
||||
# 5. Actualizar scl_map para las salidas Q y ET
|
||||
map_key_q = (network_id, instr_uid, "Q")
|
||||
scl_map[map_key_q] = f"{instance_name}.Q"
|
||||
map_key_et = (network_id, instr_uid, "ET")
|
||||
scl_map[map_key_et] = f"{instance_name}.ET"
|
||||
# TON/TOF no tienen un pin ENO estándar en LAD/FBD que se mapee directamente
|
||||
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 la información para los temporizadores TON y TOF (si se usan genéricamente)."""
|
||||
# Esta función manejaría tipos TON/TOF si aparecieran directamente en el JSON
|
||||
# y no fueran manejados por process_sd/process_se (que es lo más común desde LAD).
|
||||
# Incluir por si acaso o si la conversión inicial genera TON/TOF directamente.
|
||||
"""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}
|
||||
]
|
||||
]
|
|
@ -1,15 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# /processors/processor_utils.py
|
||||
# Procesador de utilidades para el procesamiento de archivos XML de Simatic
|
||||
# processors/processor_utils.py
|
||||
import re
|
||||
import sympy
|
||||
from .symbol_manager import SymbolManager, extract_plc_variable_name
|
||||
|
||||
# --- Copia aquí las funciones auxiliares ---
|
||||
# get_scl_representation, format_variable_name,
|
||||
# generate_temp_var_name, get_target_scl_name
|
||||
# Asegúrate de que no dependen de variables globales de x2_process.py
|
||||
# (como 'data' o 'network_access_maps' - esas se pasarán como argumentos)
|
||||
SCL_SUFFIX = "_sympy_processed" # <<< AÑADE ESTA LÍNEA
|
||||
|
||||
# Ejemplo de una función (asegúrate de copiar todas las necesarias)
|
||||
def format_variable_name(name):
|
||||
"""Limpia el nombre de la variable para SCL."""
|
||||
if not name:
|
||||
|
@ -25,8 +21,116 @@ def format_variable_name(name):
|
|||
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
||||
return prefix + name
|
||||
|
||||
# --- Helper Functions ---
|
||||
# (get_scl_representation, format_variable_name, generate_temp_var_name, get_target_scl_name - sin cambios)
|
||||
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
|
||||
|
@ -154,56 +258,53 @@ def generate_temp_var_name(network_id, instr_uid, pin_name):
|
|||
# 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, output_pin_name, network_id, default_to_temp=True):
|
||||
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"]
|
||||
output_pin_data = instruction["outputs"].get(output_pin_name)
|
||||
# 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 (
|
||||
output_pin_data
|
||||
and isinstance(output_pin_data, list)
|
||||
and len(output_pin_data) == 1
|
||||
):
|
||||
dest_access = output_pin_data[0]
|
||||
if dest_access.get("type") == "variable":
|
||||
target_scl = dest_access.get("name")
|
||||
if target_scl:
|
||||
target_scl = format_variable_name(target_scl) # Formatear nombre
|
||||
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: Var destino {instr_uid}.{output_pin_name} sin nombre (UID: {dest_access.get('uid')}). {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
||||
)
|
||||
target_scl = (
|
||||
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
||||
if default_to_temp
|
||||
else None
|
||||
)
|
||||
elif dest_access.get("type") == "constant":
|
||||
print(
|
||||
f"Advertencia: Instr {instr_uid} escribe en const UID {dest_access.get('uid')}. {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
||||
)
|
||||
target_scl = (
|
||||
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
||||
if default_to_temp
|
||||
else None
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"Advertencia: Destino {instr_uid}.{output_pin_name} no es var/const: {dest_access.get('type')}. {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
||||
)
|
||||
target_scl = (
|
||||
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
||||
if default_to_temp
|
||||
else None
|
||||
)
|
||||
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:
|
||||
target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
||||
|
||||
# Si target_scl sigue siendo None y no se debe usar temp, devolver None
|
||||
if target_scl is None and not 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
|
||||
|
||||
# Si target_scl es None pero sí se permite temp, generar uno ahora
|
||||
if target_scl is None and default_to_temp:
|
||||
target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
||||
|
||||
return target_scl
|
||||
|
|
|
@ -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
|
400
x1_to_json.py
400
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 <StructuredText>. Es una aproximación y puede no ser perfecta.
|
||||
Reconstruye una cadena SCL a partir de los elementos hijos
|
||||
de un nodo <StructuredText>, 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 <BooleanAttribute Name="HasQuotes">true</BooleanAttribute> 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 <StatementList>."""
|
||||
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 <StatementList> 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
|
||||
|
|
400
x2_process.py
400
x2_process.py
|
@ -7,167 +7,143 @@ 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"
|
||||
# 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
|
||||
# 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, scl_map, access_map, data):
|
||||
def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||
"""
|
||||
Busca instrucciones que generan condiciones (Contact, O, Eq, PBox, etc.) ya procesadas
|
||||
y, si habilitan un grupo (>1) de bloques funcionales (Move, Add, Call, etc.),
|
||||
construye el bloque IF agrupado.
|
||||
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.
|
||||
(Esta es la implementación de la función como la tenías en el archivo original)
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type = instruction["type"]
|
||||
instr_type_original = instr_type.replace("_scl", "").replace("_error", "")
|
||||
instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX, "").replace("_error", "")
|
||||
made_change = False
|
||||
|
||||
# Solo actuar sobre generadores de condición ya procesados (_scl)
|
||||
# y que no sean ellos mismos errores o ya agrupados por otro IF
|
||||
# Check if this instruction *could* generate a condition suitable for grouping
|
||||
# It must have been processed by the new SymPy method
|
||||
if (
|
||||
not instr_type.endswith("_scl")
|
||||
or "_error" in instr_type
|
||||
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 [
|
||||
"Contact",
|
||||
"O",
|
||||
"Eq",
|
||||
"Ne",
|
||||
"Gt",
|
||||
"Lt",
|
||||
"Ge",
|
||||
"Le",
|
||||
"PBox",
|
||||
"And",
|
||||
"Xor",
|
||||
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
|
||||
]
|
||||
): # Añadir más si es necesario
|
||||
):
|
||||
return False
|
||||
|
||||
# Evitar reagrupar si ya se hizo (comprobando si SCL ya es un IF complejo)
|
||||
# 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:
|
||||
# print(f"DEBUG Group: {instr_uid} ya tiene IF complejo, saltando agrupación.")
|
||||
return False
|
||||
# Ignorar comentarios simples que empiezan por IF
|
||||
if current_scl.strip().startswith("//") and "IF" in current_scl:
|
||||
if current_scl.strip().startswith("IF") and "END_IF;" in current_scl and GROUPED_COMMENT not in current_scl:
|
||||
return False
|
||||
|
||||
# Obtener la condición generada por esta instrucción (debería estar en scl_map['out'])
|
||||
# *** Get the SymPy expression for the condition ***
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
condition_scl = scl_map.get(map_key_out)
|
||||
sympy_condition_expr = sympy_map.get(map_key_out)
|
||||
|
||||
# No agrupar para condiciones triviales, no encontradas o ya agrupadas
|
||||
if condition_scl is None or condition_scl in ["TRUE", "FALSE"]:
|
||||
# No SymPy expression found or trivial conditions
|
||||
if sympy_condition_expr is None or sympy_condition_expr in [sympy.true, sympy.false]:
|
||||
return False
|
||||
|
||||
# Encontrar todos los bloques funcionales habilitados DIRECTAMENTE por esta condición
|
||||
grouped_instructions_cores = [] # Lista de SCL 'core' de los consumidores
|
||||
consumer_instr_list = [] # Lista de instrucciones consumidoras
|
||||
network_logic = next(
|
||||
(net["logic"] for net in data["networks"] if net["id"] == network_id), []
|
||||
)
|
||||
if not network_logic:
|
||||
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
|
||||
|
||||
# Identificar los tipos de instrucciones que queremos agrupar (bloques funcionales)
|
||||
groupable_types = [
|
||||
"Move",
|
||||
"Add",
|
||||
"Sub",
|
||||
"Mul",
|
||||
"Div",
|
||||
"Mod",
|
||||
"Convert",
|
||||
"Call_FC",
|
||||
"Call_FB",
|
||||
] # Añadir más si es necesario
|
||||
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"]
|
||||
# Saltar si ya está agrupado por otra condición o es él mismo
|
||||
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", "") # Tipo actual (_scl o no)
|
||||
consumer_type_original = consumer_type.replace("_scl", "").replace("_error", "")
|
||||
consumer_type = consumer_instr.get("type", "") # Current type suffix matters
|
||||
consumer_type_original = consumer_type.replace(SCL_SUFFIX, "").replace("_error", "")
|
||||
|
||||
# ¿Está la entrada 'en' del consumidor conectada a nuestra salida 'out'?
|
||||
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" # Condición viene de 'out' del generador
|
||||
):
|
||||
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
|
||||
|
||||
# ¿Es un tipo de instrucción agrupable y ya procesado (tiene SCL)?
|
||||
if (
|
||||
is_enabled_by_us
|
||||
and consumer_type.endswith("_scl")
|
||||
and consumer_type_original in groupable_types
|
||||
):
|
||||
# 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", "")
|
||||
# Extraer el SCL core (la parte DENTRO del IF o la línea única si no había IF)
|
||||
# Extract core SCL (logic is similar, maybe simpler if SCL is cleaner now)
|
||||
core_scl = None
|
||||
if consumer_scl.strip().startswith("IF"):
|
||||
# Extraer todo entre THEN y END_IF;
|
||||
match = re.search(
|
||||
r"IF\s+.*\s+THEN\s*(.*?)\s*END_IF;",
|
||||
consumer_scl,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if match:
|
||||
core_scl = match.group(1).strip()
|
||||
elif consumer_scl and not consumer_scl.strip().startswith("//"):
|
||||
# Si no es IF y no es solo comentario, es el core
|
||||
core_scl = consumer_scl.strip()
|
||||
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) # Guardar referencia
|
||||
# else: # Debug
|
||||
# print(f"DEBUG Group: Consumidor {consumer_uid} ({consumer_type}) habilitado por {instr_uid} no tenía SCL core extraíble. SCL: '{consumer_scl}'")
|
||||
consumer_instr_list.append(consumer_instr)
|
||||
|
||||
# Si encontramos más de un consumidor agrupable
|
||||
# --- If groupable consumers found ---
|
||||
if len(grouped_instructions_cores) > 1:
|
||||
print(
|
||||
f"INFO: Agrupando {len(grouped_instructions_cores)} instrucciones bajo condición de {instr_type_original} UID {instr_uid} (Cond: {condition_scl})"
|
||||
)
|
||||
print(f"INFO: Agrupando {len(grouped_instructions_cores)} instr. bajo condición de {instr_type_original} UID {instr_uid}")
|
||||
|
||||
# Construir el bloque IF agrupado
|
||||
scl_grouped = [f"IF {condition_scl} THEN"]
|
||||
# *** 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:
|
||||
# Añadir indentación adecuada (2 espacios)
|
||||
indented_core = "\n".join(
|
||||
[f" {line.strip()}" for line in core_line.splitlines()]
|
||||
)
|
||||
scl_grouped.append(indented_core)
|
||||
scl_grouped.append("END_IF;")
|
||||
final_grouped_scl = "\n".join(scl_grouped)
|
||||
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)
|
||||
|
||||
# Sobrescribir 'scl' de la instrucción generadora de condición
|
||||
# Update the generator instruction's SCL
|
||||
instruction["scl"] = final_grouped_scl
|
||||
# Marcar los consumidores como agrupados y limpiar su SCL original
|
||||
# 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 # Marcar como agrupado
|
||||
consumer_instr["grouped"] = True
|
||||
made_change = True
|
||||
# else: # Debug
|
||||
# if len(grouped_instructions_cores) == 1:
|
||||
# print(f"DEBUG Group: Solo 1 consumidor ({consumer_instr_list[0]['instruction_uid']}) para {instr_uid}. No se agrupa.")
|
||||
# elif len(grouped_instructions_cores) == 0 and condition_scl not in ['TRUE', 'FALSE']:
|
||||
# # Solo mostrar si la condición no era trivial
|
||||
# pass # print(f"DEBUG Group: Ningún consumidor agrupable encontrado para {instr_uid} (Cond: {condition_scl}).")
|
||||
|
||||
return made_change
|
||||
|
||||
|
@ -241,55 +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
|
||||
print(f"Cargando JSON desde: {json_filepath}")
|
||||
try:
|
||||
# Cargar datos en una variable local de esta función
|
||||
with open(json_filepath, "r", encoding="utf-8") as f:
|
||||
data = json.load(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 (Obtiene mapa y lista ordenada) ---
|
||||
# --- 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: # O verificar sorted_processors
|
||||
if not processor_map:
|
||||
print("Error crítico: No se cargaron procesadores. Abortando.")
|
||||
return
|
||||
|
||||
# --- Crear mapas de acceso por red ---
|
||||
network_access_maps = {}
|
||||
# (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:
|
||||
|
@ -297,32 +265,38 @@ def process_json_to_scl(json_filepath):
|
|||
current_access_map[dest["uid"]] = dest
|
||||
network_access_maps[net_id] = current_access_map
|
||||
|
||||
# --- Inicializar mapa SCL y bucle ---
|
||||
scl_map = {} # Mapa para resultados SCL intermedios
|
||||
# --- Inicializar mapa SymPy y SymbolManager ---
|
||||
symbol_manager = SymbolManager()
|
||||
sympy_map = {}
|
||||
|
||||
max_passes = 30
|
||||
passes = 0
|
||||
processing_complete = False
|
||||
|
||||
print("\n--- Iniciando Bucle de Procesamiento Iterativo (con prioridad) ---")
|
||||
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
|
||||
made_change_in_group_pass = False
|
||||
print(f"\n--- Pase {passes} ---")
|
||||
num_processed_this_pass = 0
|
||||
num_sympy_processed_this_pass = 0
|
||||
num_grouped_this_pass = 0
|
||||
|
||||
# --- FASE 1: Procesadores Base (Itera según la lista ordenada por prioridad) ---
|
||||
print(f" Fase 1 (Base - Orden por Prioridad):")
|
||||
for processor_info in sorted_processors: # Iterar sobre la lista ordenada por prioridad
|
||||
# --- FASE 1: Procesadores Base (Ignorando STL) ---
|
||||
print(f" Fase 1 (SymPy Base - Orden por Prioridad):")
|
||||
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']
|
||||
# Descomentar para depuración muy detallada:
|
||||
# print(f" Intentando procesar tipo: {current_type_name} (Prio: {processor_info['priority']})")
|
||||
|
||||
# Buscar instrucciones de este tipo en todas las redes
|
||||
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", [])
|
||||
|
||||
|
@ -330,127 +304,127 @@ 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, es un error o ya fue agrupado por otro
|
||||
# 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 el tipo efectivo de la instrucción para la comparación
|
||||
# (Manejo especial para 'Call')
|
||||
# Determinar tipo efectivo (como antes)
|
||||
lookup_key = instr_type_original.lower()
|
||||
effective_type_name = lookup_key
|
||||
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"
|
||||
# Nota: Si es un tipo de bloque desconocido, effective_type_name será '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"
|
||||
|
||||
# Si el tipo efectivo de la instrucción coincide con el tipo que estamos procesando en este ciclo...
|
||||
# Llamar al procesador si coincide el tipo
|
||||
if effective_type_name == current_type_name:
|
||||
try:
|
||||
# Llamar a la función del procesador, pasando 'data'
|
||||
changed = func_to_call(instruction, network_id, scl_map, access_map, data) # Pasar data
|
||||
# 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_processed_this_pass += 1
|
||||
# La función llamada debe añadir _scl o _error al tipo de la instrucción
|
||||
# Descomentar para depuración:
|
||||
# print(f" Procesado: {instr_type_original} UID {instr_uid}")
|
||||
num_sympy_processed_this_pass += 1
|
||||
except Exception as e:
|
||||
print(f"ERROR(Base) al procesar {instr_type_original} UID {instr_uid} con {func_to_call.__name__}: {e}")
|
||||
print(f"ERROR(SymPy Base) al procesar {instr_type_original} UID {instr_uid}: {e}")
|
||||
traceback.print_exc()
|
||||
# Marcar como error para no reintentar
|
||||
instruction["scl"] = f"// ERROR en procesador base: {e}"
|
||||
instruction["scl"] = f"// ERROR en SymPy procesador base: {e}"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
made_change_in_base_pass = True # Considerar error como cambio
|
||||
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: Procesador de Agrupación (Se ejecuta después de toda la Fase 1) ---
|
||||
# Ejecutar solo si hubo cambios en la fase base (o en 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):")
|
||||
print(f" Fase 2 (Agrupación IF con Simplificación):")
|
||||
num_grouped_this_pass = 0
|
||||
for network in data.get("networks", []):
|
||||
network_id = network["id"]
|
||||
access_map = network_access_maps.get(network_id, {})
|
||||
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 ya procesadas que podrían generar condiciones
|
||||
for instruction in network_logic:
|
||||
# Intentar agrupar solo si está procesado (_scl) y no previamente agrupado
|
||||
if instruction["type"].endswith("_scl") and not instruction.get("grouped", False):
|
||||
try:
|
||||
# Llamar a la función de agrupación, pasando 'data'
|
||||
group_changed = process_group_ifs(instruction, network_id, scl_map, access_map, data)
|
||||
if group_changed:
|
||||
made_change_in_group_pass = True
|
||||
num_grouped_this_pass += 1
|
||||
except Exception as e:
|
||||
print(f"ERROR(Group) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}")
|
||||
traceback.print_exc()
|
||||
# No marcamos la instrucción origen como error, solo falló la agrupación
|
||||
try:
|
||||
# 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
|
||||
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 (en redes no STL).")
|
||||
|
||||
|
||||
# --- Comprobar si se completó el procesamiento ---
|
||||
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:
|
||||
print(f"--- Fin Pase {passes}: {num_processed_this_pass} procesados, {num_grouped_this_pass} agrupados. Continuando...")
|
||||
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. Puede haber dependencias no resueltas. ---")
|
||||
print(f"\n--- ADVERTENCIA: Límite de {max_passes} pases alcanzado...")
|
||||
|
||||
# --- FIN BUCLE ITERATIVO ---
|
||||
|
||||
# --- Verificación Final de Instrucciones No Procesadas ---
|
||||
# --- 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'] # Tipos que esperamos no procesar
|
||||
# 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)
|
||||
|
||||
# Comprobar si NO está procesada, NO es error, NO está agrupada Y NO es un tipo ignorado
|
||||
# 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}'"
|
||||
)
|
||||
|
||||
if unprocessed_count > 0:
|
||||
print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones que no fueron procesadas (y no son tipos ignorados):")
|
||||
if unprocessed_details:
|
||||
for detail in unprocessed_details:
|
||||
print(detail)
|
||||
print(">>> Estos tipos podrían necesitar un procesador en el directorio 'processors' o tener dependencias irresolubles.")
|
||||
# else: # No debería pasar si unprocessed_count > 0 y la lógica es correcta
|
||||
# print("...")
|
||||
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 fueron procesadas a SCL, marcadas como error o agrupadas exitosamente.")
|
||||
print("INFO: Todas las instrucciones relevantes (no STL) parecen haber sido procesadas o agrupadas.")
|
||||
|
||||
# --- Guardar JSON Final ---
|
||||
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:
|
||||
# Usamos la 'data' local que hemos estado modificando
|
||||
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) ---
|
||||
# --- Ejecución (sin cambios) ---
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic.")
|
||||
parser.add_argument(
|
||||
|
@ -461,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)
|
||||
|
|
|
@ -1,28 +1,39 @@
|
|||
# x3_generate_scl.py
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
import sys
|
||||
import traceback # Importar traceback para errores
|
||||
|
||||
# --- Helper Functions ---
|
||||
# --- 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"
|
||||
|
||||
def format_variable_name(name):
|
||||
"""Limpia el nombre de la variable quitando comillas y espacios."""
|
||||
if not name:
|
||||
return "_INVALID_NAME_"
|
||||
# Quita comillas dobles iniciales/finales
|
||||
name = name.strip('"')
|
||||
# Reemplaza comillas dobles internas y puntos por guión bajo
|
||||
name = name.replace('"."', '_').replace('.', '_')
|
||||
# Quita comillas restantes (si las hubiera)
|
||||
name = name.replace('"', '')
|
||||
# Asegurarse de que no empiece con número (aunque raro con comillas iniciales)
|
||||
if name and name[0].isdigit():
|
||||
name = "_" + name
|
||||
return name
|
||||
|
||||
# --- 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."""
|
||||
"""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}'")
|
||||
|
@ -34,117 +45,153 @@ def generate_scl(processed_json_filepath, output_scl_filepath):
|
|||
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 = data.get('language', 'LAD') # Lenguaje original
|
||||
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', '')
|
||||
|
||||
# Limpiar nombre del bloque para usarlo en SCL
|
||||
# Usar format_variable_name para el nombre del bloque en SCL
|
||||
scl_block_name = format_variable_name(block_name)
|
||||
print(f"Generando SCL para el bloque: {scl_block_name} (Original: {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() # Para flancos, si se implementan completamente
|
||||
# Usar regex para encontrar variables _temp_... y stat_...
|
||||
temp_pattern = re.compile(r'"?(_temp_[a-zA-Z0-9_]+)"?')
|
||||
stat_pattern = re.compile(r'"?(stat_[a-zA-Z0-9_]+)"?')
|
||||
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', '')
|
||||
if scl_code:
|
||||
# Buscar temporales en el código SCL generado
|
||||
found_temps = temp_pattern.findall(scl_code)
|
||||
for temp_name in found_temps:
|
||||
temp_vars.add(temp_name) # Añadir al set (evita duplicados)
|
||||
# Buscar estáticas (para flancos)
|
||||
found_stats = stat_pattern.findall(scl_code)
|
||||
for stat_name in found_stats:
|
||||
stat_vars.add(stat_name)
|
||||
# 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 '')
|
||||
|
||||
print(f"Variables temporales detectadas: {len(temp_vars)}")
|
||||
# print(f"Variables estáticas detectadas (para flancos): {len(stat_vars)}")
|
||||
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}")
|
||||
if block_comment:
|
||||
scl_output.append(f"// Block Comment: {block_comment}")
|
||||
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"FUNCTION_BLOCK \"{scl_block_name}\"") # Asumir FB por variables Temp/Stat
|
||||
scl_output.append("{ S7_Optimized_Access := 'TRUE' }") # Opcional, común
|
||||
scl_output.append("VERSION : 0.1") # Opcional
|
||||
scl_output.append(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
|
||||
scl_output.append("VAR_INPUT")
|
||||
# Iterar sobre data['interface']['Input'] si existe
|
||||
# for var in data.get('interface', {}).get('Input', []):
|
||||
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
|
||||
scl_output.append("END_VAR")
|
||||
scl_output.append("")
|
||||
# Declaraciones de Interfaz (Implementación básica)
|
||||
interface_sections = ["Input", "Output", "InOut", "Static", "Temp", "Constant", "Return"]
|
||||
interface_data = data.get('interface', {})
|
||||
|
||||
scl_output.append("VAR_OUTPUT")
|
||||
# Iterar sobre data['interface']['Output'] si existe
|
||||
# for var in data.get('interface', {}).get('Output', []):
|
||||
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
|
||||
scl_output.append("END_VAR")
|
||||
scl_output.append("")
|
||||
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
|
||||
|
||||
scl_output.append("VAR_IN_OUT")
|
||||
# Iterar sobre data['interface']['InOut'] si existe
|
||||
# for var in data.get('interface', {}).get('InOut', []):
|
||||
# scl_output.append(f" {format_variable_name(var['name'])} : {var['datatype']};")
|
||||
scl_output.append("END_VAR")
|
||||
scl_output.append("")
|
||||
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
|
||||
|
||||
# Declaraciones Estáticas (para flancos)
|
||||
if stat_vars:
|
||||
scl_output.append("VAR_STAT")
|
||||
# Asumir Bool para flancos, se podría inferir mejor si PBox lo indicara
|
||||
for var_name in sorted(list(stat_vars)):
|
||||
scl_output.append(f" \"{var_name}\" : Bool; // Memory for edge detection")
|
||||
scl_output.append("END_VAR")
|
||||
scl_output.append("")
|
||||
# No declarar VAR_STAT aquí si ya lo hacemos abajo con las detectadas
|
||||
if section_name == "Static" and stat_vars: continue
|
||||
|
||||
# Declaraciones Temporales
|
||||
# Incluir las variables de la sección Temp del JSON original
|
||||
# y las generadas automáticamente (_temp_...)
|
||||
|
||||
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 = data.get('interface', {}).get('Temp', [])
|
||||
interface_temps = interface_data.get('Temp', [])
|
||||
if interface_temps:
|
||||
for var in interface_temps:
|
||||
formatted_name = format_variable_name(var['name'])
|
||||
# Añadir comillas si el nombre original las tenía o si contiene caracteres especiales
|
||||
scl_name = f'"{formatted_name}"' if '"' in var['name'] or '.' in var['name'] else formatted_name
|
||||
scl_output.append(f" {scl_name} : {var['datatype']};")
|
||||
declared_temps.add(scl_name) # Marcar como declarada
|
||||
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:
|
||||
# Intentar inferir tipo (difícil sin más info), por ahora usar Variant o Bool/DInt
|
||||
for var_name in sorted(list(temp_vars)):
|
||||
scl_name = f'"{var_name}"' # Asegurar comillas para _temp_
|
||||
scl_name = format_variable_name(var_name) # #_temp_...
|
||||
if scl_name not in declared_temps:
|
||||
# Inferencia básica de tipo por nombre de pin (muy heurístico)
|
||||
inferred_type = "Variant" # Tipo seguro por defecto
|
||||
if var_name.endswith("_out"): # Salida de bloque lógico/comparación?
|
||||
inferred_type = "Bool"
|
||||
elif var_name.endswith("_out_num"): # Salida de bloque numérico?
|
||||
inferred_type = "DInt" # O Real? O Int? Difícil saber
|
||||
# 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) # Marcar como declarada
|
||||
declared_temps.add(scl_name)
|
||||
scl_output.append("END_VAR")
|
||||
scl_output.append("")
|
||||
|
||||
|
@ -156,75 +203,85 @@ def generate_scl(processed_json_filepath, output_scl_filepath):
|
|||
for i, network in enumerate(data.get('networks', [])):
|
||||
network_title = network.get('title', f'Network {network.get("id")}')
|
||||
network_comment = network.get('comment', '')
|
||||
# Obtener lenguaje original de la red, default a LAD si no está
|
||||
network_lang = network.get('language', 'LAD')
|
||||
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("") # Línea en blanco antes del código de la red
|
||||
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')
|
||||
|
||||
# --- INICIO: Manejar RAW_SCL_CHUNK y otros tipos ---
|
||||
if instruction_type == "RAW_SCL_CHUNK" and scl_code:
|
||||
network_has_code = True
|
||||
# Imprimir el SCL reconstruido directamente
|
||||
for line in scl_code.splitlines():
|
||||
scl_output.append(f" {line}") # Añadir indentación
|
||||
elif instruction_type == "UNSUPPORTED_LANG" and scl_code:
|
||||
network_has_code = True
|
||||
# Imprimir comentario para lenguajes no soportados
|
||||
for line in scl_code.splitlines():
|
||||
scl_output.append(f" {line}")
|
||||
elif instruction_type.endswith("_scl") and scl_code:
|
||||
# Código SCL generado por x2_process.py para LAD/FBD
|
||||
if instruction.get("grouped", False): # Ignorar si fue agrupado
|
||||
# --- 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:
|
||||
|
||||
# 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
|
||||
|
||||
# Lógica mejorada para omitir comentarios internos si no aportan
|
||||
lines = scl_code.splitlines()
|
||||
is_internal_comment_only = (len(lines) == 1 and
|
||||
(lines[0].strip().startswith("// RLO") or
|
||||
lines[0].strip().startswith("// Comparison") or
|
||||
lines[0].strip().startswith("// Logic O") or
|
||||
lines[0].strip().startswith("// Logic included in grouped IF") or # Usar GROUPED_COMMENT si lo importas
|
||||
lines[0].strip().startswith("// P_TRIG") or
|
||||
lines[0].strip().startswith("// N_TRIG") ))
|
||||
# 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())
|
||||
|
||||
if not is_internal_comment_only:
|
||||
|
||||
# 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 lines:
|
||||
clean_line = line.strip()
|
||||
# Omitir comentarios autogenerados específicos
|
||||
if not (clean_line.startswith("// RLO:") or \
|
||||
clean_line.startswith("// Comparison") or \
|
||||
clean_line.startswith("// Logic O") or \
|
||||
clean_line == "// Logic included in grouped IF" or \
|
||||
clean_line.startswith("// P_TRIG(") or \
|
||||
clean_line.startswith("// N_TRIG(")):
|
||||
scl_output.append(f" {line}") # Indentación
|
||||
# Ignorar instrucciones no procesadas (sin _scl) o con error
|
||||
# elif "_error" in instruction_type:
|
||||
# scl_output.append(f" // Error processing instruction {instruction.get('instruction_uid')}: {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')}")
|
||||
|
||||
|
||||
# --- FIN: Manejar RAW_SCL_CHUNK y otros tipos ---
|
||||
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("") # Añadir línea en blanco después del código de la red si hubo algo
|
||||
else:
|
||||
# Añadir comentario si la red estaba vacía o no generó código imprimible
|
||||
scl_output.append(f" // Network did not produce printable SCL code.")
|
||||
scl_output.append("")
|
||||
|
||||
scl_output.append("END_FUNCTION_BLOCK")
|
||||
# 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}")
|
||||
|
@ -235,16 +292,12 @@ def generate_scl(processed_json_filepath, output_scl_filepath):
|
|||
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__":
|
||||
# Imports necesarios solo para la ejecución como script principal
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description="Generate final SCL file from processed JSON.")
|
||||
# Acepta el nombre del XML original como referencia para derivar nombres
|
||||
parser = argparse.ArgumentParser(description="Generate final SCL file from processed JSON (SymPy version).")
|
||||
parser.add_argument(
|
||||
"source_xml_filepath",
|
||||
nargs="?",
|
||||
|
@ -253,20 +306,17 @@ if __name__ == "__main__":
|
|||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Derivar nombres de archivos de entrada y salida
|
||||
xml_filename_base = os.path.splitext(os.path.basename(args.source_xml_filepath))[0]
|
||||
input_dir = os.path.dirname(args.source_xml_filepath) # Directorio del XML original
|
||||
# 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__)
|
||||
|
||||
# Nombre del JSON procesado (entrada para este script)
|
||||
input_json_file = os.path.join(input_dir, f"{xml_filename_base}_simplified_processed.json")
|
||||
# Nombre del SCL final (salida de este script)
|
||||
output_scl_file = os.path.join(input_dir, f"{xml_filename_base}_simplified_processed.scl")
|
||||
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")
|
||||
|
||||
# Verificar si el archivo JSON procesado de entrada existe
|
||||
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) # Salir si el archivo de entrada no existe
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Llamar a la función principal con los nombres derivados
|
||||
generate_scl(input_json_file, output_scl_file)
|
Loading…
Reference in New Issue