示例#1
0
    def __init__(self, settings, logger=None, **kwargs):
        self.logger = logger or logging.getLogger(__name__)
        self.logger.info("Version is: %s", transport_version)

        super(TransportService, self).__init__(
            logger=logger,
            c_extension=(settings.full_python is False),
            ignored_ep_filter=settings.ignored_endpoints_filter,
            **kwargs
        )

        self.gw_id = settings.gateway_id
        self.gw_model = settings.gateway_model
        self.gw_version = settings.gateway_version

        self.whitened_ep_filter = settings.whitened_endpoints_filter

        last_will_topic = TopicGenerator.make_status_topic(self.gw_id)
        last_will_message = wirepas_messaging.gateway.api.StatusEvent(
            self.gw_id, GatewayState.OFFLINE
        ).payload

        self.mqtt_wrapper = MQTTWrapper(
            settings,
            self.logger,
            self._on_mqtt_wrapper_termination_cb,
            self._on_connect,
            last_will_topic,
            last_will_message,
        )

        self.mqtt_wrapper.start()

        self.logger.info("Gateway started with id: %s", self.gw_id)
示例#2
0
    def _on_get_gateway_info_cmd_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        """
        This function doesn't need the decorator @deferred_thread as request is handled
        without I/O
        """
        self.logger.info("Gateway info request received")
        try:
            request = wirepas_messaging.gateway.api.GetGatewayInfoRequest.from_payload(
                message.payload)
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        response = wirepas_messaging.gateway.api.GetGatewayInfoResponse(
            request.req_id,
            self.gw_id,
            GatewayResultCode.GW_RES_OK,
            current_time_s_epoch=int(time()),
            gateway_model=self.gw_model,
            gateway_version=self.gw_version,
            implemented_api_version=IMPLEMENTED_API_VERSION,
        )

        topic = TopicGenerator.make_get_gateway_info_response_topic(self.gw_id)
        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#3
0
    def _on_otap_process_scratchpad_request_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("OTAP process request received")
        try:
            request = wirepas_messaging.gateway.api.ProcessScratchpadRequest.from_payload(
                message.payload
            )
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        sink = self.sink_manager.get_sink(request.sink_id)
        if sink is not None:
            res = sink.process_scratchpad()
        else:
            res = GatewayResultCode.GW_RES_INVALID_SINK_ID

        response = wirepas_messaging.gateway.api.ProcessScratchpadResponse(
            request.req_id, self.gw_id, res, request.sink_id
        )

        topic = TopicGenerator.make_otap_process_scratchpad_response_topic(
            self.gw_id, request.sink_id
        )

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#4
0
    def _on_set_config_cmd_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("Set config request received")
        try:
            request = wirepas_messaging.gateway.api.SetConfigRequest.from_payload(
                message.payload
            )
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        self.logger.debug("Set sink config: %s", request)
        sink = self.sink_manager.get_sink(request.sink_id)
        if sink is not None:
            res = sink.write_config(request.new_config)
            new_config = sink.read_config()
        else:
            res = GatewayResultCode.GW_RES_INVALID_SINK_ID
            new_config = None

        response = wirepas_messaging.gateway.api.SetConfigResponse(
            request.req_id, self.gw_id, res, request.sink_id, new_config
        )
        topic = TopicGenerator.make_set_config_response_topic(
            self.gw_id, request.sink_id
        )

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#5
0
    def _on_otap_set_target_scratchpad_request_received(
            self, client, userdata, message):
        # pylint: disable=unused-argument
        res = wmm.GatewayResultCode.GW_RES_OK
        self.logger.info("OTAP set target request received")
        try:
            request = wmm.SetScratchpadTargetAndActionRequest.from_payload(
                message.payload)
            action = request.target["action"]
        except wmm.GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return
        except KeyError:
            self.logger.error("Action is mandatory")
            res = wmm.GatewayResultCode.GW_RES_INVALID_PARAM

        if res == wmm.GatewayResultCode.GW_RES_OK:
            # Get optional params (None if not present)
            seq = request.target.get("target_sequence")
            crc = request.target.get("target_crc")
            delay = request.target.get("delay")
            if delay is not None:
                # Convert predefined delay to right format
                if delay is wmm.ProcessingDelay.DELAY_TEN_MINUTES:
                    param = 0x4A
                elif delay is wmm.ProcessingDelay.DELAY_THIRTY_MINUTES:
                    param = 0x5E
                elif delay is wmm.ProcessingDelay.DELAY_ONE_HOUR:
                    param = 0x81
                elif delay is wmm.ProcessingDelay.DELAY_SIX_HOURS:
                    param = 0x86
                elif delay is wmm.ProcessingDelay.DELAY_ONE_DAY:
                    param = 0xC1
                elif delay is wmm.ProcessingDelay.DELAY_TWO_DAYS:
                    param = 0xC2
                elif delay is wmm.ProcessingDelay.DELAY_FIVE_DAYS:
                    param = 0xC5
                else:
                    # Unknown value, set to 0, will generate an error later
                    param = 0
            else:
                param = request.target.get("param")

            # no error so far
            sink = self.sink_manager.get_sink(request.sink_id)
            if sink is not None:
                res = sink.set_target_scratchpad(action=action,
                                                 target_seq=seq,
                                                 target_crc=crc,
                                                 param=param)
            else:
                res = wmm.GatewayResultCode.GW_RES_INVALID_SINK_ID

        response = wmm.SetScratchpadTargetAndActionResponse(
            request.req_id, self.gw_id, res, request.sink_id)

        topic = TopicGenerator.make_otap_set_target_scratchpad_response_topic(
            self.gw_id, request.sink_id)

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#6
0
    def _set_status(self):
        event_online = wirepas_messaging.gateway.api.StatusEvent(
            self.gw_id, GatewayState.ONLINE
        )

        topic = TopicGenerator.make_status_topic(self.gw_id)

        self.mqtt_wrapper.publish(topic, event_online.payload, qos=1, retain=True)
