transport.py 32 KB

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