1""" EtherCAT RX error counter diagnostic utility for real-time network quality monitoring.
2
3 This utility reads the mandatory EtherCAT RX Error Counters from all network nodes following
4 the EtherCAT Technology Group diagnostic specifications. It monitors communication quality by
5 tracking both Frame Errors (CRC failures at data link layer) and Physical Layer Errors
6 (invalid symbols at hardware level) for each slave port.
7
8 RX Error Counter Types (Per ETG.1600)
9 - Frame Error Counters (0x0300, 0x0302, 0x0304, 0x0306): Increment when CRC check fails on
10 incoming frames, indicating corrupted data that will be discarded. Each slave port checks
11 frames arriving from outside and increments this counter when corruption is detected.
12 - Physical Layer Error Counters (0x0301, 0x0303, 0x0305, 0x0307): Track invalid symbols
13 detected at the physical layer, can occur both within and outside frames. When occurring
14 within frames, usually indicates Frame Errors as well.
15
16 When to Use This Diagnostic Tool
17 - Lost Frame Counter increments at master - investigate with this tool
18 - Working Counter (WKC) errors detected - deeper hardware diagnosis needed
19 - Cable qualification - validate new installations meet industrial Ethernet standards
20 - EMC troubleshooting - sporadic errors indicate electromagnetic interference
21 - Preventive maintenance - periodic monitoring for degrading connections
22
23 Interpreting Error Counter Values
24 - All zeros: Ideal state, network operating perfectly
25 - Sporadic increments (days/weeks apart): Normal due to BER 10^-12 (one bit error per trillion bits transmitted).
26 Industrial Ethernet standard.
27 - Burst errors (seconds/minutes): Actual problem requiring investigation
28 - Systematic increment: Hardware failure (damaged cable, connector oxidation, device fault)
29 - Unbalanced counters: Many Physical Layer but no Frame Errors or vice versa may indicate internal device
30 issue - consider replacing the device
31
32 Diagnostic Procedure (ETG Standard)
33 1. Follow frame path through network topology
34 2. Find FIRST port reporting Frame Error ≠ 0 in path sequence
35 3. Problem location is between this port and previous device
36 4. Check: cable routing near power lines, connector quality, shielding, grounding
37
38 Per ETG specifications, errors immediately after power-on/off are expected and should
39 be ignored. Only errors during steady-state operation indicate actual problems.
40
41 See also: ServiceChannelRead() can read ESC registers (< 0x1000) and SDOs (>= 0x1000)
42"""
43
44from typing import Dict
45import sys
46from _imports import RapidCode, helpers, constants
47
48
49
50PORT_0_FRAME_ERROR_COUNTER_REG = 0x300
51PORT_0_RX_ERROR_COUNTER_REG = 0x301
52PORT_1_FRAME_ERROR_COUNTER_REG = 0x302
53PORT_1_RX_ERROR_COUNTER_REG = 0x303
54PORT_2_FRAME_ERROR_COUNTER_REG = 0x304
55PORT_2_RX_ERROR_COUNTER_REG = 0x305
56PORT_3_FRAME_ERROR_COUNTER_REG = 0x306
57PORT_3_RX_ERROR_COUNTER_REG = 0x307
58ERROR_COUNT_SUBINDEX = 0
59ERROR_COUNT_BYTES = 1
60
61
62
63def read_node_rx_error_counters(node) -> Dict[str, int]:
64 """Read all RX error counters from a network node."""
65 error_counts = {}
66
67 try:
68
69 error_counts['Port0_FrameErrors'] = node.ServiceChannelRead(PORT_0_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
70 error_counts['Port0_RxErrors'] = node.ServiceChannelRead(PORT_0_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
71
72
73 error_counts['Port1_FrameErrors'] = node.ServiceChannelRead(PORT_1_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
74 error_counts['Port1_RxErrors'] = node.ServiceChannelRead(PORT_1_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
75
76
77 error_counts['Port2_FrameErrors'] = node.ServiceChannelRead(PORT_2_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
78 error_counts['Port2_RxErrors'] = node.ServiceChannelRead(PORT_2_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
79
80
81 error_counts['Port3_FrameErrors'] = node.ServiceChannelRead(PORT_3_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
82 error_counts['Port3_RxErrors'] = node.ServiceChannelRead(PORT_3_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
83
84 except Exception as ex:
85 print(f"Error reading node {node.NumberGet()}: {ex}")
86
87 for port in range(4):
88 error_counts[f'Port{port}_FrameErrors'] = 0
89 error_counts[f'Port{port}_RxErrors'] = 0
90
91 return error_counts
92
93def display_network_error_diagnostics(controller: RapidCode.MotionController):
94 """Display RX error diagnostic counters for all nodes in the network."""
95
96
97 nodes = []
98 max_node_name_length = 10
99 for i in range(controller.NetworkNodeCountGet()):
100 io = controller.IOGet(i)
101 helpers.check_errors(io)
102 if io.NetworkNode.Exists():
103 nodes.append(io.NetworkNode)
104 max_node_name_length = max(max_node_name_length, len(io.NetworkNode.NameGet()))
105
106
107 header_line = f"{'Node':^4} | {'Name':^{max_node_name_length}} | {'Port 0':^12} | {'Port 1':^12} | {'Port 2':^12} | {'Port 3':^12}"
108 subheader_line = f"{'':^4} | {'':^{max_node_name_length}} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12}"
109
110
111 total_width = len(header_line)
112
113 print("\n" + "="*total_width)
114 print("ETHERCAT RX ERROR COUNTERS")
115 print(f"Network: {controller.NetworkNodeCountGet()} Nodes")
116 print("="*total_width)
117
118
119 print(header_line)
120 print(subheader_line)
121 print("-"*total_width)
122
123
124 for node in nodes:
125 node_index = node.NumberGet()
126 node_name = node.NameGet()[:max_node_name_length]
127
128
129 error_counts = read_node_rx_error_counters(node)
130
131
132 ports = []
133 for port_num in range(4):
134 frame_errors = error_counts[f'Port{port_num}_FrameErrors']
135 rx_errors = error_counts[f'Port{port_num}_RxErrors']
136 ports.append(f"{frame_errors:5d} {rx_errors:5d}")
137
138 print(f"{node_index:^4} | {node_name:<{max_node_name_length}} | {ports[0]:^12} | {ports[1]:^12} | {ports[2]:^12} | {ports[3]:^12}")
139
140 print("="*total_width)
141
142
143
144print("⬤ NetworkNode Errors")
145
146exit_code = 0
147
148print("EtherCAT RX Error Counter Diagnostics")
149print("-"*40)
150
151
152creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
153motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
154
155
156try:
157 print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
158 helpers.check_errors(motion_controller)
159
160
161 print(f"RapidCode Version: {motion_controller.VersionGet()}")
162 print(f"Serial Number: {motion_controller.SerialNumberGet()}")
163
164
165 if motion_controller.NetworkStateGet() == RapidCode.RSINetworkState_RSINetworkStateOPERATIONAL:
166 display_network_error_diagnostics(motion_controller)
167 else:
168 print("\nNetwork is not in OPERATIONAL state. Current state:", helpers.enum_to_name(motion_controller.NetworkStateGet(), "RSINetworkState"))
169 print("Cannot read node error counts.")
170 exit_code = constants.EXIT_SUCCESS
171except Exception as e:
172 print(f"❌ Error: {e}")
173 exit_code = constants.EXIT_FAILURE
174finally:
175
176 motion_controller.Delete()
177
178sys.exit(exit_code)