示例#7
0
    def __init__(self, settings, logger=None, **kwargs):
        self.logger = logger or logging.getLogger(__name__)
        self.logger.info("Version is: %s", transport_version)

        super(TransportService, self).__init__(
            logger=logger,
            c_extension=(settings.full_python is False),
            ignored_ep_filter=settings.ignored_endpoints_filter,
            **kwargs)

        self.gw_id = settings.gateway_id
        self.gw_model = settings.gateway_model
        self.gw_version = settings.gateway_version

        self.whitened_ep_filter = settings.whitened_endpoints_filter

        last_will_topic = TopicGenerator.make_status_topic(self.gw_id)
        last_will_message = wirepas_messaging.gateway.api.StatusEvent(
            self.gw_id, GatewayState.OFFLINE).payload

        self.mqtt_wrapper = MQTTWrapper(
            settings,
            self.logger,
            self._on_mqtt_wrapper_termination_cb,
            self._on_connect,
            last_will_topic,
            last_will_message,
        )

        self.mqtt_wrapper.start()

        self.logger.info("Gateway started with id: %s", self.gw_id)

        self.monitoring_thread = None
        self.minimum_sink_cost = settings.buffering_minimal_sink_cost

        if settings.buffering_max_buffered_packets > 0:
            self.logger.info(
                " Black hole detection enabled: max_packets=%s packets, max_delay=%s",
                settings.buffering_max_buffered_packets,
                settings.buffering_max_delay_without_publish,
            )
            # Create and start a monitoring thread for black hole issue
            self.monitoring_thread = ConnectionToBackendMonitorThread(
                self.logger,
                self.MONITORING_BUFFERING_PERIOD_S,
                self.mqtt_wrapper,
                self.sink_manager,
                settings.buffering_minimal_sink_cost,
                settings.buffering_max_buffered_packets,
                settings.buffering_max_delay_without_publish,
            )
            self.monitoring_thread.start()

        if settings.debug_incr_data_event_id:
            self.data_event_id = 0
        else:
            self.data_event_id = None
