#!/usr/bin/env python3 """ PMM (Power Monitoring Module) Boilerplate Model =============================================== Model for 1420-V2-ENT Power Monitoring modules. Supports name, IP address, and parent module configuration. """ 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 @dataclass class PMMModuleConfig: """Configuration for a PMM (Power Monitoring Module) instance.""" name: str # Module name (e.g., "CP01_V2_ENT_Voltage_Monitor") ip_address: str = "11.200.1.2" parent_module: str = "SLOT2_EN4TR" parent_port_id: str = "2" class PMMModuleGenerator: """Generates PMM (Power Monitoring Module) configurations from boilerplate.""" def __init__(self, config: PMMModuleConfig): self.config = config self.boilerplate_filename = "PMM_Module.L5X" self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename) self.tree = None self.root = None def load_boilerplate(self): """Load the boilerplate XML file.""" 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 in the XML.""" # Update module name module = self.root.find(".//Module[@Use='Target']") if module is not None: module.set("Name", self.config.name) # Update target name in root self.root.set("TargetName", self.config.name) def update_ip_address(self): """Update the IP address in the module configuration.""" # Find the Ethernet port and update IP address 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 the parent module configuration.""" 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 configuration updates.""" self.update_module_name() self.update_ip_address() self.update_parent_module() self.update_export_date() def save(self, output_path: str): """Save the configured module to a file.""" if self.tree is None: raise ValueError("No boilerplate loaded. Call load_boilerplate() first.") # Create output directory if it doesn't exist output_dir = os.path.dirname(output_path) if output_dir and not os.path.exists(output_dir): os.makedirs(output_dir) # Write the XML to file with open(output_path, 'wb') as f: f.write(b'\n') self.tree.write(f, encoding='UTF-8') # ------------------------------------------------------------------ # Convenience helper for generator refactor # ------------------------------------------------------------------ @classmethod def from_excel(cls, module_data: 'ModuleData') -> 'PMMModuleGenerator': """Create and return a generator configured from Excel ModuleData.""" ip_addr = module_data.ip_address or "11.200.1.2" cfg = create_pmm_module( name=module_data.tagname, ip_address=ip_addr, parent_module="SLOT2_EN4TR", parent_port_id="2", ) gen = cls(cfg) gen.load_boilerplate() gen.apply_updates() return gen def create_pmm_module(name: str, ip_address: str = "11.200.1.2", parent_module: str = "SLOT2_EN4TR", parent_port_id: str = "2") -> PMMModuleConfig: """ Factory function to create a PMM (Power Monitoring Module) configuration. Args: name: Module name (e.g., "CP01_V2_ENT_Voltage_Monitor") ip_address: IP address for the module (default: "11.200.1.2") parent_module: Parent module name (default: "SLOT2_EN4TR") parent_port_id: Parent port ID (default: "2") Returns: PMMModuleConfig: Configured PMM module """ return PMMModuleConfig( name=name, ip_address=ip_address, parent_module=parent_module, parent_port_id=parent_port_id ) def main(): """Example usage of the PMM module generator.""" print("PMM (Power Monitoring Module) Generator Example") print("=" * 50) # Create PMM module configuration config = create_pmm_module( name="CP01_V2_ENT_Voltage_Monitor", ip_address="11.200.1.2", parent_module="SLOT2_EN4TR" ) # Generate the module generator = PMMModuleGenerator(config) generator.load_boilerplate() generator.apply_updates() # Save to generated folder os.makedirs("generated", exist_ok=True) output_file = f"generated/{config.name}.L5X" generator.save(output_file) print(f"Generated PMM module: {output_file}") print(f" Name: {config.name}") print(f" IP Address: {config.ip_address}") print(f" Parent Module: {config.parent_module}") print(f" Parent Port: {config.parent_port_id}") print("\nModule Features:") print(" - Allen-Bradley 1420-V2-ENT Power Monitor") print(" - Real-time power monitoring and metering") print(" - Multiple data connections (I1, I2, I3, I4)") print(" - Voltage, current, power, and energy measurements") print(" - Ethernet/IP communication") print(" - Firmware and identification information") print(" - Power factor and frequency monitoring") print(" - Energy consumption tracking (kWh, kVARh)") if __name__ == "__main__": main()