Skip to content

Manage controllers' PV names from yaml config and modify GUI screen generation#63

Open
ggayDiamond wants to merge 12 commits into
mainfrom
gui/inline-root-controllers
Open

Manage controllers' PV names from yaml config and modify GUI screen generation#63
ggayDiamond wants to merge 12 commits into
mainfrom
gui/inline-root-controllers

Conversation

@ggayDiamond
Copy link
Copy Markdown
Collaborator

Inline GUI layout for root-path couplers/boxes

Couplers and EtherCAT boxes whose resolved PV path is a single segment (e.g. BL04I-EA-E1RIO-01) are now registered as direct sub-controllers of the server controller rather than nested inside the device controller. Their group_layout is set to GroupLayout.INLINE, so PVI renders them as Grid panels directly on the top-level server screen instead of behind a SubScreen click.
This works around a PVI constraint that forbids nesting a Group(Grid()) inside another Group. Multi-segment-path couplers (e.g. BL04I-EA-CATIO-01:ETH01:E1RIO01) are unaffected and remain as SubScreen children of the device controller. FastCS PV paths are unchanged — path resolution is independent of the FastCS parent–child hierarchy.

Configurable PV name templates (name_mappings)

A new CATioNameMappings dataclass lets operators control how EtherCAT device, node (coupler/box), and module PV path segments are generated from fastcs.yaml, without touching any Python code. Templates use Python str.format placeholders — {} / {n} for numeric index, {id} for the IOC root prefix, {device_prefix} and {node_prefix} for parent path segments. Using {id} or {node_prefix} causes the rendered result to be treated as an absolute PV path (split on :), enabling standalone coupler roots like BL04I-EA-E1RIO-01 alongside relative names like ETH01. Unknown placeholder keys are validated at construction time, before any hardware connection is attempted.

Also included:

Pin fastcs[epics]==0.15.0b4 and pvi==0.14.0b1; update uv.lock.
Allow EpicsGUIOptions.title to be null (removes the requirement for a title field in fastcs.yaml).
Remove verbose per-channel AI Settings / Internal / Vendor data CoE object groups from EP4374 in terminal_types.yaml to reduce GUI screen clutter.
Refresh fastcs-epics-ioc.md: correct class names (CATioDeviceController → EtherCATMasterController), add coupler-hoisting explanation, expand device/terminal attribute tables, and document the three IO handler classes and CATioServerControllerOptions sub-dataclasses.

ggayDiamond and others added 7 commits May 22, 2026 12:21
Now 'controllers' is correctly defined as an array rather than a keyed object.
Used commands:
cd /workspaces/CATio/src/fastcs_catio
uv run fastcs-catio schema > schema.json
- Add `IONodeType.Box` enum value and handle it like a coupler in
  `get_type_name()` (assigns RIO{node} PV name)
- Detect Box terminals via regex `_BOX_TYPE_RE` in `client.py` and
  assign `IONodeType.Box` category during chain location calculation
- Include Box nodes in the controller dispatch match-case alongside
  Coupler and Slave; always resolve terminal controllers via
  `get_terminal_controller_class()`
- Rename `SUPPORTED_CONTROLLERS` → `SUPPORTED_DEVICE_CONTROLLERS` and
  remove commented-out legacy terminal entries (only `ETHERCAT` device
  controller remains)
- Rename `get_supported_hardware` → `get_supported_devices` to reflect
  device-only scope
- Update unit tests for new Box node type and renamed test method
Couplers/boxes whose resolved path is a single segment (e.g.
BL04I-EA-E1RIO-01) are now registered as direct sub-controllers of the
server controller rather than of the device controller.  Their
group_layout is set to GroupLayout.INLINE so PVI renders them as Grid
panels on the top-level server screen.

PVI forbids nesting a Group(Grid()) inside another Group, so couplers
must be at the server level (not inside the device SubScreen) to appear
inline.  Their FastCS PV paths are unchanged — path resolution is
independent of the FastCS parent-child hierarchy.

Multi-segment-path couplers (e.g. BL04I-EA-CATIO-01:ETH01:E1RIO01)
are unaffected and remain as SubScreen children of the device.
- Pin fastcs[epics]==0.15.0b4 (was >=0.15.0b3) and pvi==0.14.0b1 (was
  unpinned at 0.12.0); update uv.lock accordingly.
- Remove 'title: CATio Demo' from fastcs.yaml; update schema.json so
  EpicsGUIOptions.title defaults to null rather than a required string,
  matching the upstream fastcs change.
