Learn how to read and display network and node status diagnostics from an EtherCAT network. The application creates a MotionController object, checks for errors, and displays overall network status along with per-node AL Status, status codes, and CoE emergency messages.
""" EtherCAT network status diagnostic utility for real-time monitoring.
This utility displays the overall network status from the MotionController and
the individual status of each network node. It provides visibility into:
Network Status (MotionController level):
- ActiveNodeCount: Number of nodes actively responding on the network
- AlStatus: Logical OR of AL Status registers from all nodes (0x0130)
- MissedCyclicFrameCount: Cumulative count of missed cyclic EtherCAT frames
- CyclicFramePeriodUs: Most recent cyclic frame period in microseconds
- SynchronizationErrorNs: Distributed Clock synchronization error in nanoseconds
Node Status (per RapidCodeNetworkNode):
- AlStatus: EtherCAT AL Status register (0x0130) value for this specific node
- AlStatusCode: EtherCAT AL Status Code register value (error details)
- CoeEmergencyMessage: CANopen over EtherCAT emergency message (if any)
- CoeEmergencyMessageNetworkCounter: Network counter when emergency was received
AL Status Register (0x0130) Decoding:
Bits 0-3 indicate Device State Machine state:
- 1 = Init (initialization)
- 2 = PreOp (pre-operational, mailbox communication only)
- 3 = Bootstrap (firmware update mode)
- 4 = SafeOp (safe-operational, inputs active, outputs safe)
- 8 = Operational (full operation, inputs and outputs active)
Bit 4 is the Error Flag - when set, check AlStatusCode for details.
When to Use This Diagnostic Tool:
- Verify all nodes are in OPERATIONAL state after network startup
- Check for nodes reporting errors (Bit 4 set in AlStatus)
- Monitor missed cyclic frame counts for network quality issues
- Check DC synchronization error for timing accuracy
- Diagnose CoE emergency messages from drives or devices
Practical Examples of Output to Diagnose Network Issues:
Example 1: A cable or power failure.
- Active Node Count: 2 (7 discovered)
Example 2: A node fell out of Operational state. Look up 1A in the manual.
- 0 | Mitsubishi MR-J5-TM | 0x14 (SAFEOP+ERR) | 0x001A | 0x000000000000 | 0
"""
from _imports import RapidCode, helpers
AL_STATUS_STATES = {
0x01: "INIT",
0x02: "PREOP",
0x03: "BOOT",
0x04: "SAFEOP",
0x08: "OP",
}
ERROR_SUFFIX = "+ERR"
MAX_STATE_WIDTH = max(len(s) for s in AL_STATUS_STATES.values()) + len(ERROR_SUFFIX)
AL_STATUS_COL_WIDTH = 7 + MAX_STATE_WIDTH
def decode_al_status(al_status: int) -> str:
"""Decode AL Status register to human-readable state (single node)."""
state = al_status & 0x0F
error_flag = (al_status & 0x10) != 0
state_name = AL_STATUS_STATES.get(state, "?")
if error_flag:
state_name += ERROR_SUFFIX
return state_name
def decode_al_status_ored(al_status: int) -> str:
"""Decode ORed AL Status (network-level, multiple nodes).
When AL Status values from multiple nodes are ORed together,
we check individual bits. Note: BOOT(3) = INIT(1)|PREOP(2), so
we can't distinguish BOOT from INIT+PREOP when ORed.
"""
state_bits = al_status & 0x0F
error_flag = (al_status & 0x10) != 0
states_present = []
if state_bits & 0x01:
states_present.append("INIT")
if state_bits & 0x02:
states_present.append("PREOP")
if state_bits & 0x04:
states_present.append("SAFEOP")
if state_bits & 0x08:
states_present.append("OP")
if states_present:
state_name = "|".join(states_present)
else:
state_name = "NONE"
if error_flag:
state_name += ERROR_SUFFIX
return state_name
def display_network_status(controller: RapidCode.MotionController):
"""Display the overall network status and per-node status."""
network_status = controller.NetworkStatusGet()
nodes = []
max_name_len = 10
for i in range(controller.NetworkNodeCountGet()):
node = controller.NetworkNodeGet(i)
helpers.check_errors(node)
if node.Exists():
nodes.append(node)
max_name_len = max(max_name_len, len(node.NameGet()))
width = 80
print("\n" + "=" * width)
print("NETWORK STATUS")
print("=" * width)
network_state = helpers.get_enum_name("RSINetworkState_RSINetworkState", controller.NetworkStateGet())
al_status_str = f"0x{network_status.AlStatus:02X} ({decode_al_status_ored(network_status.AlStatus)})"
sync_sign = "+" if network_status.SynchronizationErrorNs >= 0 else ""
print(f" Network State: {network_state}")
print(f" Active Node Count: {network_status.ActiveNodeCount} ({controller.NetworkNodeCountGet()} discovered)")
print(f" AL Status: {al_status_str}")
print(f" Missed Cyclic Frames: {network_status.MissedCyclicFrameCount}")
print(f" Cyclic Frame Period: {network_status.CyclicFramePeriodUs} us")
print(f" Synchronization Error: {sync_sign}{network_status.SynchronizationErrorNs} ns")
print("\n" + "=" * width)
print("NODE STATUS")
print("=" * width)
header = f"{'Node':>4} | {'Name':<{max_name_len}} | {'AL Status':^{AL_STATUS_COL_WIDTH}} | {'AL Code':^8} | {'CoE Emergency':^16} | {'Counter':>8}"
print(header)
print("-" * len(header))
for node in nodes:
node_index = node.NumberGet()
node_name = node.NameGet()[:max_name_len]
status = node.StatusGet()
al_status_str = f"0x{status.AlStatus:02X} ({decode_al_status(status.AlStatus):^{MAX_STATE_WIDTH}})"
al_code_str = f"0x{status.AlStatusCode:04X}"
coe_emergency_str = f"0x{status.CoeEmergencyMessage:012X}"
counter_str = f"{status.CoeEmergencyMessageNetworkCounter:8d}"
print(f"{node_index:>4} | {node_name:<{max_name_len}} | {al_status_str:^{AL_STATUS_COL_WIDTH}} | {al_code_str:^8} | {coe_emergency_str:^16} | {counter_str}")
print("=" * width)
print("Network Status Diagnostic")
print("-" * 40)
creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
helpers.check_errors(motion_controller)
print(f"RapidCode Version: {motion_controller.VersionGet()}")
print(f"Serial Number: {motion_controller.SerialNumberGet()}")
display_network_status(motion_controller)
motion_controller.Delete()