Skip to content

Commit 56ed224

Browse files
committed
Annotation and fixes
* Comment annotation fixups * Added SdoServer aupload and adownload * Fix missing Network.is_async() uses * Fix workaround in pdo.map.save/asave
1 parent 8c74fdc commit 56ed224

File tree

15 files changed

+201
-61
lines changed

15 files changed

+201
-61
lines changed

README.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ The library supports Python 3.6+.
1111
This library is the asyncio port of CANopen. See below for code example.
1212

1313

14+
Async status
15+
------------
16+
17+
The remaining work for feature complete async implementation:
18+
19+
* Implement :code:`ABlockUploadStream`, :code:`ABlockDownloadStream` and
20+
:code:`ATextIOWrapper` for async in :code:`SdoClient`
21+
22+
* Implement :code:`EcmyConsumer.wait()` for async
23+
24+
* Implement async in :code:`LssMaster``
25+
26+
* Async implementation of :code:`BaseNode402`
27+
28+
* Implement async variant of :code:`Network.add_node`. This will probably also
29+
add need of async variant of :code:`input_from_node` in eds.py
30+
31+
* Update unittests for async
32+
33+
* Update documentation and examples
34+
35+
1436
Features
1537
--------
1638

canopen/emcy.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ def on_emcy(self, can_id, data, timestamp):
3333
code, register, data = EMCY_STRUCT.unpack(data)
3434
entry = EmcyError(code, register, data, timestamp)
3535

36-
with self.emcy_received: # NOTE: Blocking call
36+
# NOTE: Blocking call
37+
with self.emcy_received:
3738
if code & 0xFF00 == 0:
3839
# Error reset
3940
self.active = []
@@ -43,7 +44,8 @@ def on_emcy(self, can_id, data, timestamp):
4344
self.emcy_received.notify_all()
4445

4546
for callback in self.callbacks:
46-
callback(entry) # FIXME: Assert if callback is coroutine?
47+
# FIXME: Assert if callback is a coroutine?
48+
callback(entry)
4749

4850
async def aon_emcy(self, can_id, data, timestamp):
4951
code, register, data = EMCY_STRUCT.unpack(data)
@@ -78,7 +80,8 @@ def reset(self):
7880
self.log = []
7981
self.active = []
8082

81-
# FIXME: Make async implementation
83+
# FIXME: Implement "await" function. (Other name is needed here)
84+
8285
@ensure_not_async # NOTE: Safeguard for accidental async use
8386
def wait(
8487
self, emcy_code: Optional[int] = None, timeout: float = 10
@@ -92,9 +95,11 @@ def wait(
9295
"""
9396
end_time = time.time() + timeout
9497
while True:
95-
with self.emcy_received: # NOTE: Blocking call
98+
# NOTE: Blocking call
99+
with self.emcy_received:
96100
prev_log_size = len(self.log)
97-
self.emcy_received.wait(timeout) # NOTE: Blocking call
101+
# NOTE: Blocking call
102+
self.emcy_received.wait(timeout)
98103
if len(self.log) == prev_log_size:
99104
# Resumed due to timeout
100105
return None

canopen/lss.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ def send_identify_non_configured_remote_slave(self):
249249
message[0] = CS_IDENTIFY_NON_CONFIGURED_REMOTE_SLAVE
250250
self.__send_command(message)
251251

252-
# FIXME: Make async implementation
252+
# FIXME: Make async implementation "afast_scan"
253253
@ensure_not_async # NOTE: Safeguard for accidental async use
254254
def fast_scan(self):
255255
"""This command sends a series of fastscan message
@@ -267,7 +267,8 @@ def fast_scan(self):
267267
lss_next = 0
268268

269269
if self.__send_fast_scan_message(lss_id[0], lss_bit_check, lss_sub, lss_next):
270-
time.sleep(0.01) # NOTE: Blocking call
270+
# NOTE: Blocking call
271+
time.sleep(0.01)
271272
while lss_sub < 4:
272273
lss_bit_check = 32
273274
while lss_bit_check > 0:
@@ -276,13 +277,15 @@ def fast_scan(self):
276277
if not self.__send_fast_scan_message(lss_id[lss_sub], lss_bit_check, lss_sub, lss_next):
277278
lss_id[lss_sub] |= 1<<lss_bit_check
278279

279-
time.sleep(0.01) # NOTE: Blocking call
280+
# NOTE: Blocking call
281+
time.sleep(0.01)
280282

281283
lss_next = (lss_sub + 1) & 3
282284
if not self.__send_fast_scan_message(lss_id[lss_sub], lss_bit_check, lss_sub, lss_next):
283285
return False, None
284286

285-
time.sleep(0.01) # NOTE: Blocking call
287+
# NOTE: Blocking call
288+
time.sleep(0.01)
286289

287290
# Now the next 32 bits will be scanned
288291
lss_sub += 1
@@ -306,7 +309,7 @@ def __send_fast_scan_message(self, id_number, bit_checker, lss_sub, lss_next):
306309

307310
return False
308311

309-
# FIXME: Make async implementation
312+
# FIXME: Make async implementation "__asend_lss_address"
310313
@ensure_not_async # NOTE: Safeguard for accidental async use
311314
def __send_lss_address(self, req_cs, number):
312315
message = bytearray(8)
@@ -316,7 +319,8 @@ def __send_lss_address(self, req_cs, number):
316319
response = self.__send_command(message)
317320
# some device needs these delays between messages
318321
# because it can't handle messages arriving with no delay
319-
time.sleep(0.2) # NOTE: Blocking call
322+
# NOTE: Blocking call
323+
time.sleep(0.2)
320324

321325
return response
322326

@@ -371,7 +375,7 @@ def __send_configure(self, req_cs, value1=0, value2=0):
371375
error_msg = "LSS Error: %d" % error_code
372376
raise LssError(error_msg)
373377

374-
# FIXME: Make async implementation
378+
# FIXME: Make async implementation "__asend_command"
375379
@ensure_not_async # NOTE: Safeguard for accidental async use
376380
def __send_command(self, message):
377381
"""Send a LSS operation code to the network
@@ -392,7 +396,8 @@ def __send_command(self, message):
392396
response = None
393397
if not self.responses.empty():
394398
logger.info("There were unexpected messages in the queue")
395-
self.responses = queue.Queue() # FIXME: Recreating the queue
399+
# FIXME: Recreating the queue
400+
self.responses = queue.Queue()
396401

397402
self.network.send_message(self.LSS_TX_COBID, message)
398403

@@ -402,7 +407,8 @@ def __send_command(self, message):
402407
# Wait for the slave to respond
403408
# TODO check if the response is LSS response message
404409
try:
405-
response = self.responses.get( # NOTE: Blocking call
410+
# NOTE: Blocking call
411+
response = self.responses.get(
406412
block=True, timeout=self.RESPONSE_TIMEOUT)
407413
except queue.Empty:
408414
raise LssError("No LSS response received")
@@ -412,7 +418,8 @@ def __send_command(self, message):
412418
@ensure_not_async # NOTE: Safeguard for accidental async use
413419
def on_message_received(self, can_id, data, timestamp):
414420
# NOTE: Callback. Called from another thread
415-
self.responses.put(bytes(data)) # NOTE: Blocking call
421+
# NOTE: Blocking call
422+
self.responses.put(bytes(data))
416423

417424
async def aon_message_received(self, can_id, data, timestamp):
418425
await self.aresponses.put(bytes(data))

canopen/network.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __init__(
6969
self.lss = LssMaster()
7070
self.lss.network = self
7171

72-
if self.loop:
72+
if self.is_async():
7373
self.subscribe(self.lss.LSS_RX_COBID, self.lss.aon_message_received)
7474
else:
7575
self.subscribe(self.lss.LSS_RX_COBID, self.lss.on_message_received)
@@ -163,6 +163,8 @@ def __enter__(self):
163163
def __exit__(self, type, value, traceback):
164164
self.disconnect()
165165

166+
# FIXME: Implement async "aadd_node"
167+
166168
def add_node(
167169
self,
168170
node: Union[int, RemoteNode, LocalNode],

canopen/nmt.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,16 @@ def __init__(self, node_id: int):
132132
@ensure_not_async # NOTE: Safeguard for accidental async use
133133
def on_heartbeat(self, can_id, data, timestamp):
134134
# NOTE: Callback. Called from another thread unless async
135-
with self.state_update: # NOTE: Blocking call
135+
# NOTE: Blocking call
136+
with self.state_update:
136137
self.timestamp = timestamp
137138
new_state, = struct.unpack_from("B", data)
138139
# Mask out toggle bit
139140
new_state &= 0x7F
140141
logger.debug("Received heartbeat can-id %d, state is %d", can_id, new_state)
141142
for callback in self._callbacks:
142-
callback(new_state) # FIXME: Assert if callback is coroutine?
143+
# FIXME: Assert if callback is coroutine?
144+
callback(new_state)
143145
if new_state == 0:
144146
# Boot-up, will go to PRE-OPERATIONAL automatically
145147
self._state = 127
@@ -181,9 +183,11 @@ def send_command(self, code: int):
181183
@ensure_not_async # NOTE: Safeguard for accidental async use
182184
def wait_for_heartbeat(self, timeout: float = 10):
183185
"""Wait until a heartbeat message is received."""
184-
with self.state_update: # NOTE: Blocking call
186+
# NOTE: Blocking call
187+
with self.state_update:
185188
self._state_received = None
186-
self.state_update.wait(timeout) # NOTE: Blocking call
189+
# NOTE: Blocking call
190+
self.state_update.wait(timeout)
187191
if self._state_received is None:
188192
raise NmtError("No boot-up or heartbeat received")
189193
return self.state
@@ -204,25 +208,27 @@ def wait_for_bootup(self, timeout: float = 10) -> None:
204208
end_time = time.time() + timeout
205209
while True:
206210
now = time.time()
207-
with self.state_update: # NOTE: Blocking call
211+
# NOTE: Blocking call
212+
with self.state_update:
208213
self._state_received = None
209-
self.state_update.wait(end_time - now + 0.1) # NOTE: Blocking call
214+
# NOTE: Blocking call
215+
self.state_update.wait(end_time - now + 0.1)
210216
if now > end_time:
211217
raise NmtError("Timeout waiting for boot-up message")
212218
if self._state_received == 0:
213219
break
214220

215221
async def await_for_bootup(self, timeout: float = 10) -> None:
216222
"""Wait until a boot-up message is received."""
217-
async def wait_for_bootup():
223+
async def _wait_for_bootup():
218224
while True:
219225
async with self.astate_update:
220226
self._state_received = None
221227
await self.astate_update.wait()
222228
if self._state_received == 0:
223229
return
224230
try:
225-
await asyncio.wait_for(wait_for_bootup(), timeout=timeout)
231+
await asyncio.wait_for(_wait_for_bootup(), timeout=timeout)
226232
except asyncio.TimeoutError:
227233
raise NmtError("Timeout waiting for boot-up message")
228234

@@ -282,7 +288,8 @@ def send_command(self, code: int) -> None:
282288
# The heartbeat service should start on the transition
283289
# between INITIALIZING and PRE-OPERATIONAL state
284290
if old_state == 0 and self._state == 127:
285-
heartbeat_time_ms = self._local_node.sdo[0x1017].get_raw() # FIXME: Blocking?
291+
# NOTE: Blocking - OK. Protected in SdoClient
292+
heartbeat_time_ms = self._local_node.sdo[0x1017].get_raw()
286293
self.start_heartbeat(heartbeat_time_ms)
287294
else:
288295
self.update_heartbeat()

canopen/node/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ def __init__(
3030
self.object_dictionary = object_dictionary
3131

3232
self.id = node_id or self.object_dictionary.node_id
33+
34+
# FIXME: Should associate_network() and remove_network() be a part of the base API?

canopen/node/remote.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def add_sdo(self, rx_cobid, tx_cobid):
109109
client = SdoClient(rx_cobid, tx_cobid, self.object_dictionary)
110110
self.sdo_channels.append(client)
111111
if self.network is not None:
112-
if self.network.loop:
112+
if self.network.is_async():
113113
self.network.subscribe(client.tx_cobid, client.aon_response)
114114
else:
115115
self.network.subscribe(client.tx_cobid, client.on_response)
@@ -151,9 +151,11 @@ def __load_configuration_helper(self, index, subindex, name, value):
151151
subindex=subindex,
152152
name=name,
153153
value=value)))
154-
self.sdo[index][subindex].set_raw(value) # FIXME: Blocking?
154+
# NOTE: Blocking - OK. Protected in SdoClient
155+
self.sdo[index][subindex].set_raw(value)
155156
else:
156-
self.sdo[index].set_raw(value) # FIXME: Blocking?
157+
# FIXME: Blocking - OK. Protected in SdoClient
158+
self.sdo[index].set_raw(value)
157159
logger.info(str('SDO [{index:#06x}]: {name}: {value:#06x}'.format(
158160
index=index,
159161
name=name,

canopen/objectdictionary/eds.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def import_eds(source, node_id):
170170
return od
171171

172172

173-
# FIXME: Make async variant
173+
# FIXME: Make async variant "aimport_from_node"
174174
@ensure_not_async # NOTE: Safeguard for accidental async use
175175
def import_from_node(node_id, network):
176176
""" Download the configuration from the remote node

0 commit comments

Comments
 (0)