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
46AL_STATUS_STATES = {
47 0x01: "INIT",
48 0x02: "PREOP",
49 0x03: "BOOT",
50 0x04: "SAFEOP",
51 0x08: "OP",
52}
53ERROR_SUFFIX = "+ERR"
54
55
56MAX_STATE_WIDTH = max(len(s) for s in AL_STATUS_STATES.values()) + len(ERROR_SUFFIX)
57AL_STATUS_COL_WIDTH = 7 + MAX_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
63 error_flag = (al_status & 0x10) != 0
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
80 error_flag = (al_status & 0x10) != 0
81
82
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
108 network_status = controller.NetworkStatusGet()
109
110
111 nodes = []
112 max_name_len = 10
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
121 width = 80
122 print("\n" + "=" * width)
123 print("NETWORK STATUS")
124 print("=" * width)
125
126
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
139 print("\n" + "=" * width)
140 print("NODE STATUS")
141 print("=" * width)
142
143
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
149 for node in nodes:
150 node_index = node.NumberGet()
151 node_name = node.NameGet()[:max_name_len]
152
153
154 status = node.StatusGet()
155
156
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
168print("Network Status Diagnostic")
169print("-" * 40)
170
171
172creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
173motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
174
175
176print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
177helpers.check_errors(motion_controller)
178
179
180print(f"RapidCode Version: {motion_controller.VersionGet()}")
181print(f"Serial Number: {motion_controller.SerialNumberGet()}")
182
183
184display_network_status(motion_controller)
185
186
187motion_controller.Delete()