APIs, concepts, guides, and more
network-status.py
Note
See Network: Status 📜 for a detailed explanation of this sample code.
Warning
This is a sample program to assist in the integration of the RMP motion controller with your application. It may not contain all of the logic and safety features that your application requires. We recommend that you wire an external hardware emergency stop (e-stop) button for safety when using our code sample apps. Doing so will help ensure the safety of you and those around you and will prevent potential injury or damage.

The sample apps assume that the system (network, axes, I/O) are configured prior to running the code featured in the sample app. See the Configuration page for more information.
1""" EtherCAT network status diagnostic utility for real-time monitoring.
2
3 This utility displays the overall network status from the MotionController and
4 the individual status of each network node. It provides visibility into:
5
6 Network Status (MotionController level):
7 - ActiveNodeCount: Number of nodes actively responding on the network
8 - AlStatus: Logical OR of AL Status registers from all nodes (0x0130)
9 - MissedCyclicFrameCount: Cumulative count of missed cyclic EtherCAT frames
10 - CyclicFramePeriodUs: Most recent cyclic frame period in microseconds
11 - SynchronizationErrorNs: Distributed Clock synchronization error in nanoseconds
12
13 Node Status (per RapidCodeNetworkNode):
14 - AlStatus: EtherCAT AL Status register (0x0130) value for this specific node
15 - AlStatusCode: EtherCAT AL Status Code register value (error details)
16 - CoeEmergencyMessage: CANopen over EtherCAT emergency message (if any)
17 - CoeEmergencyMessageNetworkCounter: Network counter when emergency was received
18
19 AL Status Register (0x0130) Decoding:
20 Bits 0-3 indicate Device State Machine state:
21 - 1 = Init (initialization)
22 - 2 = PreOp (pre-operational, mailbox communication only)
23 - 3 = Bootstrap (firmware update mode)
24 - 4 = SafeOp (safe-operational, inputs active, outputs safe)
25 - 8 = Operational (full operation, inputs and outputs active)
26 Bit 4 is the Error Flag - when set, check AlStatusCode for details.
27
28 When to Use This Diagnostic Tool:
29 - Verify all nodes are in OPERATIONAL state after network startup
30 - Check for nodes reporting errors (Bit 4 set in AlStatus)
31 - Monitor missed cyclic frame counts for network quality issues
32 - Check DC synchronization error for timing accuracy
33 - Diagnose CoE emergency messages from drives or devices
34
35 Practical Examples of Output to Diagnose Network Issues:
36 Example 1: A cable or power failure.
37 - Active Node Count: 2 (7 discovered)
38 Example 2: A node fell out of Operational state. Look up 1A in the manual.
39 - 0 | Mitsubishi MR-J5-TM | 0x14 (SAFEOP+ERR) | 0x001A | 0x000000000000 | 0
40"""
41
42from _imports import RapidCode, helpers
43
44
45# AL Status state decoding (bits 0-3)
46AL_STATUS_STATES = {
47 0x01: "INIT",
48 0x02: "PREOP",
49 0x03: "BOOT",
50 0x04: "SAFEOP",
51 0x08: "OP",
52}
53ERROR_SUFFIX = "+ERR"
54
55# Calculate formatting widths from actual data
56MAX_STATE_WIDTH = max(len(s) for s in AL_STATUS_STATES.values()) + len(ERROR_SUFFIX) # e.g., "SAFEOP" + "+ERR" = 10
57AL_STATUS_COL_WIDTH = 7 + MAX_STATE_WIDTH # "0xXX (" + state + ")" = 7 + state width
58
59
60def decode_al_status(al_status: int) -> str:
61 """Decode AL Status register to human-readable state (single node)."""
62 state = al_status & 0x0F # bits 0-3
63 error_flag = (al_status & 0x10) != 0 # bit 4
64
65 state_name = AL_STATUS_STATES.get(state, "?")
66 if error_flag:
67 state_name += ERROR_SUFFIX
68
69 return state_name
70
71
72def decode_al_status_ored(al_status: int) -> str:
73 """Decode ORed AL Status (network-level, multiple nodes).
74
75 When AL Status values from multiple nodes are ORed together,
76 we check individual bits. Note: BOOT(3) = INIT(1)|PREOP(2), so
77 we can't distinguish BOOT from INIT+PREOP when ORed.
78 """
79 state_bits = al_status & 0x0F # bits 0-3
80 error_flag = (al_status & 0x10) != 0 # bit 4
81
82 # Check individual bits (only single-bit states are unambiguous when ORed)
83 states_present = []
84 if state_bits & 0x01:
85 states_present.append("INIT")
86 if state_bits & 0x02:
87 states_present.append("PREOP")
88 if state_bits & 0x04:
89 states_present.append("SAFEOP")
90 if state_bits & 0x08:
91 states_present.append("OP")
92
93 if states_present:
94 state_name = "|".join(states_present)
95 else:
96 state_name = "NONE"
97
98 if error_flag:
99 state_name += ERROR_SUFFIX
100
101 return state_name
102
103
104def display_network_status(controller: RapidCode.MotionController):
105 """Display the overall network status and per-node status."""
106
107 # Get network status from MotionController
108 network_status = controller.NetworkStatusGet()
109
110 # Collect nodes and determine max name length for formatting
111 nodes = []
112 max_name_len = 10 # minimum width
113 for i in range(controller.NetworkNodeCountGet()):
114 node = controller.NetworkNodeGet(i)
115 helpers.check_errors(node)
116 if node.Exists():
117 nodes.append(node)
118 max_name_len = max(max_name_len, len(node.NameGet()))
119
120 # Print network status header
121 width = 80
122 print("\n" + "=" * width)
123 print("NETWORK STATUS")
124 print("=" * width)
125
126 # Decode and display network-level status
127 network_state = helpers.get_enum_name("RSINetworkState_RSINetworkState", controller.NetworkStateGet())
128 al_status_str = f"0x{network_status.AlStatus:02X} ({decode_al_status_ored(network_status.AlStatus)})"
129 sync_sign = "+" if network_status.SynchronizationErrorNs >= 0 else ""
130
131 print(f" Network State: {network_state}")
132 print(f" Active Node Count: {network_status.ActiveNodeCount} ({controller.NetworkNodeCountGet()} discovered)")
133 print(f" AL Status: {al_status_str}")
134 print(f" Missed Cyclic Frames: {network_status.MissedCyclicFrameCount}")
135 print(f" Cyclic Frame Period: {network_status.CyclicFramePeriodUs} us")
136 print(f" Synchronization Error: {sync_sign}{network_status.SynchronizationErrorNs} ns")
137
138 # Print per-node status
139 print("\n" + "=" * width)
140 print("NODE STATUS")
141 print("=" * width)
142
143 # Header row
144 header = f"{'Node':>4} | {'Name':<{max_name_len}} | {'AL Status':^{AL_STATUS_COL_WIDTH}} | {'AL Code':^8} | {'CoE Emergency':^16} | {'Counter':>8}"
145 print(header)
146 print("-" * len(header))
147
148 # Print each node's status
149 for node in nodes:
150 node_index = node.NumberGet()
151 node_name = node.NameGet()[:max_name_len]
152
153 # Get node status
154 status = node.StatusGet()
155
156 # Format fields
157 al_status_str = f"0x{status.AlStatus:02X} ({decode_al_status(status.AlStatus):^{MAX_STATE_WIDTH}})"
158 al_code_str = f"0x{status.AlStatusCode:04X}"
159 coe_emergency_str = f"0x{status.CoeEmergencyMessage:012X}"
160 counter_str = f"{status.CoeEmergencyMessageNetworkCounter:8d}"
161
162 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}")
163
164 print("=" * width)
165
166
167# MAIN
168print("Network Status Diagnostic")
169print("-" * 40)
170
171# Create motion controller
172creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
173motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
174
175# Check for errors
176print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
177helpers.check_errors(motion_controller)
178
179# Print version info
180print(f"RapidCode Version: {motion_controller.VersionGet()}")
181print(f"Serial Number: {motion_controller.SerialNumberGet()}")
182
183# Display network and node status
184display_network_status(motion_controller)
185
186# Clean up
187motion_controller.Delete()