268 lines
9.6 KiB
Python
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() |