示例#8
0
    def on_data_received(
        self,
        sink_id,
        timestamp,
        src,
        dst,
        src_ep,
        dst_ep,
        travel_time,
        qos,
        hop_count,
        data,
    ):

        if self.whitened_ep_filter is not None and dst_ep in self.whitened_ep_filter:
            # Only publish payload size but not the payload
            self.logger.debug("Filtering payload data")
            data_size = data.__len__()
            data = None
        else:
            data_size = None

        event = wirepas_messaging.gateway.api.ReceivedDataEvent(
            gw_id=self.gw_id,
            sink_id=sink_id,
            rx_time_ms_epoch=timestamp,
            src=src,
            dst=dst,
            src_ep=src_ep,
            dst_ep=dst_ep,
            travel_time_ms=travel_time,
            qos=qos,
            data=data,
            data_size=data_size,
            hop_count=hop_count,
        )

        sink = self.sink_manager.get_sink(sink_id)
        if sink is None:
            # It can happen at sink connection as messages can be received
            # before sinks are identified
            self.logger.info(
                "Message received from unknown sink at the moment %s", sink_id
            )
            return

        network_address = sink.get_network_address()

        topic = TopicGenerator.make_received_data_topic(
            self.gw_id, sink_id, network_address, src_ep, dst_ep
        )
        self.logger.debug("Sending data to: %s", topic)
        # Set qos to 1 to avoid loading too much the broker
        # unique id in event header can be used for duplicate filtering in
        # backends
        self.mqtt_wrapper.publish(topic, event.payload, qos=1)
示例#9
0
    def on_stack_started(self, name):
        sink = self.sink_manager.get_sink(name)
        if sink is None:
            self.logger.error("Sink started %s error: unknown sink", name)
            return

        # Generate a setconfig answer with req_id of 0
        response = wirepas_messaging.gateway.api.SetConfigResponse(
            0, self.gw_id, GatewayResultCode.GW_RES_OK, sink.sink_id, sink.read_config()
        )
        topic = TopicGenerator.make_set_config_response_topic(self.gw_id, sink.sink_id)
        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#10
0
    def _on_connect(self):
        # Register for get gateway info
        topic = TopicGenerator.make_get_gateway_info_request_topic(self.gw_id)
        self.mqtt_wrapper.subscribe(topic,
                                    self._on_get_gateway_info_cmd_received)

        # Register for get configs request
        topic = TopicGenerator.make_get_configs_request_topic(self.gw_id)
        self.mqtt_wrapper.subscribe(topic, self._on_get_configs_cmd_received)

        # Register for set config request for any sink
        topic = TopicGenerator.make_set_config_request_topic(self.gw_id)
        self.mqtt_wrapper.subscribe(topic, self._on_set_config_cmd_received)

        # Register for send data request for any sink on the gateway
        topic = TopicGenerator.make_send_data_request_topic(self.gw_id)
        self.logger.debug("Subscribing to: %s", topic)
        # It is important to have a qos of 2 and also from the publisher as 1 could generate
        # duplicated packets and we don't know the consequences on end
        # application
        self.mqtt_wrapper.subscribe(topic,
                                    self._on_send_data_cmd_received,
                                    qos=2)

        # Register for otap commands for any sink on the gateway
        topic = TopicGenerator.make_otap_status_request_topic(self.gw_id)
        self.mqtt_wrapper.subscribe(topic,
                                    self._on_otap_status_request_received)

        topic = TopicGenerator.make_otap_load_scratchpad_request_topic(
            self.gw_id)
        self.mqtt_wrapper.subscribe(
            topic, self._on_otap_upload_scratchpad_request_received)

        topic = TopicGenerator.make_otap_process_scratchpad_request_topic(
            self.gw_id)
        self.mqtt_wrapper.subscribe(
            topic, self._on_otap_process_scratchpad_request_received)

        topic = TopicGenerator.make_otap_set_target_scratchpad_request_topic(
            self.gw_id)
        self.mqtt_wrapper.subscribe(
            topic, self._on_otap_set_target_scratchpad_request_received)

        self._set_status()

        self.logger.info("MQTT connected!")
