Usando Sympy para simplificar los parentesis en las expresiones logicas
This commit is contained in:
parent
98dbc7a5d7
commit
9b192a91fd
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
|
||||
|
|
|
@ -251,17 +251,17 @@
|
|||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "37",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "34",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "37",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
@ -326,18 +326,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mHVM302_Dly\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "25",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mHVM302_Dly\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "23",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -414,18 +414,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetTotalizerTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "24",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetTotalizerTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "23",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -501,17 +501,17 @@
|
|||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
@ -525,18 +525,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTN301TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTN301TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -636,17 +636,17 @@
|
|||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
@ -660,18 +660,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTP302TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "29",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTP302TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -795,17 +795,17 @@
|
|||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
@ -819,18 +819,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTM303TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTM303TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -930,17 +930,17 @@
|
|||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
@ -954,18 +954,18 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetProductTotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetProductTotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -1157,12 +1157,6 @@
|
|||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "27",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"T_Pulse_Recipe_Edit\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "28",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -1170,6 +1164,12 @@
|
|||
"datatype": "TypedConstant",
|
||||
"value": "S5T#500ms"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "27",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"T_Pulse_Recipe_Edit\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
{
|
||||
"instruction_uid": "30",
|
||||
"uid": "30",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -67,12 +67,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gSyrupRoomEn\""
|
||||
"scl": "// SymPy Contact: v0_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "31",
|
||||
"uid": "31",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {
|
||||
"operand": true
|
||||
|
@ -94,12 +94,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")"
|
||||
"scl": "// SymPy Contact: v0_ & ~v1_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "32",
|
||||
"uid": "32",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {
|
||||
"operand": true
|
||||
|
@ -121,12 +121,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: (\"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")) AND (NOT \"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\")"
|
||||
"scl": "// SymPy Contact: v0_ & ~v1_ & ~v2_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "33",
|
||||
"uid": "33",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -146,12 +146,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: (\"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")) AND (NOT \"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\") AND \"Procedure_Variables\".\"FTP302Line_Preparation\".\"Done\""
|
||||
"scl": "// SymPy Contact: v0_ & v3_ & ~v1_ & ~v2_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "34",
|
||||
"uid": "34",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {
|
||||
"operand": true
|
||||
|
@ -173,12 +173,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: ((\"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\")"
|
||||
"scl": "// SymPy Contact: v0_ & v3_ & ~v1_ & ~v2_ & ~v4_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "35",
|
||||
"uid": "35",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -198,12 +198,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: (\"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")) AND \"gBlenderCIPMode\""
|
||||
"scl": "// SymPy Contact: v0_ & v5_ & ~v1_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "36",
|
||||
"uid": "36",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -223,12 +223,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: ((\"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")) AND \"gBlenderCIPMode\") AND \"gIN_CIP_CIPRunning\""
|
||||
"scl": "// SymPy Contact: v0_ & v5_ & v6_ & ~v1_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "37",
|
||||
"uid": "37",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -248,39 +248,39 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: (((\"gSyrupRoomEn\" AND (NOT \"gIN_HVP301_Aux\")) AND \"gBlenderCIPMode\") AND \"gIN_CIP_CIPRunning\") AND \"Procedure_Variables\".\"Blender_Run\".\"Running\""
|
||||
"scl": "// SymPy Contact: v0_ & v5_ & v6_ & v7_ & ~v1_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "38",
|
||||
"uid": "38",
|
||||
"type": "O_scl",
|
||||
"type": "O_sympy_processed",
|
||||
"template_values": {
|
||||
"Card": "Cardinality"
|
||||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "37",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "34",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "37",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// Logic O 38: ((\"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\")"
|
||||
"scl": "// SymPy O: (v0_ & v5_ & v6_ & v7_ & ~v1_) | (v0_ & v3_ & ~v1_ & ~v2_ & ~v4_)"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "39",
|
||||
"uid": "39",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -298,7 +298,7 @@
|
|||
}
|
||||
},
|
||||
"outputs": {},
|
||||
"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\");"
|
||||
"scl": "\"gHVP301_Open\" := (\"gSyrupRoomEn\" AND \"gBlenderCIPMode\" AND \"gIN_CIP_CIPRunning\" AND \"Procedure_Variables\".\"Blender_Run\".\"Running\" AND NOT \"gIN_HVP301_Aux\") OR (\"gSyrupRoomEn\" AND \"Procedure_Variables\".\"FTP302Line_Preparation\".\"Done\" AND NOT \"gIN_HVP301_Aux\" AND NOT \"HMI_Blender_Parameters\".\"Processor_Options\".\"Blender_OPT\".\"_FastChangeOverEnabled\" AND NOT \"Procedure_Variables\".\"Syr_RunOut\".\"Done\");"
|
||||
}
|
||||
],
|
||||
"language": "LAD"
|
||||
|
@ -311,7 +311,7 @@
|
|||
{
|
||||
"instruction_uid": "25",
|
||||
"uid": "25",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -328,27 +328,27 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gIN_HVM302_Aux\""
|
||||
"scl": "// SymPy Contact: v8_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "26",
|
||||
"uid": "26",
|
||||
"type": "Sd_scl",
|
||||
"type": "Sd_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mHVM302_Dly\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "25",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mHVM302_Dly\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "23",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -366,12 +366,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"mHVM302_Dly\"(IN := \"gIN_HVM302_Aux\", PT := S5T#1S); // TODO: Declarar \"mHVM302_Dly\" : TON; en VAR_STAT o VAR"
|
||||
"scl": "\"mHVM302_Dly\"(IN := \"gIN_HVM302_Aux\", PT := S5T#1S); // TODO: Declarar \"mHVM302_Dly\" : TON;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "27",
|
||||
"uid": "27",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -402,7 +402,7 @@
|
|||
{
|
||||
"instruction_uid": "24",
|
||||
"uid": "24",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -419,27 +419,27 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gBlendResetTotalizer\""
|
||||
"scl": "// SymPy Contact: v9_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "25",
|
||||
"uid": "25",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetTotalizerTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "24",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "22",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetTotalizerTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "23",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -455,7 +455,7 @@
|
|||
}
|
||||
},
|
||||
"outputs": {},
|
||||
"scl": "\"mResetTotalizerTmr\"(IN := \"gBlendResetTotalizer\", PT := S5T#2S); // TODO: Declarar \"mResetTotalizerTmr\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"mResetTotalizerTmr\"(IN := \"gBlendResetTotalizer\", PT := S5T#2S); // TODO: Declarar \"mResetTotalizerTmr\" : TP;"
|
||||
}
|
||||
],
|
||||
"language": "LAD"
|
||||
|
@ -468,7 +468,7 @@
|
|||
{
|
||||
"instruction_uid": "26",
|
||||
"uid": "26",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -485,12 +485,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gFTN301_ResetTot\""
|
||||
"scl": "// SymPy Contact: v10_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "27",
|
||||
"uid": "27",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -507,54 +507,54 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy Contact: v11_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "28",
|
||||
"uid": "28",
|
||||
"type": "O_scl",
|
||||
"type": "O_sympy_processed",
|
||||
"template_values": {
|
||||
"Card": "Cardinality"
|
||||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// Logic O 28: \"gFTN301_ResetTot\" OR \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy O: v10_ | v11_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "29",
|
||||
"uid": "29",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTN301TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTN301TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -572,12 +572,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"mResetFTN301TotTmr\"(IN := \"gFTN301_ResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetFTN301TotTmr\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"mResetFTN301TotTmr\"(IN := \"gFTN301_ResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetFTN301TotTmr\" : TP;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "30",
|
||||
"uid": "30",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -608,7 +608,7 @@
|
|||
{
|
||||
"instruction_uid": "27",
|
||||
"uid": "27",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -625,12 +625,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gFTP302_ResetTot\""
|
||||
"scl": "// SymPy Contact: v12_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "28",
|
||||
"uid": "28",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -647,54 +647,54 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy Contact: v11_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "29",
|
||||
"uid": "29",
|
||||
"type": "O_scl",
|
||||
"type": "O_sympy_processed",
|
||||
"template_values": {
|
||||
"Card": "Cardinality"
|
||||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// Logic O 29: \"gFTP302_ResetTot\" OR \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy O: v11_ | v12_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "30",
|
||||
"uid": "30",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTP302TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "29",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTP302TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -712,12 +712,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"mResetFTP302TotTmr\"(IN := \"gFTP302_ResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetFTP302TotTmr\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"mResetFTP302TotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gFTP302_ResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetFTP302TotTmr\" : TP;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "31",
|
||||
"uid": "31",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -737,12 +737,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mResetFTP302TotTmr\".Q AND \"gSyrupRoomEn\""
|
||||
"scl": "// SymPy Contact: v0_ & v23_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "32",
|
||||
"uid": "32",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -760,7 +760,7 @@
|
|||
}
|
||||
},
|
||||
"outputs": {},
|
||||
"scl": "\"mResetSyrupTot\" := \"mResetFTP302TotTmr\".Q AND \"gSyrupRoomEn\";"
|
||||
"scl": "\"mResetSyrupTot\" := \"gSyrupRoomEn\" AND \"mResetFTP302TotTmr\".Q;"
|
||||
}
|
||||
],
|
||||
"language": "LAD"
|
||||
|
@ -773,7 +773,7 @@
|
|||
{
|
||||
"instruction_uid": "26",
|
||||
"uid": "26",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -790,12 +790,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gFTM303_ResetTot\""
|
||||
"scl": "// SymPy Contact: v13_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "27",
|
||||
"uid": "27",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -812,54 +812,54 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy Contact: v11_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "28",
|
||||
"uid": "28",
|
||||
"type": "O_scl",
|
||||
"type": "O_sympy_processed",
|
||||
"template_values": {
|
||||
"Card": "Cardinality"
|
||||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// Logic O 28: \"gFTM303_ResetTot\" OR \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy O: v11_ | v13_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "29",
|
||||
"uid": "29",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTM303TotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetFTM303TotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -877,12 +877,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"mResetFTM303TotTmr\"(IN := \"gFTM303_ResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetFTM303TotTmr\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"mResetFTM303TotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gFTM303_ResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetFTM303TotTmr\" : TP;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "30",
|
||||
"uid": "30",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -913,7 +913,7 @@
|
|||
{
|
||||
"instruction_uid": "26",
|
||||
"uid": "26",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -930,12 +930,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"gProductMFMResetTot\""
|
||||
"scl": "// SymPy Contact: v14_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "27",
|
||||
"uid": "27",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -952,54 +952,54 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy Contact: v11_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "28",
|
||||
"uid": "28",
|
||||
"type": "O_scl",
|
||||
"type": "O_sympy_processed",
|
||||
"template_values": {
|
||||
"Card": "Cardinality"
|
||||
},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in1": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "26",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"in2": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
"source_instruction_uid": "27",
|
||||
"source_pin": "out"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// Logic O 28: \"gProductMFMResetTot\" OR \"mResetTotalizerTmr\""
|
||||
"scl": "// SymPy O: v11_ | v14_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "29",
|
||||
"uid": "29",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetProductTotTmr\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "O",
|
||||
"source_instruction_uid": "28",
|
||||
"source_pin": "out"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "23",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"mResetProductTotTmr\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "24",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -1017,12 +1017,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"mResetProductTotTmr\"(IN := \"gProductMFMResetTot\" OR \"mResetTotalizerTmr\", PT := S5T#2S); // TODO: Declarar \"mResetProductTotTmr\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"mResetProductTotTmr\"(IN := \"mResetTotalizerTmr\" OR \"gProductMFMResetTot\", PT := S5T#2S); // TODO: Declarar \"mResetProductTotTmr\" : TP;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "30",
|
||||
"uid": "30",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1053,7 +1053,7 @@
|
|||
{
|
||||
"instruction_uid": "33",
|
||||
"uid": "33",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1070,12 +1070,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
|
||||
"scl": "// SymPy Contact: v15_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "34",
|
||||
"uid": "34",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {
|
||||
"operand": true
|
||||
|
@ -1097,12 +1097,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND (NOT \"mFP_Recip_Main_Page\")"
|
||||
"scl": "// SymPy Contact: v15_ & ~v16_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "35",
|
||||
"uid": "35",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1120,12 +1120,12 @@
|
|||
}
|
||||
},
|
||||
"outputs": {},
|
||||
"scl": "\"mAux_FP_M700_1\" := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND (NOT \"mFP_Recip_Main_Page\");"
|
||||
"scl": "\"mAux_FP_M700_1\" := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND NOT \"mFP_Recip_Main_Page\";"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "36",
|
||||
"uid": "36",
|
||||
"type": "Coil_scl",
|
||||
"type": "Coil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1148,7 +1148,7 @@
|
|||
{
|
||||
"instruction_uid": "37",
|
||||
"uid": "37",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1165,12 +1165,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\""
|
||||
"scl": "// SymPy Contact: v15_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "38",
|
||||
"uid": "38",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1190,21 +1190,15 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\""
|
||||
"scl": "// SymPy Contact: v15_ & v17_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "39",
|
||||
"uid": "39",
|
||||
"type": "Se_scl",
|
||||
"type": "Se_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
"timer": {
|
||||
"uid": "27",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"T_Pulse_Recipe_Edit\""
|
||||
},
|
||||
"tv": {
|
||||
"uid": "28",
|
||||
"scope": "TypedConstant",
|
||||
|
@ -1212,6 +1206,12 @@
|
|||
"datatype": "TypedConstant",
|
||||
"value": "S5T#500ms"
|
||||
},
|
||||
"timer": {
|
||||
"uid": "27",
|
||||
"scope": "GlobalVariable",
|
||||
"type": "variable",
|
||||
"name": "\"T_Pulse_Recipe_Edit\""
|
||||
},
|
||||
"s": {
|
||||
"type": "connection",
|
||||
"source_instruction_type": "Contact",
|
||||
|
@ -1228,12 +1228,12 @@
|
|||
"outputs": {
|
||||
"q": []
|
||||
},
|
||||
"scl": "\"T_Pulse_Recipe_Edit\"(IN := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\", PT := S5T#500ms); // TODO: Declarar \"T_Pulse_Recipe_Edit\" : TP; en VAR_STAT o VAR"
|
||||
"scl": "\"T_Pulse_Recipe_Edit\"(IN := \"HMI_Variables_Cmd\".\"Recipe\".\"Main_Page\" AND \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\", PT := S5T#500ms); // TODO: Declarar \"T_Pulse_Recipe_Edit\" : TP;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "40",
|
||||
"uid": "40",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1253,12 +1253,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"T_Pulse_Recipe_Edit\".Q AND \"T_Pulse_Recipe_Edit\""
|
||||
"scl": "// SymPy Contact: v18_ & v26_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "41",
|
||||
"uid": "41",
|
||||
"type": "RCoil_scl",
|
||||
"type": "RCoil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1276,12 +1276,12 @@
|
|||
}
|
||||
},
|
||||
"outputs": {},
|
||||
"scl": "IF \"T_Pulse_Recipe_Edit\".Q AND \"T_Pulse_Recipe_Edit\" THEN\n \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\" := FALSE;\nEND_IF;"
|
||||
"scl": "IF \"T_Pulse_Recipe_Edit\" AND \"T_Pulse_Recipe_Edit\".Q THEN\n \"HMI_Variables_Cmd\".\"Recipe\".\"Edit\" := FALSE;\nEND_IF;"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "42",
|
||||
"uid": "42",
|
||||
"type": "Contact_scl",
|
||||
"type": "Contact_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
@ -1298,12 +1298,12 @@
|
|||
"outputs": {
|
||||
"out": []
|
||||
},
|
||||
"scl": "// RLO: \"mAux_FP_M700_1\""
|
||||
"scl": "// SymPy Contact: v19_"
|
||||
},
|
||||
{
|
||||
"instruction_uid": "43",
|
||||
"uid": "43",
|
||||
"type": "SCoil_scl",
|
||||
"type": "SCoil_sympy_processed",
|
||||
"template_values": {},
|
||||
"negated_pins": {},
|
||||
"inputs": {
|
||||
|
|
|
@ -6,13 +6,8 @@ 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 : Void;
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
|
@ -29,43 +24,43 @@ BEGIN
|
|||
|
||||
// Network 1: 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 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
|
||||
"mHVM302_Dly"(IN := "gIN_HVM302_Aux", PT := S5T#1S); // TODO: Declarar "mHVM302_Dly" : TON;
|
||||
"gHVM302_Open" := "mHVM302_Dly".Q;
|
||||
|
||||
// Network 3: ResetTotalizer (Original Language: LAD)
|
||||
|
||||
"mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetTotalizerTmr"(IN := "gBlendResetTotalizer", PT := S5T#2S); // TODO: Declarar "mResetTotalizerTmr" : TP;
|
||||
|
||||
// Network 4: ResetWaterTot (Original Language: LAD)
|
||||
|
||||
"mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP; en VAR_STAT o VAR
|
||||
"mResetFTN301TotTmr"(IN := "gFTN301_ResetTot" OR "mResetTotalizerTmr", PT := S5T#2S); // TODO: Declarar "mResetFTN301TotTmr" : TP;
|
||||
"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";
|
||||
"mResetFTP302TotTmr"(IN := "mResetTotalizerTmr" OR "gFTP302_ResetTot", PT := S5T#2S); // TODO: Declarar "mResetFTP302TotTmr" : TP;
|
||||
"mResetSyrupTot" := "gSyrupRoomEn" AND "mResetFTP302TotTmr".Q;
|
||||
|
||||
// Network 6: ResetProductTot (Original Language: LAD)
|
||||
|
||||
"mResetFTM303TotTmr"(IN := "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 7: 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 8: 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
|
||||
|
|
|
@ -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
|
353
x2_process.py
353
x2_process.py
|
@ -7,167 +7,141 @@ 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
|
||||
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.
|
||||
"""
|
||||
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
|
||||
|
||||
|
@ -254,30 +228,20 @@ def process_json_to_scl(json_filepath):
|
|||
# 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
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"Error al cargar JSON: {e}")
|
||||
traceback.print_exc()
|
||||
return
|
||||
with open(json_filepath, "r", encoding="utf-8") as f: data = json.load(f)
|
||||
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) ---
|
||||
script_dir = os.path.dirname(__file__)
|
||||
processors_dir_path = os.path.join(script_dir, 'processors')
|
||||
# --- 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
|
||||
|
||||
if not processor_map: # O verificar sorted_processors
|
||||
print("Error crítico: No se cargaron procesadores. Abortando.")
|
||||
return
|
||||
|
||||
# --- Crear mapas de acceso por red ---
|
||||
# --- 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 = {}
|
||||
|
@ -297,30 +261,35 @@ 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 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 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_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
|
||||
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 (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 (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']
|
||||
# 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"]
|
||||
access_map = network_access_maps.get(network_id, {})
|
||||
|
@ -330,82 +299,84 @@ 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
|
||||
if (instr_type_original.endswith(SCL_SUFFIX)
|
||||
# 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 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'
|
||||
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"
|
||||
|
||||
# Si el tipo efectivo de la instrucción coincide con el tipo que estamos procesando en este ciclo...
|
||||
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
|
||||
# *** 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_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
|
||||
print(f" -> {num_sympy_processed_this_pass} instrucciones 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 (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):")
|
||||
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, {})
|
||||
# access_map = network_access_maps.get(network_id, {}) # No usado directamente por group_ifs
|
||||
network_logic = network.get("logic", [])
|
||||
# Iterar sobre instrucciones ya procesadas que podrían generar condiciones
|
||||
# Iterar sobre instrucciones que *pueden* generar condiciones booleanas
|
||||
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
|
||||
# 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:
|
||||
print(f"--- Fin Pase {passes}: {num_processed_this_pass} procesados, {num_grouped_this_pass} agrupados. Continuando...")
|
||||
# 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. 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 ---
|
||||
# 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'] # Tipos que esperamos no procesar
|
||||
ignored_types = ['raw_scl_chunk', 'unsupported_lang']
|
||||
|
||||
for network in data.get("networks", []):
|
||||
network_id = network.get("id", "Unknown ID")
|
||||
|
@ -414,8 +385,11 @@ def process_json_to_scl(json_filepath):
|
|||
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("//")
|
||||
|
||||
# Comprobar si NO está procesada, NO es error, NO está agrupada Y NO es un tipo ignorado
|
||||
# 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
|
||||
|
@ -425,24 +399,21 @@ def process_json_to_scl(json_filepath):
|
|||
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 (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 que no fueron 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 parecen haber sido procesadas o agrupadas.")
|
||||
|
||||
# --- Guardar JSON Final ---
|
||||
# --- 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:
|
||||
# Usamos la 'data' local que hemos estado modificando
|
||||
json.dump(data, f, indent=4, ensure_ascii=False)
|
||||
print("Guardado completado.")
|
||||
except Exception as e:
|
||||
|
|
|
@ -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,61 @@ 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')
|
||||
scl_code = instruction.get('scl', "") # Obtener SCL generado por x2
|
||||
|
||||
# --- 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
|
||||
continue
|
||||
# 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 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
|
||||
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:
|
||||
# scl_output.append(f" // Error processing instruction {instruction.get('instruction_uid')}: {scl_code}")
|
||||
# network_has_code = True
|
||||
# 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("") # Añadir línea en blanco después del código de la red si hubo algo
|
||||
scl_output.append("") # Línea en blanco después del código de la red
|
||||
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 +268,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 +282,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