Skip to content

Commit 33f7af3

Browse files
authored
DS402: Fix merge errors. (#265)
* ds402: Skip explicit change to SWITCHED ON state. The BaseNode402.homing() method tries to enter state SWITCHED ON upon entry. That's unnecessary, the application should handle these transitions. But more importantly, it actually fails in many cases, namely if the previous state is SWITCH ON DISABLED, the default power-up state of most devices. There is an automatic way to reach OPERATION ENABLED over multiple intermediate steps, but the library does not know how to reach SWITCHED ON from any other state than OPERATION ENABLED or READY TO SWITCH ON. In addition, setting the op_mode property will already change to SWITCHED ON only if coming from OPERATION ENABLED (which is usually a good idea to avoid unexpected movement). Note that switching the operation mode to HOMING is actually safe in any power state. * p402: Make HOMING_TIMEOUT_DEFAULT configurable per instance. When the HOMING_TIMEOUT_DEFAULT class attribute is overridden as an object attribute, the argument default value definition in homing() still uses the old value from the class attribute. Check the argument and apply the default value at runtime to pick up the updated default timeout if the argument is omitted. * ds402: Increase delay to check status on homing start. The Statusword is examined immediately after setting the Controlword command to start homing. That is very likely to fail because of the round-trip time until the Statusword is actually updated from a TPDO. To work around that, the delay between each check of the Statusword should be moved before the actual comparison, and its default value increased. Introduce a new constant TIMEOUT_CHECK_HOMING to configure that with a default value of 100 ms. This replaces the previously used INTERVAL_CHECK_STATE which is only 10 ms by default. An even better solution would be to wait for the Statusword to be updated by a received PDO, but that would be much more complex. * p402: Wait for TPDO instead of timed statusword checking. Several methods loop around waiting for some expected statusword value, usually calling time.sleep() with the INTERVAL_CHECK_STATE or INTERVAL_CHECK_HOMING constants. Replace that with a call to check_statusword(), which will only block until the TPDO is received. This allows for possibly shorter delays, because the delay can be cut short when the TPDO arrives in between. But rate limiting the checking loops is still achieved through the blocking behavior of check_statusword(). If no TPDO is configured, the statusword will fall back to checking via SDO, which will also result in periodic checks, but only rate limited by the SDO round-trip time. Anyway this is not a recommended mode of operation, as the warning in _check_statusword_configured() points out. * p402: Use RPDO to set the operation mode if possible. Fall back to the previous behavior using SDO if the relevant object 0x6060 is not mapped to an RPDO. * p402: Check PDO configuration for the Operation Mode objects. Switching operation modes for several drives simultaneously must be done via PDO. There is still a fallback mechanism via SDO, but a warning should be issued when that is about to be used.
1 parent 0d2b4b6 commit 33f7af3

File tree

1 file changed

+13
-7
lines changed

1 file changed

+13
-7
lines changed

canopen/profiles/p402.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ class BaseNode402(RemoteNode):
203203
TIMEOUT_SWITCH_STATE_FINAL = 0.8 # seconds
204204
TIMEOUT_SWITCH_STATE_SINGLE = 0.4 # seconds
205205
TIMEOUT_CHECK_TPDO = 0.2 # seconds
206-
INTERVAL_CHECK_STATE = 0.01 # seconds
207206
TIMEOUT_HOMING_DEFAULT = 30 # seconds
208207

209208
def __init__(self, node_id, object_dictionary):
@@ -222,6 +221,7 @@ def setup_402_state_machine(self, read_pdos=True):
222221
self.setup_pdos(read_pdos)
223222
self._check_controlword_configured()
224223
self._check_statusword_configured()
224+
self._check_op_mode_configured()
225225

226226
def setup_pdos(self, upload=True):
227227
"""Find the relevant PDO configuration to handle the state machine.
@@ -327,18 +327,19 @@ def is_homed(self, restore_op_mode=False):
327327
self.op_mode = previous_op_mode
328328
return homingstatus in ('TARGET REACHED', 'ATTAINED')
329329

330-
def homing(self, timeout=TIMEOUT_HOMING_DEFAULT, restore_op_mode=False):
330+
def homing(self, timeout=None, restore_op_mode=False):
331331
"""Execute the configured Homing method on the node.
332332
333-
:param int timeout: Timeout value (default: 30).
333+
:param int timeout: Timeout value (default: 30, zero to disable).
334334
:param bool restore_op_mode:
335335
Switch back to the previous operation mode after homing (default: no).
336336
:return: If the homing was complete with success.
337337
:rtype: bool
338338
"""
339+
if timeout is None:
340+
timeout = self.TIMEOUT_HOMING_DEFAULT
339341
if restore_op_mode:
340342
previous_op_mode = self.op_mode
341-
self.state = 'SWITCHED ON'
342343
self.op_mode = 'HOMING'
343344
# The homing process will initialize at operation enabled
344345
self.state = 'OPERATION ENABLED'
@@ -353,7 +354,6 @@ def homing(self, timeout=TIMEOUT_HOMING_DEFAULT, restore_op_mode=False):
353354
if homingstatus in ('INTERRUPTED', 'ERROR VELOCITY IS NOT ZERO',
354355
'ERROR VELOCITY IS ZERO'):
355356
raise RuntimeError('Unable to home. Reason: {0}'.format(homingstatus))
356-
time.sleep(self.INTERVAL_CHECK_STATE)
357357
if timeout and time.monotonic() > t:
358358
raise RuntimeError('Unable to home, timeout reached')
359359
logger.info('Homing mode carried out successfully.')
@@ -407,8 +407,14 @@ def op_mode(self, mode):
407407
if not self.is_op_mode_supported(mode):
408408
raise TypeError(
409409
'Operation mode {m} not suppported on node {n}.'.format(n=self.id, m=mode))
410-
# operation mode
411-
self.sdo[0x6060].raw = OperationMode.NAME2CODE[mode]
410+
# Update operation mode in RPDO if possible, fall back to SDO
411+
if 0x6060 in self.rpdo_pointers:
412+
self.rpdo_pointers[0x6060].raw = OperationMode.NAME2CODE[mode]
413+
pdo = self.rpdo_pointers[0x6060].pdo_parent
414+
if not pdo.is_periodic:
415+
pdo.transmit()
416+
else:
417+
self.sdo[0x6060].raw = OperationMode.NAME2CODE[mode]
412418
timeout = time.monotonic() + self.TIMEOUT_SWITCH_OP_MODE
413419
while self.op_mode != mode:
414420
if time.monotonic() > timeout:

0 commit comments

Comments
 (0)