FastCS EPICS IOC Implementation#
This document explains how CATio uses the FastCS framework to expose EtherCAT devices as EPICS Process Variables (PVs), enabling control system integration.
What is FastCS?#
FastCS is a Python framework for building EPICS Input/Output Controllers (IOCs). It provides a declarative approach where Python class attributes automatically become EPICS PVs. CATio leverages FastCS to create a hierarchical controller structure that mirrors the physical EtherCAT topology.
Key benefits of using FastCS include:
Automatic PV generation: Define Python attributes, get EPICS PVs
Asynchronous I/O: Built on
asynciofor non-blocking operationsHierarchical controllers: Natural mapping to nested hardware structures
Built-in scanning: Periodic polling with configurable intervals
The Controller Hierarchy#
CATio organizes its FastCS controllers in a tree structure that reflects the physical EtherCAT network:
CATioServerController (root)
└── CATioDeviceController (EtherCAT Master)
├── CATioTerminalController (EK1100 Coupler)
│ ├── CATioTerminalController (EL3064 Analog Input)
│ └── CATioTerminalController (EL2008 Digital Output)
└── CATioTerminalController (EK1101 Coupler)
└── ...
This hierarchy is significant because:
Each level corresponds to physical hardware: The server represents the Beckhoff PLC, devices represent EtherCAT Masters, and terminals represent individual I/O modules
Attributes are scoped appropriately: Server-level attributes (like version info) are separate from terminal-level attributes (like input values)
The tree is auto-generated: CATio introspects the hardware and builds controllers dynamically
The Base Controller#
All CATio controllers inherit from CATioController, which extends the FastCS Controller class. The base class provides:
A shared TCP connection to the TwinCAT server (class-level singleton)
Unique identifiers for API dispatch
References to corresponding hardware objects (
IOServer,IODevice, orIOSlave)Attribute grouping for organized PV naming
The CATioController class is defined in catio_controller.py. It includes connection management, attribute registration, and the core interface for communicating with the ADS client.
The Server Controller#
CATioServerController is the root of the hierarchy. It handles:
Route establishment: Uses UDP to register this client with the TwinCAT router
TCP connection: Opens the persistent ADS communication channel
Hardware discovery: Introspects the I/O server to find all devices and terminals
Subcontroller creation: Instantiates the appropriate controller classes for discovered hardware
During initialization, the server controller queries the TwinCAT system and builds the complete controller tree automatically. The key method is register_subcontrollers() which traverses the discovered hardware tree and creates corresponding FastCS controllers.
Device and Terminal Controllers#
CATioDeviceController represents EtherCAT Master devices and exposes attributes like:
Attribute |
Description |
|---|---|
|
Number of terminals connected to this master |
|
Array of EtherCAT state machine values for all terminals |
|
CRC error counters for network diagnostics |
|
Statistics on cyclic and acyclic EtherCAT frames |
CATioTerminalController represents individual I/O modules (EK couplers, EL terminals) with attributes like:
Attribute |
Description |
|---|---|
|
The terminal’s EtherCAT state machine value |
|
Network link health indicator |
|
Accumulated CRC errors for this terminal |
Dynamic Terminal Controllers#
Not all terminals are alike. A digital input module exposes different data than an analog output module. CATio handles this through dynamically generated controller classes based on YAML terminal definitions.
How Dynamic Generation Works#
When CATio discovers a terminal, it calls get_terminal_controller_class(terminal_id) from catio_dynamic_controller.py. This factory function:
Looks up the terminal type (e.g., “EL3064”) in
terminal_types.yamlCreates a controller class dynamically based on the YAML definition
Adds symbol attributes for process data (from
catio_dynamic_symbol.py)Adds CoE attributes for configuration parameters (from
catio_dynamic_coe.py)Caches the class for reuse
The key modules involved:
Module |
Purpose |
|---|---|
|
Factory function and dynamic class creation |
|
Adds PDO symbol attributes to controllers |
|
Adds CoE parameter attributes to controllers |
|
Type conversion between TwinCAT, numpy, and FastCS |
Terminal YAML Definitions#
Each terminal type is defined in src/catio_terminals/terminals/terminal_types.yaml with:
Symbol nodes: Process data accessible via ADS (inputs/outputs)
CoE objects: Configuration parameters with subindices
Selection: Only symbols with
selected: truebecome attributes
For example, a digital input terminal might expose:
Attribute Type |
Source |
Examples |
|---|---|---|
PDO symbols |
|
Input values, status bits |
Runtime symbols |
|
WcState, InfoData |
CoE parameters |
|
Filter settings, calibration |
This approach allows adding new terminal types by editing YAML files without changing Python code. See Terminal YAML Definitions for details on the YAML format.
The Attribute I/O System#
FastCS attributes need to know how to read (and optionally write) their values. CATio implements this through CATioControllerAttributeIO, which bridges FastCS attributes to the ADS client API.
How Attribute Updates Work#
The update flow for a CATio attribute follows these steps:
FastCS calls the
update()method on an attribute’s I/O handler at the configured polling intervalThe I/O handler constructs an API query string based on the attribute name and controller context
The query is sent through
CATioConnectionto theAsyncioADSClientThe client dispatches to the appropriate
get_*methodThe response flows back and the attribute value is updated
This indirection means attributes don’t need to know ADS protocol details - they just specify their name and polling period.
The CATioControllerAttributeIO class in catio_attribute_io.py implements this bridge between FastCS attributes and the ADS client API.
Polling vs Notifications#
CATio supports two update mechanisms:
Polling (default): The I/O handler periodically queries the ADS server. Simple and reliable, but adds latency and network traffic proportional to the number of attributes and polling rate.
Notifications: The ADS server pushes value changes to the client. More efficient for high-frequency data, but requires subscription management and careful buffer handling.
The choice depends on the attribute’s requirements:
Update Mode |
Use Case |
Typical Period |
|---|---|---|
|
Static configuration (device name, version) |
Read at startup only |
Standard polling |
Slowly-changing diagnostics (CRC counters) |
1-2 seconds |
Fast polling |
Process values needing moderate rates |
100-500 ms |
Notifications |
High-frequency acquisition data |
Sub-millisecond |
PV Naming Convention#
CATio generates EPICS PV names that reflect the hardware hierarchy:
<PREFIX>:<Server>:<Device>:<Coupler>:<Terminal>:<Attribute>
For example:
PV Name |
Description |
|---|---|
|
I/O server name |
|
Number of slaves on EtherCAT Master 1 |
|
Value from module 5 on remote I/O node 1 |
|
EtherCAT state of that module |
The naming components come from:
ecat_name: The name configured in TwinCAT (e.g., “Device1”, “Term 5 (EL3064)”)
get_type_name(): A method that converts Beckhoff names to PV-friendly format (e.g., “ETH1”, “RIO1”, “MOD5”)
Lifecycle Management#
CATio controllers follow a specific lifecycle managed by FastCS:
Initialization Phase#
Route addition: UDP message registers this client with the TwinCAT router
TCP connection: Establishes persistent ADS communication channel
Introspection: Queries server for devices, terminals, and symbols
Controller creation: Builds the controller tree matching discovered hardware
Attribute registration: Creates FastCS attributes for each controller
Runtime Phase#
Polling handlers execute at their configured intervals
Notification streams are processed and distributed to attributes
The controller tree remains stable (hot-plugging is not supported)
Shutdown Phase#
Notification cleanup: Unsubscribes from all ADS notifications
Connection closure: Closes the TCP connection gracefully
Route removal: Optionally removes the route from TwinCAT
Testing Considerations#
When writing tests for CATio controllers, you typically need to mock the ADS client layer. The MockADSServer class in mock_server.py simulates TwinCAT responses.
This allows testing controller logic without real hardware by:
Simulating ADS command responses
Providing mock symbol data
Testing notification handling
See Also#
Architecture Overview - High-level system architecture
ADS Client Implementation - Details of the ADS protocol layer
API Decoupling Analysis - API design discussion