290 lines
14 KiB
Python
290 lines
14 KiB
Python
"""
|
|
arnold/module_types.py — Terminator I/O module type registry.
|
|
|
|
Every T1H (full-size) and T1K (compact) module that the EBC100 supports
|
|
is catalogued here as a frozen ModuleType dataclass.
|
|
|
|
Categories
|
|
----------
|
|
digital_input FC02 (read discrete inputs) — coil address space
|
|
digital_output FC01/FC05/FC15 (read/write coils) — coil address space
|
|
relay_output Same Modbus behaviour as digital_output, relay contacts
|
|
analog_input FC04 (read input registers) — register address space
|
|
analog_output FC03/FC06/FC16 (read/write holding registers) — register space
|
|
temperature_input FC04 (read input registers) — register address space
|
|
|
|
Address spaces
|
|
--------------
|
|
The EBC100 maintains TWO independent flat address spaces:
|
|
coil space — digital modules only, 1-bit per point
|
|
register space — analog + temperature modules, 16-bit per channel
|
|
|
|
Digital and analog modules do NOT interfere with each other's address
|
|
offsets. A digital-input module in slot 1 advances coil_offset but
|
|
has zero effect on register_offset, and vice-versa.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Literal
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Category type
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Category = Literal[
|
|
"digital_input",
|
|
"digital_output",
|
|
"relay_output",
|
|
"analog_input",
|
|
"analog_output",
|
|
"temperature_input",
|
|
]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ModuleType dataclass
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass(frozen=True)
|
|
class ModuleType:
|
|
part_number: str # e.g. "T1H-08TDS"
|
|
series: Literal["T1H", "T1K"] # housing form factor
|
|
category: Category
|
|
points: int # I/O count (channels for analog)
|
|
signal_type: str # human description of signal
|
|
resolution_bits: int | None = None # None for digital, 12/15/16 for analog
|
|
range_options: tuple[str, ...] = () # e.g. ("0-20mA","4-20mA","0-10V","±10V")
|
|
max_current_per_point: str = "" # e.g. "0.3A", "1A@250VAC"
|
|
|
|
# -- Derived properties --------------------------------------------------
|
|
|
|
@property
|
|
def is_digital(self) -> bool:
|
|
return self.category in (
|
|
"digital_input", "digital_output", "relay_output",
|
|
)
|
|
|
|
@property
|
|
def is_analog(self) -> bool:
|
|
return self.category in (
|
|
"analog_input", "analog_output", "temperature_input",
|
|
)
|
|
|
|
@property
|
|
def direction(self) -> Literal["input", "output"]:
|
|
if self.category in ("digital_input", "analog_input", "temperature_input"):
|
|
return "input"
|
|
return "output"
|
|
|
|
@property
|
|
def modbus_space(self) -> Literal["coil", "register"]:
|
|
"""Which EBC100 address space this module lives in."""
|
|
return "coil" if self.is_digital else "register"
|
|
|
|
@property
|
|
def value_type(self) -> Literal["bool", "int"]:
|
|
"""Python type of per-point values: bool for digital, int for analog."""
|
|
return "bool" if self.is_digital else "int"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Full registry
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_ALL_MODULES: list[ModuleType] = [
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# DIGITAL INPUTS — T1H
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-08TDS", "T1H", "digital_input", 8,
|
|
"24VDC sinking (NPN) input, 4.1mA/pt"),
|
|
ModuleType("T1H-08ND3", "T1H", "digital_input", 8,
|
|
"24VDC sinking/sourcing input"),
|
|
ModuleType("T1H-08ND3P", "T1H", "digital_input", 8,
|
|
"24VDC sourcing (PNP) input"),
|
|
ModuleType("T1H-16ND3", "T1H", "digital_input", 16,
|
|
"24VDC sinking/sourcing input"),
|
|
ModuleType("T1H-08NA", "T1H", "digital_input", 8,
|
|
"120VAC input"),
|
|
ModuleType("T1H-16NA", "T1H", "digital_input", 16,
|
|
"120VAC input"),
|
|
|
|
# DIGITAL INPUTS — T1K
|
|
ModuleType("T1K-08TDS", "T1K", "digital_input", 8,
|
|
"24VDC sinking (NPN) input, 4.1mA/pt"),
|
|
ModuleType("T1K-08ND3", "T1K", "digital_input", 8,
|
|
"24VDC sinking/sourcing input"),
|
|
ModuleType("T1K-16ND3", "T1K", "digital_input", 16,
|
|
"24VDC sinking/sourcing input"),
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# DIGITAL OUTPUTS — T1H
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-08TD1", "T1H", "digital_output", 8,
|
|
"24VDC sourcing transistor output",
|
|
max_current_per_point="0.3A"),
|
|
ModuleType("T1H-08TD2", "T1H", "digital_output", 8,
|
|
"12-24VDC sinking transistor output",
|
|
max_current_per_point="0.3A"),
|
|
ModuleType("T1H-16TD1", "T1H", "digital_output", 16,
|
|
"24VDC sourcing transistor output",
|
|
max_current_per_point="0.1A"),
|
|
ModuleType("T1H-16TD2", "T1H", "digital_output", 16,
|
|
"12-24VDC sinking transistor output",
|
|
max_current_per_point="0.1A"),
|
|
ModuleType("T1H-08TA", "T1H", "digital_output", 8,
|
|
"120/240VAC triac output",
|
|
max_current_per_point="0.5A"),
|
|
|
|
# DIGITAL OUTPUTS — T1K
|
|
ModuleType("T1K-08TD1", "T1K", "digital_output", 8,
|
|
"24VDC sourcing transistor output",
|
|
max_current_per_point="0.3A"),
|
|
ModuleType("T1K-08TD2", "T1K", "digital_output", 8,
|
|
"12-24VDC sinking transistor output",
|
|
max_current_per_point="0.3A"),
|
|
ModuleType("T1K-16TD1", "T1K", "digital_output", 16,
|
|
"24VDC sourcing transistor output",
|
|
max_current_per_point="0.1A"),
|
|
ModuleType("T1K-16TD2", "T1K", "digital_output", 16,
|
|
"12-24VDC sinking transistor output",
|
|
max_current_per_point="0.1A"),
|
|
ModuleType("T1K-16TD2-1","T1K", "digital_output", 16,
|
|
"12-24VDC sourcing transistor output",
|
|
max_current_per_point="0.1A"),
|
|
ModuleType("T1K-08TA", "T1K", "digital_output", 8,
|
|
"120/240VAC triac output",
|
|
max_current_per_point="0.5A"),
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# RELAY OUTPUTS
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-08TRS", "T1H", "relay_output", 8,
|
|
"Relay output (Form A SPST-NO)",
|
|
max_current_per_point="1A@250VAC"),
|
|
ModuleType("T1H-16TRS", "T1H", "relay_output", 16,
|
|
"Relay output (Form A SPST-NO)",
|
|
max_current_per_point="1A@250VAC"),
|
|
ModuleType("T1H-16TRS2", "T1H", "relay_output", 16,
|
|
"Relay output (Form A SPST-NO)",
|
|
max_current_per_point="2A@250VAC"),
|
|
ModuleType("T1K-08TRS", "T1K", "relay_output", 8,
|
|
"Relay output (Form A SPST-NO)",
|
|
max_current_per_point="1A@250VAC"),
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# ANALOG INPUTS — T1H
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-08AD-1", "T1H", "analog_input", 8,
|
|
"Voltage/current analog input, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V", "0-5V", "±5V")),
|
|
ModuleType("T1H-08AD-2", "T1H", "analog_input", 8,
|
|
"Voltage/current analog input, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V", "0-5V", "±5V")),
|
|
ModuleType("T1H-16AD-1", "T1H", "analog_input", 16,
|
|
"Voltage/current analog input, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1H-16AD-2", "T1H", "analog_input", 16,
|
|
"Voltage/current analog input, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
|
|
# ANALOG INPUTS — T1K
|
|
ModuleType("T1K-08AD-1", "T1K", "analog_input", 8,
|
|
"Voltage/current analog input, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V", "0-5V", "±5V")),
|
|
ModuleType("T1K-08AD-2", "T1K", "analog_input", 8,
|
|
"Voltage/current analog input, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V", "0-5V", "±5V")),
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# ANALOG OUTPUTS — T1H
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-04DA-1", "T1H", "analog_output", 4,
|
|
"Voltage/current analog output, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1H-04DA-2", "T1H", "analog_output", 4,
|
|
"Voltage/current analog output, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1H-08DA-1", "T1H", "analog_output", 8,
|
|
"Voltage/current analog output, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1H-08DA-2", "T1H", "analog_output", 8,
|
|
"Voltage/current analog output, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1H-16DA-1", "T1H", "analog_output", 16,
|
|
"Voltage/current analog output, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V")),
|
|
ModuleType("T1H-16DA-2", "T1H", "analog_output", 16,
|
|
"Voltage/current analog output, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V")),
|
|
|
|
# ANALOG OUTPUTS — T1K
|
|
ModuleType("T1K-04DA-1", "T1K", "analog_output", 4,
|
|
"Voltage/current analog output, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1K-04DA-2", "T1K", "analog_output", 4,
|
|
"Voltage/current analog output, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1K-08DA-1", "T1K", "analog_output", 8,
|
|
"Voltage/current analog output, 12-bit",
|
|
resolution_bits=12,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
ModuleType("T1K-08DA-2", "T1K", "analog_output", 8,
|
|
"Voltage/current analog output, 15-bit",
|
|
resolution_bits=15,
|
|
range_options=("0-20mA", "4-20mA", "0-10V", "±10V")),
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# TEMPERATURE INPUTS
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
ModuleType("T1H-08THM", "T1H", "temperature_input", 8,
|
|
"Thermocouple input (J/K/E/T/R/S/N/B)",
|
|
resolution_bits=16,
|
|
range_options=("type J", "type K", "type E", "type T",
|
|
"type R", "type S", "type N", "type B")),
|
|
ModuleType("T1H-04RTD", "T1H", "temperature_input", 4,
|
|
"RTD input (Pt100/Pt1000/Ni120)",
|
|
resolution_bits=16,
|
|
range_options=("Pt100", "Pt1000", "Ni120")),
|
|
ModuleType("T1K-08THM", "T1K", "temperature_input", 8,
|
|
"Thermocouple input (J/K/E/T/R/S/N/B)",
|
|
resolution_bits=16,
|
|
range_options=("type J", "type K", "type E", "type T",
|
|
"type R", "type S", "type N", "type B")),
|
|
ModuleType("T1K-04RTD", "T1K", "temperature_input", 4,
|
|
"RTD input (Pt100/Pt1000/Ni120)",
|
|
resolution_bits=16,
|
|
range_options=("Pt100", "Pt1000", "Ni120")),
|
|
]
|
|
|
|
|
|
# Build the lookup dict
|
|
MODULE_REGISTRY: dict[str, ModuleType] = {m.part_number: m for m in _ALL_MODULES}
|
|
|
|
|
|
def get_module_type(part_number: str) -> ModuleType:
|
|
"""Look up a module type by part number. Raises KeyError with helpful message."""
|
|
try:
|
|
return MODULE_REGISTRY[part_number]
|
|
except KeyError:
|
|
known = ", ".join(sorted(MODULE_REGISTRY))
|
|
raise KeyError(
|
|
f"Unknown module type {part_number!r}. "
|
|
f"Known types: {known}"
|
|
) from None
|