#!/usr/bin/env python3 """ CODESYS XML Export Parser This script parses exported CODESYS XML files (.export or .xml) to extract useful information for documentation. Usage: python3 parse-codesys-xml.py python3 parse-codesys-xml.py """ import sys import xml.etree.ElementTree as ET from pathlib import Path from typing import Dict, List, Optional def parse_codesys_export(file_path: str) -> Dict: """Parse a CODESYS XML export file.""" try: tree = ET.parse(file_path) root = tree.getroot() return extract_project_info(root) except ET.ParseError as e: print(f"Error parsing XML: {e}") return {} except Exception as e: print(f"Error reading file: {e}") return {} def extract_project_info(root: ET.Element) -> Dict: """Extract project information from XML root.""" info = { 'project_name': '', 'network_variables': [], 'programs': [], 'function_blocks': [], 'io_config': [], 'variables': [] } # Try to find project name project_elem = root.find('.//Project') if project is not None: info['project_name'] = project_elem.get('Name', 'Unknown') # Extract network variables # (Structure depends on CODESYS version and export format) for nv in root.findall('.//NetworkVariable'): nv_info = { 'name': nv.get('Name', ''), 'type': nv.get('Type', ''), 'direction': nv.get('Direction', ''), 'initial_value': nv.get('InitialValue', '') } info['network_variables'].append(nv_info) # Extract programs/POUs for pou in root.findall('.//POU'): pou_info = { 'name': pou.get('Name', ''), 'type': pou.get('POUType', ''), 'language': pou.get('Language', '') } info['programs'].append(pou_info) # Extract function blocks for fb in root.findall('.//FunctionBlock'): fb_info = { 'name': fb.get('Name', ''), 'type': fb.get('Type', '') } info['function_blocks'].append(fb_info) return info def print_summary(info: Dict): """Print a summary of extracted information.""" print("=" * 60) print("CODESYS Project Summary") print("=" * 60) if info.get('project_name'): print(f"\nProject Name: {info['project_name']}") if info.get('network_variables'): print(f"\nNetwork Variables ({len(info['network_variables'])}):") for nv in info['network_variables']: print(f" - {nv['name']} ({nv['type']}) [{nv.get('direction', 'N/A')}]") if info.get('programs'): print(f"\nPrograms/POUs ({len(info['programs'])}):") for prog in info['programs']: print(f" - {prog['name']} ({prog['type']}) [{prog.get('language', 'N/A')}]") if info.get('function_blocks'): print(f"\nFunction Blocks ({len(info['function_blocks'])}):") for fb in info['function_blocks']: print(f" - {fb['name']} ({fb.get('type', 'N/A')})") print("\n" + "=" * 60) def main(): if len(sys.argv) < 2: print("Usage: python3 parse-codesys-xml.py ") sys.exit(1) file_path = sys.argv[1] if not Path(file_path).exists(): print(f"Error: File not found: {file_path}") sys.exit(1) print(f"Parsing: {file_path}") info = parse_codesys_export(file_path) if info: print_summary(info) # Optionally save to JSON if len(sys.argv) > 2 and sys.argv[2] == '--json': import json output_file = Path(file_path).stem + '_parsed.json' with open(output_file, 'w') as f: json.dump(info, f, indent=2) print(f"\nParsed data saved to: {output_file}") else: print("No information extracted. The XML structure may be different.") print("Try opening the file in a text editor to inspect its structure.") if __name__ == '__main__': main()