- Fix docstring and unit-test examples: BL04I-EA-ERIO-01 →
  BL04I-EA-E1RIO-01 (correct real-world device name).
- Update fastcs-epics-ioc.md to reference CATioNameMappings templates
  instead of the removed ecat_name / get_type_name() implementation
  details.
- Remove verbose per-channel AI Settings / Internal data / Vendor data
  CoE object groups from EP4374 in terminal_types.yaml to reduce GUI
  screen clutter.
- Independently refresh fastcs-epics-ioc.md: correct class names
  (CATioDeviceController → EtherCATMasterController), add coupler-hoisting
  explanation, expand device/terminal attribute tables, and document the
  three IO handler classes and CATioServerControllerOptions sub-dataclasses.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adopts a YAML-driven naming scheme for EtherCAT controllers and reworks PVI screen layout so that single-segment coupler/box controllers are hoisted to the server root and rendered inline (working around a PVI Group-nesting restriction). It pins new beta versions of fastcs and pvi, refreshes the EPICS IOC explanation doc, allows a null EpicsGUIOptions.title, and trims verbose EP4374 CoE objects to reduce screen clutter.

Changes:

  • Introduce CATioNameMappings dataclass with device_prefix / node_prefix / module_prefix templates, validated at construction; replace hardcoded get_type_name() methods with template-driven _resolve_controller_name_and_path.
  • Hoist single-segment coupler/box controllers to the server with GroupLayout.INLINE; add Box IONodeType and recognize E[PQR]P?\d{4} boxes in chain traversal.
  • Pin fastcs[epics]==0.15.0b4, pvi==0.14.0b1; expand and correct the fastcs-epics-ioc.md explanation document.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pyproject.toml / uv.lock Pin fastcs/pvi to specific beta versions.
src/fastcs_catio/catio_controller.py New name-template machinery, hoisting logic, path injection into subcontrollers.
src/fastcs_catio/catio_hardware.py Rename SUPPORTED_CONTROLLERSSUPPORTED_DEVICE_CONTROLLERS, drop terminal entries from the device map.
src/fastcs_catio/client.py Recognize EtherCAT box terminals and assign them node positions.
src/fastcs_catio/devices.py Add Box node type, remove get_type_name() methods, convert ChainLocation to NamedTuple.
src/fastcs_catio/utils.py Add make_node_prefix helper (currently unused) and beamline regex.
src/fastcs_catio/main.py Pass path to server controller constructor.
src/fastcs_catio/fastcs.yaml Switch to absolute id, fully-qualified controller type, add name_mappings.
src/fastcs_catio/schema.json Schema for new CATioNameMappings, nullable title, transports tightening.
src/catio_terminals/terminals/terminal_types.yaml Drop bulky EP4374 per-channel AI/AO CoE objects.
tests/test_catio_units.py New TestControllerNameMappings suite; update hyphen-trim fixtures and remove tests for deleted methods.
docs/explanations/fastcs-epics-ioc.md Documentation refresh aligned with new naming + hoisting behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/fastcs_catio/utils.py Outdated
Comment on lines +320 to +350
def make_node_prefix(parent_path: list[str], substitution: str) -> list[str]:
"""
Create a node prefix for the CATio controller based on the parent path.
If the server prefix matches the beamline pattern, the substitution string is used \
to create a new prefix based on that pattern (e.g. "BL04I-EA-E1RIO-01").
Otherwise, the substitution is simply appended to the parent path \
(e.g. "BL04I-EA-CATIO-01:ETH1:E1RIO1").

:param parent_path: the parent path provided by the user
:param substitution: the substitution string to use if the server prefix matches \
the beamline pattern

:returns: a list of strings representing the node path for the controller
"""
server_prefix = parent_path[0]
if _BEAMLINE_NAME_RE.match(server_prefix):
formatted_sub = "-".join(
p.zfill(2) if p.isdigit() else p
for p in [
"".join(x) for _, x in itertools.groupby(substitution, key=str.isdigit)
]
)
return [
re.sub(
_BEAMLINE_NAME_RE,
r"\1-\2-" + formatted_sub,
server_prefix,
)
]