示例#11
0
    def _send_asynchronous_get_configs_response(self):
        # Create a list of different sink configs
        configs = []
        for sink in self.sink_manager.get_sinks():
            config = sink.read_config()
            if config is not None:
                configs.append(config)

        # Generate a setconfig answer with req_id of 0 as not from
        # a real request
        response = wirepas_messaging.gateway.api.GetConfigsResponse(
            0, self.gw_id, GatewayResultCode.GW_RES_OK, configs)
        topic = TopicGenerator.make_get_configs_response_topic(self.gw_id)

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#12
0
    def _on_otap_status_request_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("OTAP status request received")
        try:
            request = wmm.GetScratchpadStatusRequest.from_payload(
                message.payload)
        except wmm.GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        sink = self.sink_manager.get_sink(request.sink_id)
        if sink is not None:
            d = sink.get_scratchpad_status()

            target_and_action = {}
            try:
                target_and_action["action"] = d["target_action"]
                target_and_action["target_sequence"] = d["target_seq"]
                target_and_action["target_crc"] = d["target_crc"]
                target_and_action["param"] = d["target_param"]
            except KeyError:
                # If not present, just an old node
                target_and_action = None

            response = wmm.GetScratchpadStatusResponse(
                request.req_id,
                self.gw_id,
                wmm.GatewayResultCode.GW_RES_OK,
                request.sink_id,
                d["stored_scartchpad"],
                d["stored_status"],
                d["stored_type"],
                d["processed_scartchpad"],
                d["firmware_area_id"],
                target_and_action,
            )
        else:
            response = wmm.GetScratchpadStatusResponse(
                request.req_id,
                self.gw_id,
                wmm.GatewayResultCode.GW_RES_INVALID_SINK_ID,
                request.sink_id,
            )

        topic = TopicGenerator.make_otap_status_response_topic(
            self.gw_id, request.sink_id)

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#13
0
    def _on_send_data_cmd_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("Request to send data")
        try:
            request = wirepas_messaging.gateway.api.SendDataRequest.from_payload(
                message.payload
            )
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        # Get the sink-id from topic
        _, sink_id = TopicParser.parse_send_data_topic(message.topic)

        self.logger.debug("Request for sink %s", sink_id)

        sink = self.sink_manager.get_sink(sink_id)
        if sink is not None:
            if request.hop_limit > self.MAX_HOP_LIMIT:
                res = GatewayResultCode.GW_RES_INVALID_MAX_HOP_COUNT
            else:
                res = sink.send_data(
                    request.destination_address,
                    request.source_endpoint,
                    request.destination_endpoint,
                    request.qos,
                    request.initial_delay_ms,
                    request.data_payload,
                    request.is_unack_csma_ca,
                    request.hop_limit,
                )
        else:
            self.logger.warning("No sink with id: %s", sink_id)
            # No sink with  this id
            res = GatewayResultCode.GW_RES_INVALID_SINK_ID

        # Answer to backend
        response = wirepas_messaging.gateway.api.SendDataResponse(
            request.req_id, self.gw_id, res, sink_id
        )
        topic = TopicGenerator.make_send_data_response_topic(self.gw_id, sink_id)

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#14
0
    def _on_get_configs_cmd_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("Config request received")
        try:
            request = wirepas_messaging.gateway.api.GetConfigsRequest.from_payload(
                message.payload)
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        # Create a list of different sink configs
        configs = []
        for sink in self.sink_manager.get_sinks():
            config = sink.read_config()
            if config is not None:
                configs.append(config)

        response = wirepas_messaging.gateway.api.GetConfigsResponse(
            request.req_id, self.gw_id, GatewayResultCode.GW_RES_OK, configs)
        topic = TopicGenerator.make_get_configs_response_topic(self.gw_id)

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)
示例#15
0
    def _on_otap_status_request_received(self, client, userdata, message):
        # pylint: disable=unused-argument
        self.logger.info("OTAP status request received")
        try:
            request = wirepas_messaging.gateway.api.GetScratchpadStatusRequest.from_payload(
                message.payload
            )
        except GatewayAPIParsingException as e:
            self.logger.error(str(e))
            return

        sink = self.sink_manager.get_sink(request.sink_id)
        if sink is not None:
            d = sink.get_scratchpad_status()

            response = wirepas_messaging.gateway.api.GetScratchpadStatusResponse(
                request.req_id,
                self.gw_id,
                GatewayResultCode.GW_RES_OK,
                request.sink_id,
                d["stored_scartchpad"],
                d["stored_status"],
                d["stored_type"],
                d["processed_scartchpad"],
                d["firmware_area_id"],
            )
        else:
            response = wirepas_messaging.gateway.api.GetScratchpadStatusResponse(
                request.req_id,
                self.gw_id,
                GatewayResultCode.GW_RES_INVALID_SINK_ID,
                request.sink_id,
            )

        topic = TopicGenerator.make_otap_status_response_topic(
            self.gw_id, request.sink_id
        )

        self.mqtt_wrapper.publish(topic, response.payload, qos=2)