#!/usr/bin/env python3 """ VFD Module Boilerplate Model ============================ Model for VFD (Variable Frequency Drive) modules with support for different horsepower ratings. Supports 15, 20, and 30 HP variants. """ from typing import Optional, TYPE_CHECKING import xml.etree.ElementTree as ET from dataclasses import dataclass from datetime import datetime import os if TYPE_CHECKING: from excel_data_processor import ModuleData from .mcm_pattern_utils import get_parent_for_vfd, check_and_register_en4tr_for_device @dataclass class VFDModuleConfig: """Configuration for a VFD module instance.""" name: str # Module name (e.g., "VFD1") hp: str # Horsepower rating: "15", "20", "30" ip_address: str = "11.200.1.10" parent_module: str = "SLOT2_EN4TR" parent_port_id: str = "2" class VFDModuleGenerator: """Generator for VFD module XML with different HP support.""" # Mapping of HP values to boilerplate filenames HP_BOILERPLATE_MAP = { "15": "VFD_Module_15_HP.L5X", "20": "VFD_Module_20_HP.L5X", "30": "VFD_Module_30_HP.L5X" } def __init__(self, config: VFDModuleConfig): self.config = config # Determine the correct boilerplate file if self.config.hp not in self.HP_BOILERPLATE_MAP: raise ValueError(f"Unsupported HP value: {self.config.hp}. Supported values: 15, 20, 30") self.boilerplate_filename = self.HP_BOILERPLATE_MAP[self.config.hp] # Use project-specific boilerplate directory if set, otherwise default boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate') self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename) self.tree = None self.root = None def load_boilerplate(self): """Load the appropriate boilerplate template based on HP rating.""" if not os.path.exists(self.boilerplate_path): raise FileNotFoundError(f"Boilerplate file not found: {self.boilerplate_path}") self.tree = ET.parse(self.boilerplate_path) self.root = self.tree.getroot() def update_module_name(self): """Update the module name throughout the XML.""" # Update in root attributes self.root.set("TargetName", self.config.name) # Update Module element module = self.root.find(".//Module[@Use='Target']") if module is not None: module.set("Name", self.config.name) def update_ip_address(self): """Update the IP address in the Ethernet port.""" port = self.root.find(".//Port[@Type='Ethernet']") if port is not None: port.set("Address", self.config.ip_address) def update_parent_module(self): """Update parent module references.""" module = self.root.find(".//Module[@Use='Target']") if module is not None: module.set("ParentModule", self.config.parent_module) module.set("ParentModPortId", self.config.parent_port_id) def update_export_date(self): """Update the export date to current time.""" export_date = datetime.now().strftime("%a %b %d %H:%M:%S %Y") self.root.set("ExportDate", export_date) def apply_updates(self): """Apply all updates to the boilerplate.""" self.update_module_name() self.update_ip_address() self.update_parent_module() self.update_export_date() def save(self, output_path: str): """Save the updated module to file.""" if self.tree is None: raise RuntimeError("No boilerplate loaded. Call load_boilerplate() first.") # Save with proper formatting self.tree.write(output_path, encoding='UTF-8', xml_declaration=True) def get_xml_string(self) -> str: """Get the XML as a string.""" if self.tree is None: raise RuntimeError("No boilerplate loaded. Call load_boilerplate() first.") return ET.tostring(self.root, encoding='unicode') # ------------------------------------------------------------------ # Helper for EnhancedMCMGenerator refactor # ------------------------------------------------------------------ @classmethod def from_excel(cls, module_data: 'ModuleData', hp: str, parent_module: str = None) -> 'VFDModuleGenerator': # Determine parent module using pattern matching if not explicitly provided if parent_module is None: has_ip = bool(module_data.ip_address and module_data.ip_address.strip()) parent_module = check_and_register_en4tr_for_device(module_data.tagname, has_ip_address=has_ip) cfg = create_vfd_module( name=module_data.tagname, hp=hp, ip_address=module_data.ip_address or "11.200.1.10", parent_module=parent_module, parent_port_id="2", ) gen = cls(cfg) gen.load_boilerplate() gen.apply_updates() return gen def create_vfd_module(name: str, hp: str, ip_address: str = "11.200.1.10", parent_module: str = "SLOT2_EN4TR", parent_port_id: str = "2") -> VFDModuleConfig: """Factory function to create a VFD module configuration.""" return VFDModuleConfig( name=name, hp=hp, ip_address=ip_address, parent_module=parent_module, parent_port_id=parent_port_id ) # Example usage if __name__ == "__main__": # Example: Create a 15 HP VFD module config = create_vfd_module( name="VFD1_15HP", hp="15", # Specify the horsepower ip_address="11.200.1.10", parent_module="SLOT2_EN4TR" ) generator = VFDModuleGenerator(config) generator.load_boilerplate() generator.apply_updates() generator.save("generated/VFD1_15HP.L5X") print(f"Generated {config.hp} HP VFD module: {config.name}")