jmri_nce.command_set

NCE command-set strategy — captures variations between firmware versions.

NCE command stations have evolved memory layouts and feature flags. The three production variants Stage 4 supports:

  • PH-Pro 2006+ — full PowerPro/Smart Booster with the post-2006 binary command set; turnout/macro/consist tables at the canonical addresses.
  • PowerCab — smaller command station, different turnout-memory base, same binary command opcodes.
  • SB5 (Smart Booster) — booster-only variant; same memory map as PH-Pro 2006+ but a few features (e.g. macro recall) are unavailable.

The NceCommandSet dataclass captures every per-variant constant so service code (turnout monitor, AIU poller, programmer) reads memory addresses from the active command set rather than hard-coding.

 1"""NCE command-set strategy — captures variations between firmware versions.
 2
 3NCE command stations have evolved memory layouts and feature flags. The
 4three production variants Stage 4 supports:
 5
 6* **PH-Pro 2006+** — full PowerPro/Smart Booster with the post-2006 binary
 7  command set; turnout/macro/consist tables at the canonical addresses.
 8* **PowerCab** — smaller command station, different turnout-memory base,
 9  same binary command opcodes.
10* **SB5 (Smart Booster)** — booster-only variant; same memory map as
11  PH-Pro 2006+ but a few features (e.g. macro recall) are unavailable.
12
13The :class:`NceCommandSet` dataclass captures every per-variant constant
14so service code (turnout monitor, AIU poller, programmer) reads memory
15addresses from the active command set rather than hard-coding.
16"""
17from __future__ import annotations
18
19from dataclasses import dataclass
20
21
22@dataclass(frozen=True, slots=True)
23class NceCommandSet:
24    """Per-firmware constants used by services."""
25
26    name: str
27    turnout_memory_base: int    # base of accessory/turnout status table
28    turnout_table_bytes: int    # 8 turnouts per byte (256 bytes → 2048 turnouts)
29    macro_memory_base: int      # base of macro table
30    consist_memory_base: int    # base of consist table
31    supports_ops_mode_prog: bool
32    aiu_polling_supported: bool
33
34
35# ---- canonical instances --------------------------------------------------
36
37
38NCE_PH_PRO_2006 = NceCommandSet(
39    name="PH-Pro 2006+",
40    turnout_memory_base=0xEC00,
41    turnout_table_bytes=256,
42    macro_memory_base=0xC800,
43    consist_memory_base=0xF500,
44    supports_ops_mode_prog=True,
45    aiu_polling_supported=True,
46)
47
48NCE_POWERCAB = NceCommandSet(
49    name="PowerCab",
50    turnout_memory_base=0xEC00,
51    turnout_table_bytes=64,    # PowerCab supports fewer turnouts (~512)
52    macro_memory_base=0xC800,
53    consist_memory_base=0xF500,
54    supports_ops_mode_prog=True,
55    aiu_polling_supported=False,  # AIU isn't typically used with PowerCab
56)
57
58NCE_SB5 = NceCommandSet(
59    name="Smart Booster SB5",
60    turnout_memory_base=0xEC00,
61    turnout_table_bytes=256,
62    macro_memory_base=0xC800,
63    consist_memory_base=0xF500,
64    supports_ops_mode_prog=True,
65    aiu_polling_supported=True,
66)
67
68
69COMMAND_SETS: dict[str, NceCommandSet] = {
70    cs.name: cs for cs in (NCE_PH_PRO_2006, NCE_POWERCAB, NCE_SB5)
71}
72
73
74def get_command_set(name: str) -> NceCommandSet:
75    """Look up a command set by friendly name. Raises ``KeyError`` if unknown."""
76    if name not in COMMAND_SETS:
77        raise KeyError(
78            f"unknown NCE command set: {name!r} (have: {sorted(COMMAND_SETS)})"
79        )
80    return COMMAND_SETS[name]
@dataclass(frozen=True, slots=True)
class NceCommandSet:
23@dataclass(frozen=True, slots=True)
24class NceCommandSet:
25    """Per-firmware constants used by services."""
26
27    name: str
28    turnout_memory_base: int    # base of accessory/turnout status table
29    turnout_table_bytes: int    # 8 turnouts per byte (256 bytes → 2048 turnouts)
30    macro_memory_base: int      # base of macro table
31    consist_memory_base: int    # base of consist table
32    supports_ops_mode_prog: bool
33    aiu_polling_supported: bool

Per-firmware constants used by services.

NceCommandSet( name: str, turnout_memory_base: int, turnout_table_bytes: int, macro_memory_base: int, consist_memory_base: int, supports_ops_mode_prog: bool, aiu_polling_supported: bool)
name: str
turnout_memory_base: int
turnout_table_bytes: int
macro_memory_base: int
consist_memory_base: int
supports_ops_mode_prog: bool
aiu_polling_supported: bool
NCE_PH_PRO_2006 = NceCommandSet(name='PH-Pro 2006+', turnout_memory_base=60416, turnout_table_bytes=256, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=True)
NCE_POWERCAB = NceCommandSet(name='PowerCab', turnout_memory_base=60416, turnout_table_bytes=64, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=False)
NCE_SB5 = NceCommandSet(name='Smart Booster SB5', turnout_memory_base=60416, turnout_table_bytes=256, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=True)
COMMAND_SETS: dict[str, NceCommandSet] = {'PH-Pro 2006+': NceCommandSet(name='PH-Pro 2006+', turnout_memory_base=60416, turnout_table_bytes=256, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=True), 'PowerCab': NceCommandSet(name='PowerCab', turnout_memory_base=60416, turnout_table_bytes=64, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=False), 'Smart Booster SB5': NceCommandSet(name='Smart Booster SB5', turnout_memory_base=60416, turnout_table_bytes=256, macro_memory_base=51200, consist_memory_base=62720, supports_ops_mode_prog=True, aiu_polling_supported=True)}
def get_command_set(name: str) -> NceCommandSet:
75def get_command_set(name: str) -> NceCommandSet:
76    """Look up a command set by friendly name. Raises ``KeyError`` if unknown."""
77    if name not in COMMAND_SETS:
78        raise KeyError(
79            f"unknown NCE command set: {name!r} (have: {sorted(COMMAND_SETS)})"
80        )
81    return COMMAND_SETS[name]

Look up a command set by friendly name. Raises KeyError if unknown.