2025-08-05 14:38:54 +04:00

162 lines
5.6 KiB
Python

"""
IB16 Digital Input Module L5X Generator (Boilerplate-based)
===========================================================
This module provides functionality to generate IB16 module L5X files by
loading a boilerplate template and modifying specific fields.
"""
from dataclasses import dataclass
from typing import Optional, Dict
import xml.etree.ElementTree as ET
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
@dataclass
class IB16ModuleConfig(BaseModuleConfig):
"""Configuration for an IB16 module."""
name: str
boilerplate_path: str = "boilerplate/SLOT5_IB16_Module.L5X"
slot_address: str = "5"
parent_module: str = "Local"
parent_port_id: str = "1"
input_device_names: Optional[Dict[int, str]] = None # Names for 16 input devices (0-15)
def get_updates(self):
"""Get dictionary of updates to apply."""
return {
"name": self.name,
"slot_address": self.slot_address,
"parent_module": self.parent_module,
"parent_port_id": self.parent_port_id,
"input_device_names": self.input_device_names
}
class IB16ModuleGenerator(BaseBoilerplateGenerator):
"""Generator for IB16 module L5X files using boilerplate template."""
def apply_updates(self):
"""Apply IB16-specific updates to the boilerplate."""
# Update TargetName in root element
self.root.set("TargetName", self.config.name)
# Update Module name and parent info
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 input device comments
self.update_comments()
def update_comments(self):
"""Update comments for input devices."""
# Always clear boilerplate comments so callers can opt-in to add their
# own. When no mapping is supplied, the section remains empty.
input_comments = self.root.find(".//Connection[@Name='StandardInput']/InputTag/Comments")
if input_comments is None:
return
# Clear whatever the template had.
input_comments.clear()
if not self.config.input_device_names:
return # leave section empty (no comments)
# Populate provided names
for index, text in self.config.input_device_names.items():
comment = ET.SubElement(input_comments, "Comment")
comment.set("Operand", f".DATA.{index}")
comment.text = text
@classmethod
def from_mapping(cls, mapping: Dict[str, str], comments: Optional[Dict[int, str]] = None) -> "IB16ModuleGenerator":
"""Create and fully configure an IB16 generator from the Excel-derived
`modules` entry (a plain dict). The structure expected is the one
produced in EnhancedMCMGenerator._organize_modules_by_type().
:param comments: Optional dict of input index (0-15) to comment text.
"""
cfg = IB16ModuleConfig(
name=mapping["name"],
slot_address=mapping["slot_address"],
parent_module="Local",
parent_port_id="1",
input_device_names=comments,
)
gen = cls(cfg)
gen.load_boilerplate()
gen.apply_updates()
return gen
# Factory function
def create_ib16_module(
name: str,
slot_address: str = "5",
parent_module: str = "Local",
parent_port_id: str = "1",
input_device_names: Optional[Dict[int, str]] = None
) -> IB16ModuleConfig:
"""
Create an IB16 module configuration.
Args:
name: Module name
slot_address: Slot number in the chassis
parent_module: Parent module name
parent_port_id: Parent module port ID
input_device_names: Dictionary mapping input index (0-15) to device names
Returns:
IB16ModuleConfig object
"""
return IB16ModuleConfig(
name=name,
slot_address=slot_address,
parent_module=parent_module,
parent_port_id=parent_port_id,
input_device_names=input_device_names
)
if __name__ == "__main__":
# Example usage
config = create_ib16_module(
name="SLOT4_IB16",
slot_address="4",
parent_module="Local",
parent_port_id="1",
input_device_names={
0: "Emergency Stop",
1: "Start Button",
2: "Stop Button",
3: "Reset Button",
4: "Door Sensor",
5: "Light Curtain",
6: "Pressure Switch",
7: "Flow Switch",
8: "Level Sensor High",
9: "Level Sensor Low",
10: "Temperature Alarm",
11: "Vibration Sensor",
12: "Proximity Sensor 1",
13: "Proximity Sensor 2",
14: "Limit Switch Up",
15: "Limit Switch Down"
}
)
# Generate the module
generator = IB16ModuleGenerator(config)
generator.save_to_file("generated/SLOT4_IB16.L5X")
print(f"Generated IB16 module configuration saved to generated/SLOT4_IB16.L5X")