|
8 | 8 | from collections import Mapping
|
9 | 9 | import logging
|
10 | 10 | import binascii
|
| 11 | +import asyncio |
11 | 12 |
|
12 | 13 | if TYPE_CHECKING:
|
13 | 14 | from ..network import Network
|
@@ -59,6 +60,11 @@ def read(self):
|
59 | 60 | for pdo_map in self.map.values():
|
60 | 61 | pdo_map.read()
|
61 | 62 |
|
| 63 | + async def aread(self): |
| 64 | + """Read PDO configuration from node using SDO.""" |
| 65 | + for pdo_map in self.map.values(): |
| 66 | + await pdo_map.aread() |
| 67 | + |
62 | 68 | def save(self):
|
63 | 69 | """Save PDO configuration to node using SDO."""
|
64 | 70 | for pdo_map in self.map.values():
|
@@ -195,7 +201,8 @@ def __init__(self, pdo_node: PdoBase, com_record, map_array):
|
195 | 201 | #: Set explicitly or using the :meth:`start()` method.
|
196 | 202 | self.period: Optional[float] = None
|
197 | 203 | self.callbacks = []
|
198 |
| - self.receive_condition = threading.Condition() |
| 204 | + #self.receive_condition = threading.Condition() # FIXME |
| 205 | + self.receive_condition = asyncio.Condition() |
199 | 206 | self.is_received: bool = False
|
200 | 207 | self._task = None
|
201 | 208 |
|
@@ -296,6 +303,22 @@ def is_periodic(self) -> bool:
|
296 | 303 | # Unknown transmission type, assume non-periodic
|
297 | 304 | return False
|
298 | 305 |
|
| 306 | + async def aon_message(self, can_id, data, timestamp): |
| 307 | + is_transmitting = self._task is not None |
| 308 | + if can_id == self.cob_id and not is_transmitting: |
| 309 | + async with self.receive_condition: |
| 310 | + self.is_received = True |
| 311 | + self.data = data |
| 312 | + if self.timestamp is not None: |
| 313 | + self.period = timestamp - self.timestamp |
| 314 | + self.timestamp = timestamp |
| 315 | + self.receive_condition.notify_all() |
| 316 | + for callback in self.callbacks: |
| 317 | + callback(self) |
| 318 | + |
| 319 | + def on_message_async(self, can_id, data, timestamp): |
| 320 | + asyncio.create_task(self.aon_message(can_id, data, timestamp)) |
| 321 | + |
299 | 322 | def on_message(self, can_id, data, timestamp):
|
300 | 323 | is_transmitting = self._task is not None
|
301 | 324 | if can_id == self.cob_id and not is_transmitting:
|
@@ -367,6 +390,55 @@ def read(self) -> None:
|
367 | 390 |
|
368 | 391 | self.subscribe()
|
369 | 392 |
|
| 393 | + async def aread(self) -> None: |
| 394 | + """Read PDO configuration for this map using SDO.""" |
| 395 | + cob_id = await self.com_record[1].aget_raw() |
| 396 | + self.cob_id = cob_id & 0x1FFFFFFF |
| 397 | + logger.info("COB-ID is 0x%X", self.cob_id) |
| 398 | + self.enabled = cob_id & PDO_NOT_VALID == 0 |
| 399 | + logger.info("PDO is %s", "enabled" if self.enabled else "disabled") |
| 400 | + self.rtr_allowed = cob_id & RTR_NOT_ALLOWED == 0 |
| 401 | + logger.info("RTR is %s", "allowed" if self.rtr_allowed else "not allowed") |
| 402 | + self.trans_type = await self.com_record[2].aget_raw() |
| 403 | + logger.info("Transmission type is %d", self.trans_type) |
| 404 | + if self.trans_type >= 254: |
| 405 | + try: |
| 406 | + self.inhibit_time = await self.com_record[3].aget_raw() |
| 407 | + except (KeyError, SdoAbortedError) as e: |
| 408 | + logger.info("Could not read inhibit time (%s)", e) |
| 409 | + else: |
| 410 | + logger.info("Inhibit time is set to %d ms", self.inhibit_time) |
| 411 | + |
| 412 | + try: |
| 413 | + self.event_timer = await self.com_record[5].aget_raw() |
| 414 | + except (KeyError, SdoAbortedError) as e: |
| 415 | + logger.info("Could not read event timer (%s)", e) |
| 416 | + else: |
| 417 | + logger.info("Event timer is set to %d ms", self.event_timer) |
| 418 | + |
| 419 | + try: |
| 420 | + self.sync_start_value = await self.com_record[6].aget_raw() |
| 421 | + except (KeyError, SdoAbortedError) as e: |
| 422 | + logger.info("Could not read SYNC start value (%s)", e) |
| 423 | + else: |
| 424 | + logger.info("SYNC start value is set to %d ms", self.sync_start_value) |
| 425 | + |
| 426 | + self.clear() |
| 427 | + nof_entries = await self.map_array[0].aget_raw() |
| 428 | + for subindex in range(1, nof_entries + 1): |
| 429 | + value = await self.map_array[subindex].aget_raw() |
| 430 | + index = value >> 16 |
| 431 | + subindex = (value >> 8) & 0xFF |
| 432 | + size = value & 0xFF |
| 433 | + if hasattr(self.pdo_node.node, "curtis_hack") and self.pdo_node.node.curtis_hack: # Curtis HACK: mixed up field order |
| 434 | + index = value & 0xFFFF |
| 435 | + subindex = (value >> 16) & 0xFF |
| 436 | + size = (value >> 24) & 0xFF |
| 437 | + if index and size: |
| 438 | + self.add_variable(index, subindex, size) |
| 439 | + |
| 440 | + self.subscribe() |
| 441 | + |
370 | 442 | def save(self) -> None:
|
371 | 443 | """Save PDO configuration for this map using SDO."""
|
372 | 444 | logger.info("Setting COB-ID 0x%X and temporarily disabling PDO",
|
@@ -433,7 +505,8 @@ def subscribe(self) -> None:
|
433 | 505 | """
|
434 | 506 | if self.enabled:
|
435 | 507 | logger.info("Subscribing to enabled PDO 0x%X on the network", self.cob_id)
|
436 |
| - self.pdo_node.network.subscribe(self.cob_id, self.on_message) |
| 508 | + #self.pdo_node.network.subscribe(self.cob_id, self.on_message) # FIXME |
| 509 | + self.pdo_node.network.subscribe(self.cob_id, self.on_message_async) |
437 | 510 |
|
438 | 511 | def clear(self) -> None:
|
439 | 512 | """Clear all variables from this map."""
|
@@ -532,6 +605,17 @@ def wait_for_reception(self, timeout: float = 10) -> float:
|
532 | 605 | self.receive_condition.wait(timeout)
|
533 | 606 | return self.timestamp if self.is_received else None
|
534 | 607 |
|
| 608 | + async def await_for_reception(self, timeout: float = 10) -> float: |
| 609 | + """Wait for the next transmit PDO. |
| 610 | +
|
| 611 | + :param float timeout: Max time to wait in seconds. |
| 612 | + :return: Timestamp of message received or None if timeout. |
| 613 | + """ |
| 614 | + async with self.receive_condition: |
| 615 | + self.is_received = False |
| 616 | + await self.receive_condition.wait() |
| 617 | + return self.timestamp if self.is_received else None |
| 618 | + |
535 | 619 |
|
536 | 620 | class Variable(variable.Variable):
|
537 | 621 | """One object dictionary variable mapped to a PDO."""
|
@@ -571,6 +655,11 @@ def get_data(self) -> bytes:
|
571 | 655 |
|
572 | 656 | return data
|
573 | 657 |
|
| 658 | + async def aget_data(self) -> bytes: |
| 659 | + # As long as get_data() is not making any IO, it can be called |
| 660 | + # directly with no special async variant |
| 661 | + return self.get_data() |
| 662 | + |
574 | 663 | def set_data(self, data: bytes):
|
575 | 664 | """Set for the given variable the PDO data.
|
576 | 665 |
|
@@ -603,3 +692,8 @@ def set_data(self, data: bytes):
|
603 | 692 | self.pdo_parent.data[byte_offset:byte_offset + len(data)] = data
|
604 | 693 |
|
605 | 694 | self.pdo_parent.update()
|
| 695 | + |
| 696 | + async def aset_data(self, data: bytes): |
| 697 | + # As long as get_data() is not making any IO, it can be called |
| 698 | + # directly with no special async variant |
| 699 | + return self.set_data(data) |
0 commit comments