ParamManagerScripts/backend/script_groups/TwinCat/simple_lad_converter.py

268 lines
9.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Convertidor simplificado de LAD TwinCAT a pseudocódigo estructurado
"""
import re
class SimpleLadConverter:
"""Convertidor simplificado de LAD a código estructurado"""
def __init__(self):
self.networks = []
self.current_network_id = 0
def parse_file(self, file_path):
"""Parse el archivo LAD"""
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Encontrar sección LAD
lad_start = content.find('_LD_BODY')
if lad_start == -1:
print("No se encontró _LD_BODY")
return
# Extraer contenido LAD
lines = content[lad_start:].split('\n')
self._parse_networks(lines)
def _parse_networks(self, lines):
"""Parse todas las redes"""
i = 0
while i < len(lines):
if lines[i].strip() == '_NETWORK':
self.current_network_id += 1
i = self._parse_network(lines, i)
else:
i += 1
def _parse_network(self, lines, start_idx):
"""Parse una red individual"""
network = {
'id': self.current_network_id,
'comment': '',
'contacts': [],
'assignments': [],
'outputs': []
}
i = start_idx + 1
# Parse comentario
if i < len(lines) and lines[i].strip() == '_COMMENT':
i, comment = self._parse_comment(lines, i)
network['comment'] = comment
# Parse contenido de la red
current_assignment = {
'conditions': [],
'target': '',
'expression': '',
'function_block': '',
'operator': '',
'operands': []
}
while i < len(lines):
line = lines[i].strip()
if line == '_NETWORK':
break
elif line == '_LD_ASSIGN':
if current_assignment['target'] or current_assignment['function_block']:
network['assignments'].append(current_assignment.copy())
current_assignment = {
'conditions': [],
'target': '',
'expression': '',
'function_block': '',
'operator': '',
'operands': []
}
i += 1
elif line == '_LD_CONTACT':
i += 1
if i < len(lines):
contact_name = lines[i].strip()
i += 1
negated = False
if i < len(lines) and lines[i].strip() == '_NEGATIV':
negated = True
i += 1
current_assignment['conditions'].append({
'name': contact_name,
'negated': negated
})
elif line.startswith('_FUNCTIONBLOCK'):
i += 1
if i < len(lines):
current_assignment['function_block'] = lines[i].strip()
i += 1
elif line.startswith('_OPERATOR'):
i += 1
# Buscar operador y operandos
while i < len(lines) and not lines[i].strip().startswith('_OUTPUT'):
subline = lines[i].strip()
if subline in ['ADD', 'SUB', 'MUL', 'DIV', 'AND', 'OR', 'LT', 'GT', 'EQ', 'SEL', 'MOVE']:
current_assignment['operator'] = subline
elif subline.startswith('_OPERAND'):
i += 2 # Saltar _EXPRESSION y _POSITIV
if i < len(lines):
operand = lines[i].strip()
current_assignment['operands'].append(operand)
i += 1
continue
elif line.startswith('_OUTPUT'):
# Buscar variable de salida
i += 1
while i < len(lines) and lines[i].strip().startswith('_'):
i += 1
if i < len(lines):
current_assignment['target'] = lines[i].strip()
i += 1
elif not line.startswith('_') and line and 'ENABLELIST' not in line:
if not current_assignment['expression']:
current_assignment['expression'] = line
i += 1
else:
i += 1
# Agregar última asignación si existe
if current_assignment['target'] or current_assignment['function_block']:
network['assignments'].append(current_assignment)
self.networks.append(network)
return i
def _parse_comment(self, lines, start_idx):
"""Parse comentario"""
i = start_idx + 1
comment_lines = []
while i < len(lines):
line = lines[i].strip()
if line == '_END_COMMENT':
break
if line and not line.startswith('_'):
comment_lines.append(line)
i += 1
return i + 1, ' '.join(comment_lines)
def convert_to_structured(self):
"""Convertir a código estructurado"""
output = []
output.append("// Código pseudo estructurado generado desde LAD TwinCAT")
output.append("// Compatible con IEC61131-3")
output.append("PROGRAM Input_Converted")
output.append("")
for network in self.networks:
output.append(f" // Red {network['id']}")
if network['comment']:
output.append(f" // {network['comment']}")
for assignment in network['assignments']:
structured_line = self._convert_assignment(assignment)
if structured_line:
if assignment['conditions']:
# Construir condición
conditions = []
for cond in assignment['conditions']:
if cond['negated']:
conditions.append(f"NOT {cond['name']}")
else:
conditions.append(cond['name'])
condition_str = " AND ".join(conditions)
output.append(f" IF {condition_str} THEN")
output.append(f" {structured_line};")
output.append(" END_IF;")
else:
output.append(f" {structured_line};")
output.append("")
output.append("END_PROGRAM")
return '\n'.join(output)
def _convert_assignment(self, assignment):
"""Convertir una asignación a código estructurado"""
target = assignment['target']
expression = assignment['expression']
function_block = assignment['function_block']
operator = assignment['operator']
operands = assignment['operands']
if function_block:
params = ", ".join(operands) if operands else ""
if target:
return f"{target} := {function_block}({params})"
else:
return f"{function_block}({params})"
elif operator and operands:
if len(operands) >= 2:
if operator == "SEL":
return f"{target} := SEL({operands[0]}, {operands[1]}, condition)"
else:
return f"{target} := {operands[0]} {operator} {operands[1]}"
elif len(operands) == 1:
if operator == "MOVE":
return f"{target} := {operands[0]}"
else:
return f"{target} := {operator}({operands[0]})"
elif expression and target:
return f"{target} := {expression}"
return None
def save_to_file(self, output_path):
"""Guardar código estructurado"""
structured_code = self.convert_to_structured()
with open(output_path, 'w', encoding='utf-8') as f:
f.write(structured_code)
print(f"Código guardado en: {output_path}")
return structured_code
def main():
"""Función principal"""
converter = SimpleLadConverter()
try:
print("=== Convertidor LAD Simplificado ===")
print("Parseando archivo INPUT.EXP...")
converter.parse_file(".example/_PUMPCONTROL.EXP")
print(f"Redes encontradas: {len(converter.networks)}")
# Mostrar algunas estadísticas
for i, network in enumerate(converter.networks[:5]):
print(f"Red {network['id']}: {len(network['assignments'])} asignaciones")
if network['comment']:
print(f" Comentario: {network['comment']}")
# Convertir y guardar
print("\nGenerando código estructurado...")
structured_code = converter.save_to_file("simple_output.txt")
# Mostrar primeras líneas
lines = structured_code.split('\n')
print(f"\nPrimeras {min(30, len(lines))} líneas:")
for i, line in enumerate(lines[:30]):
print(f"{i+1:3d}: {line}")
print(f"\n✓ Conversión completada! Total de líneas: {len(lines)}")
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()