transport.py
Go to the documentation of this file.
1 #
2 # Copyright (C) 2014-2015 UAVCAN Development Team <uavcan.org>
3 #
4 # This software is distributed under the terms of the MIT License.
5 #
6 # Author: Ben Dyer <ben_dyer@mac.com>
7 # Pavel Kirienko <pavel.kirienko@zubax.com>
8 #
9 
10 from __future__ import division, absolute_import, print_function, unicode_literals
11 import sys
12 import time
13 import math
14 import copy
15 import struct
16 import functools
17 
18 try:
19  import collections.abc # Python 3
20  MutableSequence = collections.abc.MutableSequence
21 except ImportError:
22  import collections # Python 2
23  MutableSequence = collections.MutableSequence
24 
25 import pyuavcan_v0
26 import pyuavcan_v0.dsdl as dsdl
27 import pyuavcan_v0.dsdl.common as common
28 
29 
30 try:
31  long # Python 2
32 except NameError:
33  long = int # Python 3
34 
35 if sys.version_info[0] < 3:
36  bchr = chr
37 else:
38  def bchr(x):
39  return bytes([x])
40 
41 
43  # noinspection PyProtectedMember
44  return obj._type
45 
46 
47 def is_union(obj):
48  if not isinstance(obj, CompoundValue):
49  raise ValueError('Only CompoundValue can be union')
50  # noinspection PyProtectedMember
51  return obj._is_union
52 
53 
55  if not is_union(obj):
56  raise ValueError('Object is not a union')
57  # noinspection PyProtectedMember
58  return obj._union_field
59 
60 
61 def switch_union_field(obj, value):
62  if not is_union(obj):
63  raise ValueError('Object is not a union')
64  # noinspection PyProtectedMember
65  obj._union_field = value
66 
67 
68 def get_fields(obj):
69  if not isinstance(obj, CompoundValue):
70  raise ValueError('Only CompoundValue can have fields')
71  # noinspection PyProtectedMember
72  return obj._fields
73 
74 
75 def get_constants(obj):
76  if not isinstance(obj, CompoundValue):
77  raise ValueError('Only CompoundValue can have constants')
78  # noinspection PyProtectedMember
79  return obj._constants
80 
81 
82 def is_request(obj):
83  # noinspection PyProtectedMember
84  return obj._mode == 'request'
85 
86 
87 def is_response(obj):
88  # noinspection PyProtectedMember
89  return obj._mode == 'response'
90 
91 
93  return "".join(format(c, "08b") for c in s)
94 
95 
97  return bytearray(int(s[i:i + 8], 2) for i in range(0, len(s), 8))
98 
99 
100 def be_from_le_bits(s, bitlen):
101  if len(s) < bitlen:
102  raise ValueError("Not enough bits; need {0} but got {1}".format(bitlen, len(s)))
103  elif len(s) > bitlen:
104  s = s[0:bitlen]
105 
106  return "".join([s[i:i + 8] for i in range(0, len(s), 8)][::-1])
107 
108 
109 def le_from_be_bits(s, bitlen):
110  if len(s) < bitlen:
111  raise ValueError("Not enough bits; need {0} but got {1}".format(bitlen, len(s)))
112  elif len(s) > bitlen:
113  s = s[len(s) - bitlen:]
114 
115  return "".join([s[max(0, i - 8):i] for i in range(len(s), 0, -8)])
116 
117 
118 def format_bits(s):
119  return " ".join(s[i:i + 8] for i in range(0, len(s), 8))
120 
121 
123  return int(math.ceil(math.log(num_elements, 2)))
124 
126  return int(math.ceil(math.log(max_size+1, 2)))
127 
128 def enum_mark_last(iterable, start=0):
129  """
130  Returns a generator over iterable that tells whether the current item is the last one.
131  Usage:
132  >>> iterable = range(10)
133  >>> for index, is_last, item in enum_mark_last(iterable):
134  >>> print(index, item, end='\n' if is_last else ', ')
135  """
136  it = iter(iterable)
137  count = start
138  try:
139  last = next(it)
140  except StopIteration:
141  return
142  for val in it:
143  yield count, False, last
144  last = val
145  count += 1
146  yield count, True, last
147 
148 
149 class Float32IntegerUnion(object):
150  """
151  Yes we've got ourselves a tiny little union here:
152  union FloatIntegerUnion
153  {
154  std::uint32_t u;
155  float f;
156  };
157  This is madness.
158  """
159 
160  def __init__(self, integer=None, floating_point=None):
161  self._bytes = struct.pack("=L", 0)
162  if integer is not None:
163  assert floating_point is None
164  self.u = int(integer)
165  if floating_point is not None:
166  self.f = float(floating_point)
167 
168  @property
169  def f(self):
170  return struct.unpack("=f", self._bytes)[0]
171 
172  @f.setter
173  def f(self, value):
174  assert isinstance(value, float)
175  self._bytes = struct.pack("=f", value)
176 
177  @property
178  def u(self):
179  return struct.unpack("=I", self._bytes)[0]
180 
181  @u.setter
182  def u(self, value):
183  assert isinstance(value, (int, long))
184  self._bytes = struct.pack("=I", value)
185 
186 
187 def f16_from_f32(float32):
188  # Directly translated from libuavcan's implementation in C++
189  f32infty = Float32IntegerUnion(integer=255 << 23)
190  f16infty = Float32IntegerUnion(integer=31 << 23)
191  magic = Float32IntegerUnion(integer=15 << 23)
192  inval = Float32IntegerUnion(floating_point=float32)
193  sign_mask = 0x80000000
194  round_mask = ~0xFFF
195 
196  sign = inval.u & sign_mask
197  inval.u ^= sign
198 
199  if inval.u >= f32infty.u: # Inf or NaN (all exponent bits set)
200  out = 0x7FFF if inval.u > f32infty.u else 0x7C00
201  else:
202  inval.u &= round_mask
203  inval.f *= magic.f
204  inval.u -= round_mask
205  if inval.u > f16infty.u:
206  inval.u = f16infty.u # Clamp to signed infinity if overflowed
207  out = (inval.u >> 13) & 0xFFFF # Take the bits!
208 
209  return out | (sign >> 16) & 0xFFFF
210 
211 
212 def f32_from_f16(float16):
213  # Directly translated from libuavcan's implementation in C++
214  magic = Float32IntegerUnion(integer=(254 - 15) << 23)
215  was_inf_nan = Float32IntegerUnion(integer=(127 + 16) << 23)
216 
217  out = Float32IntegerUnion(integer=(float16 & 0x7FFF) << 13) # exponent/mantissa bits
218  out.f *= magic.f # exponent adjust
219  if out.f >= was_inf_nan.f: # make sure Inf/NaN survive
220  out.u |= 255 << 23
221  out.u |= (float16 & 0x8000) << 16 # sign bit
222 
223  return out.f
224 
225 
226 def cast(value, dtype):
227  if dtype.cast_mode == dsdl.PrimitiveType.CAST_MODE_SATURATED:
228  if value > dtype.value_range[1]:
229  value = dtype.value_range[1]
230  elif value < dtype.value_range[0]:
231  value = dtype.value_range[0]
232  return value
233  elif dtype.cast_mode == dsdl.PrimitiveType.CAST_MODE_TRUNCATED and dtype.kind == dsdl.PrimitiveType.KIND_FLOAT:
234  if not math.isnan(value) and value > dtype.value_range[1]:
235  value = float("+inf")
236  elif not math.isnan(value) and value < dtype.value_range[0]:
237  value = float("-inf")
238  return value
239  elif dtype.cast_mode == dsdl.PrimitiveType.CAST_MODE_TRUNCATED:
240  return value & ((1 << dtype.bitlen) - 1)
241  else:
242  raise ValueError("Invalid cast_mode: " + repr(dtype))
243 
244 
245 class BaseValue(object):
246  # noinspection PyUnusedLocal
247  def __init__(self, _uavcan_type, *_args, **_kwargs):
248  self._type = _uavcan_type
249  self._bits = None
250 
251  def _unpack(self, stream, tao):
252  if self._type.bitlen:
253  self._bits = be_from_le_bits(stream, self._type.bitlen)
254  return stream[self._type.bitlen:]
255  else:
256  return stream
257 
258  def _pack(self, tao):
259  if self._bits:
260  return le_from_be_bits(self._bits, self._type.bitlen)
261  else:
262  return "0" * self._type.bitlen
263 
264 
266  def _unpack(self, stream, tao):
267  return stream[self._type.bitlen:]
268 
269  def _pack(self, tao):
270  return "0" * self._type.bitlen
271 
272 
274  def __init__(self, _uavcan_type, *args, **kwargs):
275  super(PrimitiveValue, self).__init__(_uavcan_type, *args, **kwargs)
276  # Default initialization
277  self.value = 0
278 
279  def __repr__(self):
280  return repr(self.value)
281 
282  @property
283  def value(self):
284  if not self._bits:
285  return None
286 
287  int_value = int(self._bits, 2)
288  if self._type.kind == dsdl.PrimitiveType.KIND_BOOLEAN:
289  return bool(int_value)
290  elif self._type.kind == dsdl.PrimitiveType.KIND_UNSIGNED_INT:
291  return int_value
292  elif self._type.kind == dsdl.PrimitiveType.KIND_SIGNED_INT:
293  if int_value >= (1 << (self._type.bitlen - 1)):
294  int_value = -((1 << self._type.bitlen) - int_value)
295  return int_value
296  elif self._type.kind == dsdl.PrimitiveType.KIND_FLOAT:
297  if self._type.bitlen == 16:
298  return f32_from_f16(int_value)
299  elif self._type.bitlen == 32:
300  return struct.unpack("<f", struct.pack("<L", int_value))[0]
301  elif self._type.bitlen == 64:
302  return struct.unpack("<d", struct.pack("<Q", int_value))[0]
303  else:
304  raise ValueError('Bad float')
305 
306  @value.setter
307  def value(self, new_value):
308  if new_value is None:
309  raise ValueError("Can't serialize a None value")
310  elif self._type.kind == dsdl.PrimitiveType.KIND_BOOLEAN:
311  self._bits = "1" if new_value else "0"
312  elif self._type.kind == dsdl.PrimitiveType.KIND_UNSIGNED_INT:
313  new_value = cast(new_value, self._type)
314  self._bits = format(new_value, "0" + str(self._type.bitlen) + "b")
315  elif self._type.kind == dsdl.PrimitiveType.KIND_SIGNED_INT:
316  new_value = cast(new_value, self._type)
317  if new_value < 0: # Computing two's complement for negatives
318  new_value += 2 ** self._type.bitlen
319  self._bits = format(new_value, "0" + str(self._type.bitlen) + "b")
320  elif self._type.kind == dsdl.PrimitiveType.KIND_FLOAT:
321  new_value = cast(new_value, self._type)
322  if self._type.bitlen == 16:
323  int_value = f16_from_f32(new_value)
324  elif self._type.bitlen == 32:
325  int_value = struct.unpack("<L", struct.pack("<f", new_value))[0]
326  elif self._type.bitlen == 64:
327  int_value = struct.unpack("<Q", struct.pack("<d", new_value))[0]
328  else:
329  raise ValueError('Bad float, no donut')
330  self._bits = format(int_value, "0" + str(self._type.bitlen) + "b")
331 
332 
333 # noinspection PyProtectedMember
335  def __init__(self, _uavcan_type, *args, **kwargs):
336  super(ArrayValue, self).__init__(_uavcan_type, *args, **kwargs)
337 
338  if isinstance(self._type.value_type, dsdl.PrimitiveType):
339  self.__item_ctor = functools.partial(PrimitiveValue, self._type.value_type)
340  elif isinstance(self._type.value_type, dsdl.ArrayType):
341  self.__item_ctor = functools.partial(ArrayValue, self._type.value_type)
342  elif isinstance(self._type.value_type, dsdl.CompoundType):
343  self.__item_ctor = functools.partial(CompoundValue, self._type.value_type)
344 
345  if self._type.mode == dsdl.ArrayType.MODE_STATIC:
346  self.__items = list(self.__item_ctor() for _ in range(self._type.max_size))
347  else:
348  self.__items = []
349 
350  def __repr__(self):
351  return "ArrayValue(type={0!r}, items={1!r})".format(self._type, self.__items)
352 
353  def __str__(self):
354  if self._type.is_string_like:
355  # noinspection PyBroadException
356  try:
357  return self.decode()
358  except Exception:
359  pass
360  return self.__repr__()
361 
362  def __getitem__(self, idx):
363  if isinstance(self.__items[idx], PrimitiveValue):
364  return self.__items[idx].value if self.__items[idx]._bits else 0
365  else:
366  return self.__items[idx]
367 
368  def __setitem__(self, idx, value):
369  if idx >= self._type.max_size:
370  raise IndexError("Index {0} too large (max size {1})".format(idx, self._type.max_size))
371  if isinstance(self._type.value_type, dsdl.PrimitiveType):
372  self.__items[idx].value = value
373  else:
374  self.__items[idx] = value
375 
376  def __delitem__(self, idx):
377  del self.__items[idx]
378 
379  def __len__(self):
380  return len(self.__items)
381 
382  def __eq__(self, other):
383  if isinstance(other, str):
384  return self.decode() == other
385  else:
386  return list(self) == other
387 
388  def clear(self):
389  try:
390  while True:
391  self.pop()
392  except IndexError:
393  pass
394 
395  def new_item(self):
396  return self.__item_ctor()
397 
398  def insert(self, idx, value):
399  if idx >= self._type.max_size:
400  raise IndexError("Index {0} too large (max size {1})".format(idx, self._type.max_size))
401  elif len(self) == self._type.max_size:
402  raise IndexError("Array already full (max size {0})".format(self._type.max_size))
403  if isinstance(self._type.value_type, dsdl.PrimitiveType):
404  new_item = self.__item_ctor()
405  new_item.value = value
406  self.__items.insert(idx, new_item)
407  else:
408  self.__items.insert(idx, value)
409 
410  def _unpack(self, stream, tao):
411  if self._type.mode == dsdl.ArrayType.MODE_STATIC:
412  for _, last, i in enum_mark_last(range(self._type.max_size)):
413  stream = self.__items[i]._unpack(stream, tao and last)
414 
415  elif tao and self._type.value_type.get_min_bitlen() >= 8:
416  del self[:]
417  while len(stream) >= 8:
418  new_item = self.__item_ctor()
419  stream = new_item._unpack(stream, False)
420  self.__items.append(new_item)
421  stream = ''
422 
423  else:
424  del self[:]
425  count_width = array_len_bits_from_max_size(self._type.max_size)
426  count = int(be_from_le_bits(stream[0:count_width], count_width), 2)
427  stream = stream[count_width:]
428  for _, last, i in enum_mark_last(range(count)):
429  new_item = self.__item_ctor()
430  stream = new_item._unpack(stream, tao and last)
431  self.__items.append(new_item)
432 
433  return stream
434 
435  def _pack(self, tao):
436  self.__items = self.__items[:self._type.max_size] # Constrain max len
437 
438  if self._type.mode == dsdl.ArrayType.MODE_STATIC:
439  while len(self) < self._type.max_size: # Constrain min len
440  self.__items.append(self.new_item())
441  return ''.join(i._pack(tao and last) for _, last, i in enum_mark_last(self.__items))
442 
443  elif tao and self._type.value_type.get_min_bitlen() >= 8:
444  return ''.join(i._pack(False) for i in self.__items)
445 
446  else:
447  count_width = array_len_bits_from_max_size(self._type.max_size)
448  count = le_from_be_bits(format(len(self), '0{0:1d}b'.format(count_width)), count_width)
449  return count + ''.join(i._pack(tao and last) for _, last, i in enum_mark_last(self.__items))
450 
451  def from_bytes(self, value):
452  del self[:]
453  for byte in bytearray(value):
454  self.append(byte)
455 
456  def to_bytes(self):
457  return bytes(bytearray(item.value for item in self.__items if item._bits))
458 
459  def encode(self, value, errors='strict'):
460  if not self._type.is_string_like:
461  raise ValueError('encode() can be used only with string-like arrays')
462  del self[:]
463  value = bytearray(value, encoding="utf-8", errors=errors)
464  for byte in value:
465  self.append(byte)
466 
467  def decode(self, encoding="utf-8"):
468  if not self._type.is_string_like:
469  raise ValueError('decode() can be used only with string-like arrays')
470  return bytearray(item.value for item in self.__items if item._bits).decode(encoding)
471 
472 
473 # noinspection PyProtectedMember
475  def __init__(self, _uavcan_type, _mode=None, *args, **kwargs):
476  self.__dict__["_fields"] = collections.OrderedDict()
477  self.__dict__["_constants"] = {}
478  super(CompoundValue, self).__init__(_uavcan_type, *args, **kwargs)
479 
480  if self._type.kind == dsdl.CompoundType.KIND_SERVICE:
481  if _mode == "request":
482  source_fields = self._type.request_fields
483  source_constants = self._type.request_constants
484  self._is_union = self._type.request_union
485  elif _mode == "response":
486  source_fields = self._type.response_fields
487  source_constants = self._type.response_constants
488  self._is_union = self._type.response_union
489  else:
490  raise ValueError("mode must be either 'request' or 'response' for service types")
491  else:
492  if _mode is not None:
493  raise ValueError("mode is not applicable for message types")
494  source_fields = self._type.fields
495  source_constants = self._type.constants
496  self._is_union = self._type.union
497 
498  self._mode = _mode
499  self._union_field = None
500 
501  for constant in source_constants:
502  self._constants[constant.name] = constant.value
503 
504  for idx, field in enumerate(source_fields):
505  if isinstance(field.type, dsdl.VoidType):
506  self._fields["_void_{0}".format(idx)] = VoidValue(field.type)
507  elif isinstance(field.type, dsdl.PrimitiveType):
508  self._fields[field.name] = PrimitiveValue(field.type)
509  elif isinstance(field.type, dsdl.ArrayType):
510  self._fields[field.name] = ArrayValue(field.type)
511  elif isinstance(field.type, dsdl.CompoundType):
512  self._fields[field.name] = CompoundValue(field.type)
513 
514  for name, value in kwargs.items():
515  if name.startswith('_'):
516  raise NameError('%r is not a valid field name' % name)
517  setattr(self, name, value)
518 
519  def __repr__(self):
520  if self._is_union:
521  field = self._union_field or list(self._fields.keys())[0]
522  fields = "{0}={1!r}".format(field, self._fields[field])
523  else:
524  fields = ", ".join("{0}={1!r}".format(f, v) for f, v in self._fields.items() if not f.startswith("_void_"))
525  return "{0}({1})".format(self._type.full_name, fields)
526 
527  def __copy__(self):
528  # http://stackoverflow.com/a/15774013/1007777
529  cls = self.__class__
530  result = cls.__new__(cls)
531  result.__dict__.update(self.__dict__)
532  return result
533 
534  def __deepcopy__(self, memo):
535  # http://stackoverflow.com/a/15774013/1007777
536  cls = self.__class__
537  result = cls.__new__(cls)
538  memo[id(self)] = result
539  for k, v in self.__dict__.items():
540  # noinspection PyArgumentList
541  result.__dict__[k] = copy.deepcopy(v, memo)
542  return result
543 
544  def __getattr__(self, attr):
545  if attr in self._constants:
546  return self._constants[attr]
547  elif attr in self._fields:
548  if self._is_union:
549  if self._union_field and self._union_field != attr:
550  raise AttributeError(attr)
551  else:
552  self._union_field = attr
553 
554  if isinstance(self._fields[attr], PrimitiveValue):
555  return self._fields[attr].value
556  else:
557  return self._fields[attr]
558  else:
559  raise AttributeError(attr)
560 
561  def __setattr__(self, attr, value):
562  if attr in self._constants:
563  raise AttributeError(attr + " is read-only")
564  elif attr in self._fields:
565  if self._is_union:
566  if self._union_field and self._union_field != attr:
567  raise AttributeError(attr)
568  else:
569  self._union_field = attr
570 
571  # noinspection PyProtectedMember
572  attr_type = self._fields[attr]._type
573 
574  if isinstance(attr_type, dsdl.PrimitiveType):
575  self._fields[attr].value = value
576 
577  elif isinstance(attr_type, dsdl.CompoundType):
578  if not isinstance(value, CompoundValue):
579  raise AttributeError('Invalid type of the value, expected CompoundValue, got %r' % type(value))
580  if attr_type.full_name != get_uavcan_data_type(value).full_name:
581  raise AttributeError('Incompatible type of the value, expected %r, got %r' %
582  (attr_type.full_name, get_uavcan_data_type(value).full_name))
583  self._fields[attr] = copy.copy(value)
584 
585  elif isinstance(attr_type, dsdl.ArrayType):
586  self._fields[attr].clear()
587  try:
588  if isinstance(value, str):
589  self._fields[attr].encode(value)
590  else:
591  for item in value:
592  self._fields[attr].append(item)
593  except Exception as ex:
594  # We should be using 'raise from' here, but unfortunately we have to be compatible with 2.7
595  raise AttributeError('Array field could not be constructed from the provided value', ex)
596 
597  else:
598  raise AttributeError(attr + " cannot be set directly")
599  else:
600  super(CompoundValue, self).__setattr__(attr, value)
601 
602  def _unpack(self, stream, tao=True):
603  if self._is_union:
604  tag_len = union_tag_bits_from_num_elements(len(self._fields))
605  self._union_field = list(self._fields.keys())[int(stream[0:tag_len], 2)]
606  stream = self._fields[self._union_field]._unpack(stream[tag_len:], tao)
607  else:
608  for _, last, field in enum_mark_last(self._fields.values()):
609  stream = field._unpack(stream, tao and last)
610  return stream
611 
612  def _pack(self, tao=True):
613  if self._is_union:
614  keys = list(self._fields.keys())
615  field = self._union_field or keys[0]
616  tag = keys.index(field)
617  tag_len = union_tag_bits_from_num_elements(len(self._fields))
618  return format(tag, '0' + str(tag_len) + 'b') + self._fields[field]._pack(tao)
619  else:
620  return ''.join(field._pack(tao and last) for _, last, field in enum_mark_last(self._fields.values()))
621 
622 
623 class Frame(object):
624  def __init__(self, message_id, data, ts_monotonic=None, ts_real=None): # @ReservedAssignment
625  self.message_id = message_id
626  self.bytes = bytearray(data)
627  self.ts_monotonic = ts_monotonic
628  self.ts_real = ts_real
629 
630  @property
631  def transfer_key(self):
632  # The transfer is uniquely identified by the message ID and the 5-bit
633  # Transfer ID contained in the last byte of the frame payload.
634  return self.message_id, (self.bytes[-1] & 0x1F) if self.bytes else None
635 
636  @property
637  def toggle(self):
638  return bool(self.bytes[-1] & 0x20) if self.bytes else False
639 
640  @property
641  def end_of_transfer(self):
642  return bool(self.bytes[-1] & 0x40) if self.bytes else False
643 
644  @property
645  def start_of_transfer(self):
646  return bool(self.bytes[-1] & 0x80) if self.bytes else False
647 
648 
650  pass
651 
652 
653 class Transfer(object):
654  DEFAULT_TRANSFER_PRIORITY = 31
655 
656  def __init__(self,
657  transfer_id=0,
658  source_node_id=0,
659  dest_node_id=None,
660  payload=None,
661  transfer_priority=None,
662  request_not_response=False,
663  service_not_message=False,
664  discriminator=None):
665  self.transfer_priority = transfer_priority if transfer_priority is not None else self.DEFAULT_TRANSFER_PRIORITY
666  self.transfer_id = transfer_id
667  self.source_node_id = source_node_id
668  self.dest_node_id = dest_node_id
669  self.data_type_signature = 0
670  self.request_not_response = request_not_response
671  self.service_not_message = service_not_message
672  self.discriminator = discriminator
673  self.ts_monotonic = None
674  self.ts_real = None
675 
676  if payload:
677  # noinspection PyProtectedMember
678  payload_bits = payload._pack()
679  if len(payload_bits) & 7:
680  payload_bits += "0" * (8 - (len(payload_bits) & 7))
681  self.payload = bytes_from_bits(payload_bits)
682  self.data_type_id = get_uavcan_data_type(payload).default_dtid
683  self.data_type_signature = get_uavcan_data_type(payload).get_data_type_signature()
684  self.data_type_crc = get_uavcan_data_type(payload).base_crc
685  else:
686  self.payload = None
687  self.data_type_id = None
688  self.data_type_signature = None
689  self.data_type_crc = None
690 
691  self.is_complete = True if self.payload else False
692 
693  def __repr__(self):
694  return "Transfer(id={0}, source_node_id={1}, dest_node_id={2}, transfer_priority={3}, payload={4!r})"\
696 
697  @property
698  def message_id(self):
699  # Common fields
700  id_ = (((self.transfer_priority & 0x1F) << 24) |
701  (int(self.service_not_message) << 7) |
702  (self.source_node_id or 0))
703 
704  if self.service_not_message:
705  assert 0 <= self.data_type_id <= 0xFF
706  assert 1 <= self.dest_node_id <= 0x7F
707  # Service frame format
708  id_ |= self.data_type_id << 16
709  id_ |= int(self.request_not_response) << 15
710  id_ |= self.dest_node_id << 8
711  elif self.source_node_id == 0:
712  assert self.dest_node_id is None
713  assert self.discriminator is not None
714  # Anonymous message frame format
715  id_ |= self.discriminator << 10
716  id_ |= (self.data_type_id & 0x3) << 8
717  else:
718  assert 0 <= self.data_type_id <= 0xFFFF
719  # Message frame format
720  id_ |= self.data_type_id << 8
721 
722  return id_
723 
724  @message_id.setter
725  def message_id(self, value):
726  self.transfer_priority = (value >> 24) & 0x1F
727  self.service_not_message = bool(value & 0x80)
728  self.source_node_id = value & 0x7F
729 
730  if self.service_not_message:
731  self.data_type_id = (value >> 16) & 0xFF
732  self.request_not_response = bool(value & 0x8000)
733  self.dest_node_id = (value >> 8) & 0x7F
734  elif self.source_node_id == 0:
735  self.discriminator = (value >> 10) & 0x3FFF
736  self.data_type_id = (value >> 8) & 0x3
737  else:
738  self.data_type_id = (value >> 8) & 0xFFFF
739 
740  def to_frames(self):
741  out_frames = []
742  remaining_payload = self.payload
743 
744  # Prepend the transfer CRC to the payload if the transfer requires
745  # multiple frames
746  if len(remaining_payload) > 7:
747  crc = common.crc16_from_bytes(self.payload,
748  initial=self.data_type_crc)
749  remaining_payload = bytearray([crc & 0xFF, crc >> 8]) + remaining_payload
750 
751  # Generate the frame sequence
752  tail = 0x20 # set toggle bit high so the first frame is emitted with it cleared
753  while True:
754  # Tail byte contains start-of-transfer, end-of-transfer, toggle, and Transfer ID
755  tail = ((0x80 if len(out_frames) == 0 else 0) |
756  (0x40 if len(remaining_payload) <= 7 else 0) |
757  ((tail ^ 0x20) & 0x20) |
758  (self.transfer_id & 0x1F))
759  out_frames.append(Frame(message_id=self.message_id, data=remaining_payload[0:7] + bchr(tail)))
760  remaining_payload = remaining_payload[7:]
761  if not remaining_payload:
762  break
763 
764  return out_frames
765 
766  def from_frames(self, frames):
767  # Initialize transfer timestamps from the first frame
768  self.ts_monotonic = frames[0].ts_monotonic
769  self.ts_real = frames[0].ts_real
770 
771  # Validate the flags in the tail byte
772  expected_toggle = 0
773  expected_transfer_id = frames[0].bytes[-1] & 0x1F
774  for idx, f in enumerate(frames):
775  tail = f.bytes[-1]
776  if (tail & 0x1F) != expected_transfer_id:
777  raise TransferError("Transfer ID {0} incorrect, expected {1}".format(tail & 0x1F, expected_transfer_id))
778  elif idx == 0 and not (tail & 0x80):
779  raise TransferError("Start of transmission not set on frame 0")
780  elif idx > 0 and tail & 0x80:
781  raise TransferError("Start of transmission set unexpectedly on frame {0}".format(idx))
782  elif idx == len(frames) - 1 and not (tail & 0x40):
783  raise TransferError("End of transmission not set on last frame")
784  elif idx < len(frames) - 1 and (tail & 0x40):
785  raise TransferError("End of transmission set unexpectedly on frame {0}".format(idx))
786  elif (tail & 0x20) != expected_toggle:
787  raise TransferError("Toggle bit value {0} incorrect on frame {1}".format(tail & 0x20, idx))
788 
789  expected_toggle ^= 0x20
790 
791  self.transfer_id = expected_transfer_id
792  self.message_id = frames[0].message_id
793  payload_bytes = bytearray(b''.join(bytes(f.bytes[0:-1]) for f in frames))
794 
795  # Find the data type
796  if self.service_not_message:
797  kind = dsdl.CompoundType.KIND_SERVICE
798  else:
799  kind = dsdl.CompoundType.KIND_MESSAGE
800  datatype = pyuavcan_v0.DATATYPES.get((self.data_type_id, kind))
801  if not datatype:
802  raise TransferError("Unrecognised {0} type ID {1}"
803  .format("service" if self.service_not_message else "message", self.data_type_id))
804 
805  # For a multi-frame transfer, validate the CRC and frame indexes
806  if len(frames) > 1:
807  transfer_crc = payload_bytes[0] + (payload_bytes[1] << 8)
808  payload_bytes = payload_bytes[2:]
809  crc = common.crc16_from_bytes(payload_bytes, initial=datatype.base_crc)
810  if crc != transfer_crc:
811  raise TransferError("CRC mismatch: expected {0:x}, got {1:x} for payload {2!r} (DTID {3:d})"
812  .format(crc, transfer_crc, payload_bytes, self.data_type_id))
813 
814  self.data_type_id = datatype.default_dtid
815  self.data_type_signature = datatype.get_data_type_signature()
816  self.data_type_crc = datatype.base_crc
817 
818  if self.service_not_message:
819  self.payload = datatype(_mode="request" if self.request_not_response else "response")
820  else:
821  self.payload = datatype()
822 
823  # noinspection PyProtectedMember
824  self.payload._unpack(bits_from_bytes(payload_bytes))
825 
826  @property
827  def key(self):
828  return self.message_id, self.transfer_id
829 
830  def is_response_to(self, transfer):
831  if (transfer.service_not_message and self.service_not_message and
832  transfer.request_not_response and
833  not self.request_not_response and
834  transfer.dest_node_id == self.source_node_id and
835  transfer.source_node_id == self.dest_node_id and
836  transfer.data_type_id == self.data_type_id and
837  transfer.transfer_id == self.transfer_id):
838  return True
839  else:
840  return False
841 
842 
843 class TransferManager(object):
844  def __init__(self):
847 
848  def receive_frame(self, frame):
849  result = None
850  key = frame.transfer_key
851  if key in self.active_transfers or frame.start_of_transfer:
852  # If the first frame was received, restart this transfer from scratch
853  if frame.start_of_transfer:
854  self.active_transfers[key] = []
855 
856  self.active_transfers[key].append(frame)
857  self.active_transfer_timestamps[key] = time.monotonic()
858 
859  # If the last frame of a transfer was received, return its frames
860  if frame.end_of_transfer:
861  result = self.active_transfers[key]
862  del self.active_transfers[key]
863  del self.active_transfer_timestamps[key]
864 
865  return result
866 
867  def remove_inactive_transfers(self, timeout=1.0):
868  t = time.monotonic()
869  transfer_keys = self.active_transfers.keys()
870  for key in transfer_keys:
871  if t - self.active_transfer_timestamps[key] > timeout:
872  del self.active_transfers[key]
873  del self.active_transfer_timestamps[key]
pyuavcan_v0.transport.Frame.message_id
message_id
Definition: transport.py:625
pyuavcan_v0.transport.ArrayValue._pack
def _pack(self, tao)
Definition: transport.py:435
pyuavcan_v0.dsdl.parser.PrimitiveType
Definition: parser.py:74
pyuavcan_v0.transport.Frame.bytes
bytes
Definition: transport.py:626
pyuavcan_v0.transport.Transfer.source_node_id
source_node_id
Definition: transport.py:659
pyuavcan_v0.transport.CompoundValue
Definition: transport.py:474
pyuavcan_v0.transport.bits_from_bytes
def bits_from_bytes(s)
Definition: transport.py:92
pyuavcan_v0.transport.CompoundValue.__deepcopy__
def __deepcopy__(self, memo)
Definition: transport.py:534
pyuavcan_v0.transport.PrimitiveValue.__repr__
def __repr__(self)
Definition: transport.py:279
pyuavcan_v0.transport.Frame.ts_monotonic
ts_monotonic
Definition: transport.py:627
pyuavcan_v0.transport.ArrayValue.clear
def clear(self)
Definition: transport.py:388
pyuavcan_v0.transport.get_fields
def get_fields(obj)
Definition: transport.py:68
pyuavcan_v0.transport.BaseValue.__init__
def __init__(self, _uavcan_type, *_args, **_kwargs)
Definition: transport.py:247
pyuavcan_v0.transport.VoidValue
Definition: transport.py:265
pyuavcan_v0.transport.Frame.toggle
def toggle(self)
Definition: transport.py:637
pyuavcan_v0.transport.enum_mark_last
def enum_mark_last(iterable, start=0)
Definition: transport.py:128
pyuavcan_v0.transport.ArrayValue._unpack
def _unpack(self, stream, tao)
Definition: transport.py:410
pyuavcan_v0.transport.cast
def cast(value, dtype)
Definition: transport.py:226
pyuavcan_v0.transport.Transfer.ts_monotonic
ts_monotonic
Definition: transport.py:665
pyuavcan_v0.transport.Frame.ts_real
ts_real
Definition: transport.py:628
pyuavcan_v0.transport.Transfer.__init__
def __init__(self, transfer_id=0, source_node_id=0, dest_node_id=None, payload=None, transfer_priority=None, request_not_response=False, service_not_message=False, discriminator=None)
Definition: transport.py:656
pyuavcan_v0.transport.union_tag_bits_from_num_elements
def union_tag_bits_from_num_elements(num_elements)
Definition: transport.py:122
pyuavcan_v0.transport.MutableSequence
MutableSequence
Definition: transport.py:20
pyuavcan_v0.transport.BaseValue._unpack
def _unpack(self, stream, tao)
Definition: transport.py:251
pyuavcan_v0.transport.Transfer.transfer_id
transfer_id
Definition: transport.py:658
pyuavcan_v0.transport.ArrayValue.to_bytes
def to_bytes(self)
Definition: transport.py:456
pyuavcan_v0.transport.VoidValue._unpack
def _unpack(self, stream, tao)
Definition: transport.py:266
pyuavcan_v0.transport.bchr
bchr
Definition: transport.py:36
pyuavcan_v0.transport.BaseValue._pack
def _pack(self, tao)
Definition: transport.py:258
pyuavcan_v0.transport.Frame.__init__
def __init__(self, message_id, data, ts_monotonic=None, ts_real=None)
Definition: transport.py:624
pyuavcan_v0.transport.le_from_be_bits
def le_from_be_bits(s, bitlen)
Definition: transport.py:109
pyuavcan_v0.transport.ArrayValue.__eq__
def __eq__(self, other)
Definition: transport.py:382
pyuavcan_v0.transport.get_active_union_field
def get_active_union_field(obj)
Definition: transport.py:54
pyuavcan_v0.dsdl.parser.VoidType
Definition: parser.py:326
pyuavcan_v0.transport.ArrayValue.new_item
def new_item(self)
Definition: transport.py:395
pyuavcan_v0.transport.Transfer.key
def key(self)
Definition: transport.py:827
pyuavcan_v0.transport.Transfer.is_complete
is_complete
Definition: transport.py:683
pyuavcan_v0.transport.CompoundValue._union_field
_union_field
Definition: transport.py:499
pyuavcan_v0.transport.Float32IntegerUnion
Definition: transport.py:149
pyuavcan_v0.transport.is_response
def is_response(obj)
Definition: transport.py:87
pyuavcan_v0.transport.ArrayValue.__delitem__
def __delitem__(self, idx)
Definition: transport.py:376
pyuavcan_v0.dsdl.parser.ArrayType
Definition: parser.py:132
pyuavcan_v0.transport.ArrayValue.__item_ctor
__item_ctor
Definition: transport.py:339
pyuavcan_v0.transport.ArrayValue.__items
__items
Definition: transport.py:346
libuavcan_dsdl_compiler.str
str
Definition: libuavcan_dsdl_compiler/__init__.py:22
pyuavcan_v0.transport.CompoundValue._pack
def _pack(self, tao=True)
Definition: transport.py:612
Transfer
Definition: transfer_test_helpers.hpp:16
pyuavcan_v0.transport.is_union
def is_union(obj)
Definition: transport.py:47
pyuavcan_v0.UAVCANException
Definition: pyuavcan/pyuavcan_v0/__init__.py:45
pyuavcan_v0.transport.CompoundValue._mode
_mode
Definition: transport.py:498
pyuavcan_v0.transport.VoidValue._pack
def _pack(self, tao)
Definition: transport.py:269
pyuavcan_v0.transport.Transfer.dest_node_id
dest_node_id
Definition: transport.py:660
pyuavcan_v0.transport.Transfer.DEFAULT_TRANSFER_PRIORITY
int DEFAULT_TRANSFER_PRIORITY
Definition: transport.py:654
pyuavcan_v0.transport.Frame.end_of_transfer
def end_of_transfer(self)
Definition: transport.py:641
pyuavcan_v0.transport.PrimitiveValue
Definition: transport.py:273
pyuavcan_v0.transport.TransferManager.active_transfers
active_transfers
Definition: transport.py:845
pyuavcan_v0.transport.BaseValue._type
_type
Definition: transport.py:248
pyuavcan_v0.transport.Transfer.to_frames
def to_frames(self)
Definition: transport.py:740
pyuavcan_v0.transport.PrimitiveValue.value
value
Definition: transport.py:277
pyuavcan_v0.transport.format_bits
def format_bits(s)
Definition: transport.py:118
uavcan::max
const UAVCAN_EXPORT T & max(const T &a, const T &b)
Definition: templates.hpp:291
pyuavcan_v0.transport.Float32IntegerUnion.f
f
Definition: transport.py:166
pyuavcan_v0.transport.Float32IntegerUnion.u
u
Definition: transport.py:164
pyuavcan_v0.transport.ArrayValue.__len__
def __len__(self)
Definition: transport.py:379
pyuavcan_v0.transport.BaseValue
Definition: transport.py:245
pyuavcan_v0.transport.TransferManager
Definition: transport.py:843
pyuavcan_v0.transport.CompoundValue.__getattr__
def __getattr__(self, attr)
Definition: transport.py:544
pyuavcan_v0.transport.f16_from_f32
def f16_from_f32(float32)
Definition: transport.py:187
pyuavcan_v0.transport.TransferManager.__init__
def __init__(self)
Definition: transport.py:844
pyuavcan_v0.transport.Transfer.data_type_crc
data_type_crc
Definition: transport.py:676
pyuavcan_v0.transport.is_request
def is_request(obj)
Definition: transport.py:82
pyuavcan_v0.transport.PrimitiveValue.__init__
def __init__(self, _uavcan_type, *args, **kwargs)
Definition: transport.py:274
pyuavcan_v0.transport.switch_union_field
def switch_union_field(obj, value)
Definition: transport.py:61
pyuavcan_v0.transport.ArrayValue
Definition: transport.py:334
pyuavcan_v0.transport.CompoundValue._unpack
def _unpack(self, stream, tao=True)
Definition: transport.py:602
pyuavcan_v0.transport.Transfer.is_response_to
def is_response_to(self, transfer)
Definition: transport.py:830
pyuavcan_v0.transport.Frame.start_of_transfer
def start_of_transfer(self)
Definition: transport.py:645
pyuavcan_v0.transport.ArrayValue.from_bytes
def from_bytes(self, value)
Definition: transport.py:451
pyuavcan_v0.transport.ArrayValue.__str__
def __str__(self)
Definition: transport.py:353
pyuavcan_v0.transport.Transfer.payload
payload
Definition: transport.py:673
pyuavcan_v0.transport.Transfer.data_type_signature
data_type_signature
Definition: transport.py:661
pyuavcan_v0.transport.ArrayValue.insert
def insert(self, idx, value)
Definition: transport.py:398
pyuavcan_v0.transport.get_uavcan_data_type
def get_uavcan_data_type(obj)
Definition: transport.py:42
int
int
Definition: libstubs.cpp:120
pyuavcan_v0.transport.be_from_le_bits
def be_from_le_bits(s, bitlen)
Definition: transport.py:100
pyuavcan_v0.transport.ArrayValue.__init__
def __init__(self, _uavcan_type, *args, **kwargs)
Definition: transport.py:335
pyuavcan_v0.transport.Transfer.from_frames
def from_frames(self, frames)
Definition: transport.py:766
pyuavcan_v0.transport.ArrayValue.encode
def encode(self, value, errors='strict')
Definition: transport.py:459
pyuavcan_v0.dsdl.parser.CompoundType
Definition: parser.py:179
pyuavcan_v0.transport.get_constants
def get_constants(obj)
Definition: transport.py:75
pyuavcan_v0.transport.CompoundValue.__setattr__
def __setattr__(self, attr, value)
Definition: transport.py:561
pyuavcan_v0.transport.Frame.transfer_key
def transfer_key(self)
Definition: transport.py:631
pyuavcan_v0.dsdl
Definition: pyuavcan/pyuavcan_v0/dsdl/__init__.py:1
pyuavcan_v0.transport.TransferManager.remove_inactive_transfers
def remove_inactive_transfers(self, timeout=1.0)
Definition: transport.py:867
pyuavcan_v0.transport.TransferManager.active_transfer_timestamps
active_transfer_timestamps
Definition: transport.py:846
pyuavcan_v0.transport.Transfer.service_not_message
service_not_message
Definition: transport.py:663
pyuavcan_v0.transport.Transfer.transfer_priority
transfer_priority
Definition: transport.py:657
pyuavcan_v0.transport.bytes_from_bits
def bytes_from_bits(s)
Definition: transport.py:96
pyuavcan_v0.transport.CompoundValue._is_union
_is_union
Definition: transport.py:484
pyuavcan_v0.transport.ArrayValue.decode
def decode(self, encoding="utf-8")
Definition: transport.py:467
pyuavcan_v0.transport.CompoundValue.__repr__
def __repr__(self)
Definition: transport.py:519
pyuavcan_v0.transport.ArrayValue.__getitem__
def __getitem__(self, idx)
Definition: transport.py:362
pyuavcan_v0.transport.f32_from_f16
def f32_from_f16(float16)
Definition: transport.py:212
pyuavcan_v0.transport.Frame
Definition: transport.py:623
pyuavcan_v0.transport.Transfer.discriminator
discriminator
Definition: transport.py:664
pyuavcan_v0.transport.CompoundValue.__init__
def __init__(self, _uavcan_type, _mode=None, *args, **kwargs)
Definition: transport.py:475
pyuavcan_v0.dsdl.common
Definition: dsdl/common.py:1
pyuavcan_v0.transport.ArrayValue.__setitem__
def __setitem__(self, idx, value)
Definition: transport.py:368
pyuavcan_v0.transport.Float32IntegerUnion.__init__
def __init__(self, integer=None, floating_point=None)
Definition: transport.py:160
pyuavcan_v0.transport.Transfer.ts_real
ts_real
Definition: transport.py:666
pyuavcan_v0.transport.BaseValue._bits
_bits
Definition: transport.py:249
pyuavcan_v0.transport.ArrayValue.__repr__
def __repr__(self)
Definition: transport.py:350
pyuavcan_v0.transport.CompoundValue.__copy__
def __copy__(self)
Definition: transport.py:527
pyuavcan_v0.transport.Transfer.__repr__
def __repr__(self)
Definition: transport.py:693
pyuavcan_v0.transport.array_len_bits_from_max_size
def array_len_bits_from_max_size(max_size)
Definition: transport.py:125
test.format
format
Definition: dsdl/test.py:6
pyuavcan_v0.transport.Transfer.message_id
message_id
Definition: transport.py:792
pyuavcan_v0.transport.Transfer.data_type_id
data_type_id
Definition: transport.py:674
pyuavcan_v0.transport.Float32IntegerUnion._bytes
_bytes
Definition: transport.py:161
pyuavcan_v0.transport.Transfer.request_not_response
request_not_response
Definition: transport.py:662
pyuavcan_v0.transport.TransferError
Definition: transport.py:649
pyuavcan_v0.transport.TransferManager.receive_frame
def receive_frame(self, frame)
Definition: transport.py:848


uavcan_communicator
Author(s):
autogenerated on Fri Dec 13 2024 03:10:03