193 lines
7.1 KiB
Python
193 lines
7.1 KiB
Python
"""
|
|
EN4TR EtherNet/IP Module L5X Generator (Boilerplate-based)
|
|
==========================================================
|
|
|
|
This module provides functionality to generate EN4TR module L5X files by
|
|
loading a boilerplate template and modifying specific fields.
|
|
"""
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
|
|
|
|
|
@dataclass
|
|
class EN4TRModuleConfig(BaseModuleConfig):
|
|
"""Configuration for an EN4TR module."""
|
|
name: str
|
|
ethernet_address: str # e.g., "11.200.1.1"
|
|
slot_number: str = "2" # Primary slot specification (2, 3, 4, etc.)
|
|
boilerplate_path: str = "" # Will be auto-determined based on slot if empty
|
|
parent_module: str = "Local"
|
|
parent_port_id: str = "1"
|
|
slot_address: str = "" # Will be set to slot_number if empty (for backward compatibility)
|
|
|
|
def __post_init__(self):
|
|
"""Post-initialization to set defaults based on slot specification."""
|
|
# Set slot_address to slot_number if not provided (backward compatibility)
|
|
if not self.slot_address:
|
|
self.slot_address = self.slot_number
|
|
|
|
# Auto-determine boilerplate path if not provided
|
|
if not self.boilerplate_path:
|
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
|
# Try slot-specific first, then fall back to generic SLOT2_EN4TR
|
|
slot_specific_filename = f'SLOT{self.slot_number}_EN4TR_Module.L5X'
|
|
generic_filename = 'SLOT2_EN4TR_Module.L5X'
|
|
|
|
slot_specific_path = os.path.join(boilerplate_dir, slot_specific_filename)
|
|
generic_path = os.path.join(boilerplate_dir, generic_filename)
|
|
|
|
if os.path.exists(slot_specific_path):
|
|
self.boilerplate_path = slot_specific_path
|
|
elif os.path.exists(generic_path):
|
|
self.boilerplate_path = generic_path
|
|
print(f" Using generic EN4TR boilerplate for {self.name}: {generic_filename}")
|
|
else:
|
|
# Default to generic filename if neither exists
|
|
self.boilerplate_path = generic_path
|
|
|
|
# Auto-set name based on slot if not following standard pattern
|
|
if not self.name or self.name == "EN4TR":
|
|
self.name = f"SLOT{self.slot_number}_EN4TR"
|
|
|
|
def get_updates(self):
|
|
"""Get dictionary of updates to apply."""
|
|
return {
|
|
"name": self.name,
|
|
"ethernet_address": self.ethernet_address,
|
|
"parent_module": self.parent_module,
|
|
"parent_port_id": self.parent_port_id,
|
|
"slot_address": self.slot_address,
|
|
"slot_number": self.slot_number
|
|
}
|
|
|
|
|
|
class EN4TRModuleGenerator(BaseBoilerplateGenerator):
|
|
"""Generator for EN4TR module L5X files using boilerplate template."""
|
|
|
|
def apply_updates(self):
|
|
"""Apply EN4TR-specific updates to the boilerplate."""
|
|
# Update TargetName in root element
|
|
self.root.set("TargetName", self.config.name)
|
|
|
|
# Update Module name
|
|
module = self.root.find(".//Module[@Use='Target']")
|
|
if module is not None:
|
|
module.set("Name", self.config.name)
|
|
module.set("ParentModule", self.config.parent_module)
|
|
module.set("ParentModPortId", self.config.parent_port_id)
|
|
|
|
# Update slot address (ICP port)
|
|
icp_port = self.root.find(".//Module[@Use='Target']/Ports/Port[@Type='ICP']")
|
|
if icp_port is not None:
|
|
icp_port.set("Address", self.config.slot_address)
|
|
|
|
# Update Ethernet address
|
|
eth_port = self.root.find(".//Module[@Use='Target']/Ports/Port[@Type='Ethernet']")
|
|
if eth_port is not None:
|
|
eth_port.set("Address", self.config.ethernet_address)
|
|
|
|
|
|
# Factory functions
|
|
def create_en4tr_module(
|
|
name: str,
|
|
ethernet_address: str,
|
|
slot_address: str = "2",
|
|
parent_module: str = "Local",
|
|
parent_port_id: str = "1"
|
|
) -> EN4TRModuleConfig:
|
|
"""
|
|
Create an EN4TR module configuration (legacy interface).
|
|
|
|
Args:
|
|
name: Module name
|
|
ethernet_address: Ethernet IP address (e.g., "11.200.1.1")
|
|
slot_address: Slot number in the chassis
|
|
parent_module: Parent module name
|
|
parent_port_id: Parent module port ID
|
|
|
|
Returns:
|
|
EN4TRModuleConfig object
|
|
"""
|
|
return EN4TRModuleConfig(
|
|
name=name,
|
|
ethernet_address=ethernet_address,
|
|
slot_number=slot_address, # Use slot_number as primary
|
|
slot_address=slot_address, # Keep for backward compatibility
|
|
parent_module=parent_module,
|
|
parent_port_id=parent_port_id
|
|
)
|
|
|
|
|
|
def create_en4tr_for_slot(
|
|
slot_number: str,
|
|
ethernet_address: str,
|
|
name: str = "",
|
|
parent_module: str = "Local",
|
|
parent_port_id: str = "1"
|
|
) -> EN4TRModuleConfig:
|
|
"""
|
|
Create an EN4TR module configuration by slot number (recommended interface).
|
|
|
|
Args:
|
|
slot_number: Slot number in the chassis (e.g., "2", "3", "4")
|
|
ethernet_address: Ethernet IP address (e.g., "11.200.1.1")
|
|
name: Module name (auto-generated as SLOTx_EN4TR if empty)
|
|
parent_module: Parent module name
|
|
parent_port_id: Parent module port ID
|
|
|
|
Returns:
|
|
EN4TRModuleConfig object
|
|
"""
|
|
return EN4TRModuleConfig(
|
|
name=name or f"SLOT{slot_number}_EN4TR",
|
|
ethernet_address=ethernet_address,
|
|
slot_number=slot_number,
|
|
parent_module=parent_module,
|
|
parent_port_id=parent_port_id
|
|
)
|
|
|
|
|
|
def extract_slot_from_name(module_name: str) -> str:
|
|
"""
|
|
Extract slot number from EN4TR module name.
|
|
|
|
Args:
|
|
module_name: Module name like "SLOT3_EN4TR" or "SLOT2_EN4TR"
|
|
|
|
Returns:
|
|
Slot number as string (e.g., "3", "2")
|
|
"""
|
|
import re
|
|
match = re.search(r'SLOT(\d+)_EN4TR', module_name, re.IGNORECASE)
|
|
if match:
|
|
return match.group(1)
|
|
return "2" # Default to slot 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Example usage - new slot-centric approach (recommended)
|
|
config = create_en4tr_for_slot(
|
|
slot_number="3",
|
|
ethernet_address="11.200.1.10"
|
|
)
|
|
|
|
# Generate the module
|
|
generator = EN4TRModuleGenerator(config)
|
|
generator.save_to_file("generated/SLOT3_EN4TR.L5X")
|
|
print(f"Generated EN4TR module configuration for slot {config.slot_number}: {config.name}")
|
|
|
|
# Example: Creating multiple EN4TR modules for different slots
|
|
slots_config = [
|
|
("2", "11.200.1.1"),
|
|
("3", "11.200.1.2"),
|
|
("4", "11.200.1.3")
|
|
]
|
|
|
|
for slot, ip in slots_config:
|
|
config = create_en4tr_for_slot(slot_number=slot, ethernet_address=ip)
|
|
print(f"Created config for {config.name} at IP {config.ethernet_address}")
|
|
# generator = EN4TRModuleGenerator(config)
|
|
# generator.save_to_file(f"generated/{config.name}.L5X") |