3.3.4. Error Handling
Two types of errors can be distinguished:
Client-side Errors: Errors that occur in the Python client - e.g., logic errors in the Python code or failed network connection.
Server-side Errors: Errors that occur in the voraus EtherCAT Master application - e.g., misconfiguration or connection problems on the EtherCAT bus.
The status of the EtherCAT master application (server) can be obtained by ethercat.get_system_info(). For more
details related to the server-side error handling, refer to the voraus EtherCAT Master
documentation. Client-side errors are typically handled as Python exceptions. Log messages from the master application
are also available through the Python logger: logging.getLogger("voraus_ecat.master").
The available error handling options are best demonstrated with an example.
3.3.4.1. Example Code
Error Handling Example
1# pylint: disable=duplicate-code
2"""A very simple example for error handling."""
3
4import logging
5import time
6from os import environ
7
8from fieldbus import Inputs, Outputs
9
10from voraus_ecat import EtherCAT, State, SystemInfo, SystemState
11from voraus_ecat.exceptions import EtherCATError, PDOError
12
13# create a simple application logger
14_logger = logging.getLogger("application")
15logging.getLogger().addHandler(logging.StreamHandler())
16
17ERROR_TRANSITION_TIMEOUT = 2.0
18STATE_TIMEOUT = 10.0
19NO_FAULTS_TIMEOUT_SECONDS = 2.0
20
21
22def wait_for_no_faults(ecat: EtherCAT, timeout: float) -> bool:
23 """Waits until the master detects no communication faults anymore and returns whether it does."""
24 deadline = time.monotonic() + timeout
25 while ecat.get_cyclic_fault_indicator() > 0:
26 if time.monotonic() > deadline:
27 return False
28 time.sleep(0.1)
29 return True
30
31
32def try_to_resolve_error(ecat: EtherCAT, info: SystemInfo) -> None:
33 """A simple error reaction that resets the error and brings the master back to OP."""
34 _logger.info(f"System State: {info.state.name}")
35 _logger.info(f"Error: {info.error_message} (#{info.error_number})")
36
37 if info.state == SystemState.ERR_TRANS:
38 # The master keeps the EtherCAT communication active for a configured time period. This is the time to bring
39 # the devices into a safe state.
40 _logger.info("Waiting for ERROR state...")
41 ecat.wait_for_state(SystemState.ERROR, timeout=ERROR_TRANSITION_TIMEOUT)
42
43 # In the Error state the bus is brought to a safe state.
44 # The master only accepts reset when it detects no communication faults anymore.
45 # Depending on the Error, it might be necessary to first "resolve" the error.
46 # E.g. if Error was entered because the power supply of a device was interrupted, the device is probably in INIT
47 # now, while all other devices are in SAFEOP.
48 # Most of these situations can be resolved by requesting INIT first.
49 _logger.info("Waiting for the communication faults to decay...")
50 if not wait_for_no_faults(ecat, NO_FAULTS_TIMEOUT_SECONDS):
51 _logger.warning(f"Communication faults persist (indicator: {ecat.get_cyclic_fault_indicator()})")
52 _logger.info("Requesting INIT state...")
53 ecat.set_state(State.INIT, timeout=STATE_TIMEOUT)
54 if not wait_for_no_faults(ecat, NO_FAULTS_TIMEOUT_SECONDS):
55 raise TimeoutError(f"Communication faults persist in INIT after {NO_FAULTS_TIMEOUT_SECONDS} seconds.")
56
57 _logger.info("Resetting the error...")
58 ecat.reset()
59
60 _logger.info("Waiting for CONFIG state...")
61 ecat.wait_for_state(SystemState.CONFIG, timeout=STATE_TIMEOUT)
62 # The master does not request an EtherCAT state on its own after the reset.
63 _logger.info("Trying to get back to OP...")
64 ecat.set_op_state(timeout=STATE_TIMEOUT)
65 ecat.wait_for_state(SystemState.ACTIVE, timeout=STATE_TIMEOUT)
66 _logger.info("Ready to go.")
67
68
69if __name__ == "__main__":
70 ethercat = EtherCAT(inputs=Inputs(), outputs=Outputs())
71 url = environ.get("VECAT_URL", "opc.tcp://localhost:4841")
72
73 try:
74 with ethercat.connection(url):
75 # Set the master to operational state.
76 ethercat.set_op_state()
77
78 while True:
79
80 ethercat.read_pdos()
81 # [do something...]
82 ethercat.write_pdos()
83
84 time.sleep(0.1)
85
86 # check for errors
87 system_info = ethercat.get_system_info()
88 if system_info.state in (SystemState.ERR_TRANS, SystemState.ERROR):
89 try_to_resolve_error(ethercat, system_info)
90
91 except EtherCATError as e:
92 # the master rejected a request, e.g. the reset or a state request
93 _logger.error(e)
94 except ConnectionError as e:
95 # connection to EtherCAT master was lost (or never established)
96 _logger.error(f"No connection to EtherCAT master ({url})")
97 except PDOError as e:
98 # This happens if PDO values can not be read (e.g. if master is not in OP state)
99 _logger.error(e)
100 except TimeoutError as e:
101 # a state was not reached in time or communication faults persist, e.g. a device does not come back
102 _logger.error(e)