def discover_node(self, node, uid): """Discover resources available on a node.""" coap_node_url = 'coap://[{}]'.format(node.address) if len(node.endpoints) == 0: logger.debug("Discovering CoAP node {}".format(node.address)) code, payload = yield _coap_resource( '{0}/.well-known/core'.format(coap_node_url), method=GET) node.endpoints = _coap_endpoints(payload) endpoints = [ endpoint for endpoint in node.endpoints if 'well-known/core' not in endpoint ] logger.debug("Fetching CoAP node resources: {}".format(endpoints)) for endpoint in endpoints: elems = endpoint.split(';') path = elems.pop(0).replace('<', '').replace('>', '') try: code, payload = yield _coap_resource('{0}{1}'.format( coap_node_url, path), method=GET) except: logger.debug("Cannot discover ressource {} on node {}".format( endpoint, node.address)) return # Remove '/' from path path = path[1:] self._on_message_cb(Msg.update_node(uid, path, payload)) self.nodes[node]['data'].update({path: payload}) logger.debug( "CoAP node resources '{}' sent to broker".format(endpoints))
def handle_coap_post(self, address, endpoint, value): """Handle CoAP post message sent from coap node.""" node = CoapNode(address) if node in self.nodes and endpoint in self.nodes[node]['data']: self.nodes[node]['data'][endpoint] = value self._on_message_cb( Msg.update_node(self.nodes[node]['uid'], endpoint, value))
def handle_node_check(self, data): """Handle alive message received from coap node.""" node_id = data['id'] node = MQTTNode(node_id) node.check_time = time.time() if node not in self.nodes: resources_topic = 'node/{}/resources'.format(node_id) yield from self.mqtt_client.subscribe([(resources_topic, QOS_1)]) logger.debug("Subscribed to topic: {}".format(resources_topic)) node_uid = str(uuid.uuid4()) self.nodes.update( {node: { 'uid': node_uid, 'data': { 'protocol': PROTOCOL } }}) logger.debug("Available nodes: {}".format(self.nodes)) self._on_message_cb(Msg.new_node(node_uid)) self._on_message_cb(Msg.update_node(node_uid, "protocol", PROTOCOL)) discover_topic = 'gateway/{}/discover'.format(node_id) yield from self.mqtt_client.publish(discover_topic, b"resources", qos=QOS_1) logger.debug("Published '{}' to topic: {}".format( "resources", discover_topic)) else: data = self.nodes.pop(node) self.nodes.update({node: data}) logger.debug("Available nodes: {}".format(self.nodes))
def forward_data_from_node(self, node, resource, value): """Send data received from a node to the broker via the gateway.""" logger.debug( "Sending data received from node '{}': '{}', '{}'.".format( node, resource, value)) node.set_resource_value(resource, value) self.send_to_broker(Message.update_node(node.uid, resource, value))
def send_data_to_node(self, data): """Forward received message data to the destination node. The message should be JSON and contain 'uid', 'path' and 'payload' keys. - 'uid' corresponds to the node uid (uuid) - 'path' corresponds to the CoAP resource on the node - 'payload' corresponds to the new payload for the CoAP resource. """ uid = data['uid'] endpoint = data['endpoint'] payload = data['payload'] logger.debug("Translating message ('{}') received to CoAP PUT " "request".format(data)) for node, _ in self.nodes.items(): if self.nodes[node]['uid'] == uid: address = self.nodes[node]['data']['ip'] logger.debug("Updating CoAP node '{}' resource '{}'".format( self.nodes[node]['data']['ip'], endpoint)) code, p = yield _coap_resource('coap://[{0}]/{1}'.format( address, endpoint), method=PUT, payload=payload.encode('ascii')) if code == Code.CHANGED: self.nodes[node]['data'][endpoint] = payload yield self._on_message_cb( Msg.update_node(uid, endpoint, payload)) break
def fetch_nodes_cache(self, source): """Send cached nodes information.""" logger.debug("Fetching cached information of registered nodes.") for _, node in self.nodes.items(): self._on_message_cb(Msg.new_node(node['uid'], dst=source)) for endpoint, value in node['data'].items(): yield self._on_message_cb( Msg.update_node(node['uid'], endpoint, value, dst=source))
def add_node(self, node): """Add a new node to the list of nodes and notify the broker.""" node.set_resource_value('protocol', self.PROTOCOL) self.nodes.update({node.uid: node}) self.send_to_broker(Message.new_node(node.uid)) for res, value in node.resources.items(): self.send_to_broker(Message.update_node(node.uid, res, value)) yield self.discover_node(node)
def fetch_nodes_cache(self, source): """Send cached nodes information.""" logger.debug( "Fetching cached information of registered nodes '{}'.".format( self.nodes)) for _, value in self.nodes.items(): self._on_message_cb(Msg.new_node(value['uid'], dst=source)) for resource, data in value['data'].items(): self._on_message_cb( Msg.update_node(value['uid'], resource, data, dst=source))
def test_update_node(value): serialized = Message.update_node('1234', 'test', 'value') assert serialized == Message.serialize({ 'type': 'update', 'uid': '1234', 'endpoint': 'test', 'data': 'value', 'dst': 'all' }) serialized = Message.update_node('1234', 'test', value, '5678') assert serialized == Message.serialize({ 'type': 'update', 'uid': '1234', 'endpoint': 'test', 'data': value, 'dst': '5678' })
def fetch_nodes_cache(self, source): """Send cached nodes information.""" logger.debug("Fetching cached information of registered nodes.") for ws, node in self.nodes.items(): self.send_to_broker(Message.new_node(node['uid'], dst=source)) for endpoint, value in node['data'].items(): self.send_to_broker( Message.update_node(node['uid'], endpoint, value, dst=source))
def handle_coap_alive(self, address): """Handle alive message received from coap node.""" node = CoapNode(address) node.check_time = time.time() if node not in self.nodes: node_uid = str(uuid.uuid4()) self.nodes.update({ node: { 'uid': node_uid, 'data': { 'ip': address, 'protocol': 'coap' } } }) self._on_message_cb(Msg.new_node(node_uid)) self._on_message_cb(Msg.update_node(node_uid, "ip", address)) self._on_message_cb(Msg.update_node(node_uid, "protocol", 'coap')) self.discover_node(node, node_uid) else: data = self.nodes.pop(node) self.nodes.update({node: data})
def fetch_nodes_cache(self, client): """Send cached nodes information to a given client. :param client: the ID of the client """ logger.debug( "Fetching cached information of registered nodes '{}'.".format( self.nodes)) for node in self.nodes.values(): self.send_to_broker(Message.new_node(node.uid, dst=client)) for resource, value in node.resources.items(): self.send_to_broker( Message.update_node(node.uid, resource, value, dst=client))
def handle_coap_check(self, address, reset=False): """Handle check message received from coap node.""" node = CoapNode(address) node.check_time = time.time() if node not in self.nodes: # This is a totally new node: create uid, initialized cached node # send 'new' node notification, 'update' notification. node_uid = str(uuid.uuid4()) self.nodes.update({ node: { 'uid': node_uid, 'data': { 'ip': address, 'protocol': PROTOCOL } } }) self._on_message_cb(Msg.new_node(node_uid)) self._on_message_cb(Msg.update_node(node_uid, "ip", address)) self._on_message_cb(Msg.update_node(node_uid, "protocol", PROTOCOL)) self.discover_node(node, node_uid) elif reset: # The data of the node need to be reset without removing it. This # is particularly the case after a reboot of the node or a # firmware update of the node that triggered the reboot. node_uid = self.nodes[node]['uid'] self.nodes[node]['data'] = {} self.nodes[node]['data'].update({ 'ip': address, 'protocol': PROTOCOL }) self._on_message_cb(Msg.reset_node(node_uid)) self.discover_node(node, node_uid) else: # The node simply sent a check message to notify that it's still # online. data = self.nodes.pop(node) self.nodes.update({node: data})
def on_node_message(self, ws, message): """Handle a message received from a node websocket.""" if message['type'] == "update": logger.debug("New update message received from node websocket") for key, value in message['data'].items(): if key in self.nodes[ws]['data']: self.nodes[ws]['data'][key] = value else: self.nodes[ws]['data'].update({key: value}) self.send_to_broker( Message.update_node(self.nodes[ws]['uid'], key, value)) else: logger.debug("Invalid message received from node websocket")
def handle_node_update(self, topic_name, data): """Handle CoAP post message sent from coap node.""" _, node_id, resource = topic_name.split("/") node = MQTTNode(node_id) value = data['value'] if node in self.nodes: if resource in self.nodes[node]['data']: # Add updated information to cache self.nodes[node]['data'][resource] = value else: self.nodes[node]['data'].update({resource: value}) # Send update to broker self._on_message_cb( Msg.update_node(self.nodes[node]['uid'], resource, value))
def open(self): """Discover nodes on each opened connection.""" self.set_nodelay(True) logger.debug("New node websocket opened") self.application.nodes.update( {self: { 'uid': str(uuid.uuid4()), 'data': { 'protocol': PROTOCOL } }}) node_uid = self.application.nodes[self]['uid'] self.application.send_to_broker(Message.new_node(node_uid)) yield self.write_message(Message.discover_node()) self.application.send_to_broker( Message.update_node(node_uid, 'protocol', PROTOCOL))