return parent_path + [substitution]
Comment thread src/fastcs_catio/catio_hardware.py Outdated
Comment on lines +1180 to +1184
Log the list of I/O ETherCAT devices currently supported by the CATio driver.
"""
logger.info(
"List of I/O hardware currently supported by the CATio driver:\n "
+ f"{list(SUPPORTED_CONTROLLERS.keys())}"
"List of I/O ETherCAT devices currently supported by the CATio driver:\n "
+ f"{list(SUPPORTED_DEVICE_CONTROLLERS.keys())}"
Comment thread src/fastcs_catio/catio_hardware.py Outdated
Comment on lines 1178 to 1185
def get_supported_devices(self) -> None:
"""
Log the list of I/O hardware currently supported by the CATio driver.
Log the list of I/O ETherCAT devices currently supported by the CATio driver.
"""
logger.info(
"List of I/O hardware currently supported by the CATio driver:\n "
+ f"{list(SUPPORTED_CONTROLLERS.keys())}"
"List of I/O ETherCAT devices currently supported by the CATio driver:\n "
+ f"{list(SUPPORTED_DEVICE_CONTROLLERS.keys())}"
)
Comment thread src/fastcs_catio/catio_controller.py Outdated

logger.verbose(
f"{len(subcontrollers)} subcontrollers were found for {node.data.name}."
f"{len(subcontrollers)} subcontrollers were found for "
self,
options: CATioServerControllerOptions,
*,
path=None,
@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 93.02326% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.63%. Comparing base (c36e26b) to head (5e8cf2b).

Files with missing lines Patch % Lines
src/fastcs_catio/catio_controller.py 92.53% 5 Missing ⚠️
src/fastcs_catio/__main__.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #63      +/-   ##
==========================================
+ Coverage   75.26%   75.63%   +0.37%     
==========================================
  Files          19       19              
  Lines        4096     4146      +50     
==========================================
+ Hits         3083     3136      +53     
+ Misses       1013     1010       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The previous defaults (ETH{}, E1RIO{}, MOD{}) rendered to bare single-segment
names, causing devices, couplers and modules to appear at the top level of the
PV tree instead of being nested under their parents.

New defaults:

  device_prefix  "{id}:ETH{:02d}"               → [<root>, ETH01]
  node_prefix    "{device_prefix}:E1RIO{:02d}"   → [<root>, ETH01, E1RIO01]
  module_prefix  "{node_prefix}:MOD{:02d}"        → [<root>, ETH01, E1RIO01, MOD03]

Tests updated to assert the correct nested paths and confirm that the old
single-segment behaviour no longer applies by default.
Expose device_prefix, node_prefix and module_prefix as --device-prefix,
--node-prefix and --module-prefix options on the 'ioc' command, mirroring
the name_mappings block already available in the YAML-driven 'run' command.

Also adds docs/how-to/run-ioc.md documenting both launch modes and the PV
name template system.
Underscores in EPICS PV name components produce invalid PV names.
Two validation points added:

- CATioNameMappings.__post_init__: rejects underscores in template
  literal text at construction time (before any hardware is touched).
- CATioServerController._render: rejects underscores in the fully
  rendered result, catching cases where a substituted value such as
  {id} contributes an underscore at runtime.

Tests added for both cases. docs/how-to/run-ioc.md updated with a
warning explaining the restriction and pointing to hyphens as the
correct separator.
- Add '# pragma: no cover' to two unreachable defensive guards in
  CATioServerController (_resolve_controller_name_and_path else-branch
  and the empty-path fallback) and to CLI-only runtime branches in
  __main__.py (terminal_defs block and screens_dir fallback).
- Add TestGetSupportedDevices: verifies get_supported_devices() is
  callable with no arguments (fixes the stray 'self' parameter removed
  in the same session).
- Add TestEtherCATChainCategorisation: unit tests for
  _get_ethercat_chains covering the Box-type branch (EP/EQ/ER
  terminals), the Coupler branch (EK1100), and plain slaves — covering
  the previously uncovered client.py lines 1446-1449.
- Remove dead make_node_prefix() from utils.py together with its
  exclusive dependencies (_BEAMLINE_NAME_RE, import itertools).
- Fix CATioServerController.__init__ path parameter: remove type
  annotation from the keyword-only 'path=' argument so FastCS's
  _launch() introspection correctly identifies CATioServerControllerOptions
  as the options type instead of raising a LaunchError on startup.
In some CI environments (e.g. GitHub Actions with FORCE_COLOR set),
Rich/Typer emits colour codes that split option names such as
'--device-prefix' into separate escape sequences, causing plain-string
membership tests to fail.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants