APIs, concepts, guides, and more
NodeErrorCounts.py
1
54
55
56
57import os
58import platform
59import sys
60from typing import Dict
61
62# Import the ntx.dll from INtime
63if platform.system() == "Windows":
64 os.add_dll_directory("c:\\Program Files (x86)\\INtime\\bin") # ntx.dll
65
66import RapidCodeHelpers as helpers
67
68# Import the RapidCodePython.py file
69rapidcode_dir = helpers.find_rapid_code_directory()
70sys.path.append(rapidcode_dir)
71import RapidCodePython as RapidCode
72
73# CONSTANTS (from https://www.ethercat.org/download/documents/EtherCAT_Diagnosis_For_Users.pdf pages 19-25)
74PORT_0_FRAME_ERROR_COUNTER_REG = 0x300
75PORT_0_RX_ERROR_COUNTER_REG = 0x301
76PORT_1_FRAME_ERROR_COUNTER_REG = 0x302
77PORT_1_RX_ERROR_COUNTER_REG = 0x303
78PORT_2_FRAME_ERROR_COUNTER_REG = 0x304
79PORT_2_RX_ERROR_COUNTER_REG = 0x305
80PORT_3_FRAME_ERROR_COUNTER_REG = 0x306
81PORT_3_RX_ERROR_COUNTER_REG = 0x307
82ERROR_COUNT_SUBINDEX = 0
83ERROR_COUNT_BYTES = 1
84
85
86def read_node_rx_error_counters(node) -> Dict[str, int]:
87 """Read all RX error counters from a network node."""
88 error_counts = {}
89
90 try:
91 # PORT 0
92 error_counts['Port0_FrameErrors'] = node.ServiceChannelRead(PORT_0_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
93 error_counts['Port0_RxErrors'] = node.ServiceChannelRead(PORT_0_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
94
95 # PORT 1
96 error_counts['Port1_FrameErrors'] = node.ServiceChannelRead(PORT_1_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
97 error_counts['Port1_RxErrors'] = node.ServiceChannelRead(PORT_1_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
98
99 # PORT 2
100 error_counts['Port2_FrameErrors'] = node.ServiceChannelRead(PORT_2_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
101 error_counts['Port2_RxErrors'] = node.ServiceChannelRead(PORT_2_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
102
103 # PORT 3
104 error_counts['Port3_FrameErrors'] = node.ServiceChannelRead(PORT_3_FRAME_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
105 error_counts['Port3_RxErrors'] = node.ServiceChannelRead(PORT_3_RX_ERROR_COUNTER_REG, ERROR_COUNT_SUBINDEX, ERROR_COUNT_BYTES)
106
107 except Exception as ex:
108 print(f"Error reading node {node.NumberGet()}: {ex}")
109 # Return zeros if there's an error
110 for port in range(4):
111 error_counts[f'Port{port}_FrameErrors'] = 0
112 error_counts[f'Port{port}_RxErrors'] = 0
113
114 return error_counts
115
116
117def display_network_error_diagnostics(controller: RapidCode.MotionController):
118 """Display RX error diagnostic counters for all nodes in the network."""
119
120 # Collect all nodes first to determine max name length
121 nodes = []
122 max_node_name_length = 10 # minimum width
123 for i in range(controller.NetworkNodeCountGet()):
124 io = controller.IOGet(i)
125 helpers.check_errors(io)
126 if io.NetworkNode.Exists():
127 nodes.append(io.NetworkNode)
128 max_node_name_length = max(max_node_name_length, len(io.NetworkNode.NameGet()))
129
130 # Print header first to measure actual line length
131 header_line = f"{'Node':^4} | {'Name':^{max_node_name_length}} | {'Port 0':^12} | {'Port 1':^12} | {'Port 2':^12} | {'Port 3':^12}"
132 subheader_line = f"{'':^4} | {'':^{max_node_name_length}} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12} | {'Frame RX':^12}"
133
134 # Use actual header length for separator lines
135 total_width = len(header_line)
136
137 print("\n" + "="*total_width)
138 print("ETHERCAT RX ERROR COUNTERS")
139 print(f"Network: {controller.NetworkNodeCountGet()} Nodes")
140 print("="*total_width)
141
142 # Print header
143 print(header_line)
144 print(subheader_line)
145 print("-"*total_width)
146
147 # Print error counts for each node
148 for node in nodes:
149 node_index = node.NumberGet()
150 node_name = node.NameGet()[:max_node_name_length] # Truncate if needed
151
152 # Read error counters
153 error_counts = read_node_rx_error_counters(node)
154
155 # Format each port's errors - properly spaced (5+2+5 = 12 chars total)
156 ports = []
157 for port_num in range(4):
158 frame_errors = error_counts[f'Port{port_num}_FrameErrors']
159 rx_errors = error_counts[f'Port{port_num}_RxErrors']
160 ports.append(f"{frame_errors:5d} {rx_errors:5d}")
161
162 print(f"{node_index:^4} | {node_name:<{max_node_name_length}} | {ports[0]:^12} | {ports[1]:^12} | {ports[2]:^12} | {ports[3]:^12}")
163
164 print("="*total_width)
165
166
167def main():
168 print("EtherCAT RX Error Counter Diagnostics")
169 print("-"*40)
170
171 # Create motion controller
172 creation_params: RapidCode.CreationParameters = helpers.get_creation_parameters()
173 motion_controller: RapidCode.MotionController = RapidCode.MotionController.Create(creation_params)
174
175 # Check for errors
176 print(f"MotionController creation error count: {motion_controller.ErrorLogCountGet()}")
177 helpers.check_errors(motion_controller)
178
179 # Print version info
180 print(f"RapidCode Version: {motion_controller.VersionGet()}")
181 print(f"Serial Number: {motion_controller.SerialNumberGet()}")
182
183 # Check if network is operational
184 if motion_controller.NetworkStateGet() == RapidCode.RSINetworkState_RSINetworkStateOPERATIONAL:
185 display_network_error_diagnostics(motion_controller)
186 else:
187 print("\nNetwork is not in OPERATIONAL state. Current state:", motion_controller.NetworkStateGet())
188 print("Cannot read node error counts.")
189
190 # Clean up
191 motion_controller.Delete()
192
193
194
195if __name__ == '__main__':
196 main()