10 from __future__
import division, absolute_import, print_function, unicode_literals
13 from logging
import getLogger
15 from pyuavcan_v0
import UAVCANException
18 logger = getLogger(__name__)
22 return ' '.join([
'%02X' % x
for x
in bytearray(uid)])
if uid
else None
26 QUERY_TIMEOUT = pyuavcan_v0.protocol.dynamic_node_id.Allocation().FOLLOWUP_TIMEOUT_MS / 1000
27 DEFAULT_NODE_ID_RANGE = 1, 125
28 DATABASE_STORAGE_MEMORY =
':memory:'
33 self.
db = sqlite3.connect(path, check_same_thread=
False)
35 self.
_modify(
'''CREATE TABLE IF NOT EXISTS `allocation` (
36 `node_id` INTEGER NOT NULL UNIQUE,
38 `ts` time NOT NULL DEFAULT CURRENT_TIMESTAMP,
39 PRIMARY KEY(node_id));''')
49 def set(self, unique_id, node_id):
50 if unique_id
is not None and unique_id == bytes([0] * len(unique_id)):
52 if unique_id
is not None:
53 unique_id = sqlite3.Binary(unique_id)
54 logger.debug(
'[CentralizedServer] AllocationTable update: %d %s', node_id,
_unique_id_to_string(unique_id))
55 self.
_modify(
'''insert or replace into allocation (node_id, unique_id) values (?, ?);''',
59 assert isinstance(unique_id, bytes)
61 c.execute(
'''select node_id from allocation where unique_id = ? order by ts desc limit 1''',
64 return res[0]
if res
else None
67 assert isinstance(node_id, int)
69 c.execute(
'''select unique_id from allocation where node_id = ?''', (node_id,))
71 return res[0]
if res
else None
74 assert isinstance(node_id, int)
76 c.execute(
'''select count(*) from allocation where node_id = ?''', (node_id,))
77 return c.fetchone()[0] > 0
81 c.execute(
'''select unique_id, node_id from allocation order by ts desc''')
82 return list(c.fetchall())
84 def __init__(self, node, node_monitor, database_storage=None, dynamic_node_id_range=None):
86 :param node: Node instance.
88 :param node_monitor: Instance of NodeMonitor.
90 :param database_storage: Path to the file where the instance will keep the allocation table.
91 If not provided, the allocation table will be kept in memory.
93 :param dynamic_node_id_range: Range of node ID available for dynamic allocation; defaults to [1, 125].
96 raise UAVCANException(
'Dynamic node ID server cannot be launched on an anonymous node')
106 self.
_handle = node.add_handler(pyuavcan_v0.protocol.dynamic_node_id.Allocation,
109 self.
_allocation_table.set(node.node_info.hardware_version.unique_id.to_bytes(), node.node_id)
112 for entry
in node_monitor.find_all(
lambda _:
True):
113 unique_id = entry.info.hardware_version.unique_id.to_bytes()
if entry.info
else None
120 unique_id = event.entry.info.hardware_version.unique_id.to_bytes()
if event.entry.info
else None
124 """Stops the instance and closes the allocation table storage.
134 if e.transfer.source_node_id != 0:
135 logger.warning(
'[CentralizedServer] Message from another allocator ignored: %r', e)
140 logger.info(
'[CentralizedServer] Request ignored: not all nodes are discovered')
146 logger.info(
"[CentralizedServer] Query timeout, resetting query")
149 if e.message.first_part_of_unique_id:
151 self.
_query = e.message.unique_id.to_bytes()
154 response = pyuavcan_v0.protocol.dynamic_node_id.Allocation()
155 response.first_part_of_unique_id = 0
157 response.unique_id.from_bytes(self.
_query)
158 e.node.broadcast(response)
160 logger.debug(
"[CentralizedServer] Got first-stage dynamic ID request for %s",
163 elif len(e.message.unique_id) == 6
and len(self.
_query) == 6:
165 self.
_query += e.message.unique_id.to_bytes()
168 response = pyuavcan_v0.protocol.dynamic_node_id.Allocation()
169 response.first_part_of_unique_id = 0
171 response.unique_id.from_bytes(self.
_query)
172 e.node.broadcast(response)
173 logger.debug(
"[CentralizedServer] Got second-stage dynamic ID request for %s",
176 elif len(e.message.unique_id) == 4
and len(self.
_query) == 12:
178 self.
_query += e.message.unique_id.to_bytes()
181 logger.debug(
"[CentralizedServer] Got third-stage dynamic ID request for %s",
184 node_requested_id = e.message.node_id
189 if node_requested_id
and not node_allocated_id:
192 node_allocated_id = node_id
197 if not node_allocated_id:
200 node_allocated_id = node_id
203 if node_allocated_id:
206 response = pyuavcan_v0.protocol.dynamic_node_id.Allocation()
207 response.first_part_of_unique_id = 0
208 response.node_id = node_allocated_id
209 response.unique_id.from_bytes(self.
_query)
210 e.node.broadcast(response)
212 logger.info(
"[CentralizedServer] Allocated node ID %d to node with unique ID %s",
217 logger.error(
"[CentralizedServer] Couldn't allocate dynamic node ID")