예제 #1
0
 def __init__(self) -> None:
     self.mqtt_client: mqtt.Client = None
     self.auth: SymmetricKeyAuth = None
     self.connected = threading.Event()
     self.incoming_subacks: WaitableDict[int, int] = WaitableDict()
     self.incoming_messages = IncomingMessageList()
예제 #2
0
class SampleApp(object):
    def __init__(self) -> None:
        self.mqtt_client: mqtt.Client = None
        self.auth: SymmetricKeyAuth = None
        self.connected = threading.Event()
        self.incoming_subacks: WaitableDict[int, int] = WaitableDict()
        self.incoming_messages = IncomingMessageList()

    def handle_on_connect(self, mqtt_client: mqtt.Client, userdata: Any,
                          flags: Any, rc: int) -> None:
        logger.info("handle_on_connect called with rc={} ({})".format(
            rc, mqtt.connack_string(rc)))
        if rc == mqtt.MQTT_ERR_SUCCESS:
            self.connected.set()

    def handle_on_subscribe(
        self,
        client: mqtt.Client,
        userdata: Any,
        mid: int,
        granted_qos: int,
        properties: Any = None,
    ) -> None:
        # we're inside a Paho thread here.
        # Save the results and return as quickly as possible
        logger.info("Received SUBACK for mid {}".format(mid))
        self.incoming_subacks.add_item(mid, granted_qos)

    def subscribe_for_methods(self) -> None:
        (rc, mid) = self.mqtt_client.subscribe(
            topic_builder.build_method_request_subscribe_topic(
                self.auth.device_id, self.auth.module_id),
            1,
        )
        if rc:
            raise Exception("subscribe RC = {}".format(rc))
        print("send SUBSCRIBE for mid {}".format(mid))
        self.incoming_subacks.get_next_item(mid, timeout=10)
        print("SUBACK received for mid {}".format(mid))

    def handle_on_message(self, mqtt_client: mqtt.Client, userdata: Any,
                          message: mqtt.MQTTMessage) -> None:
        # we're inside a Paho thread here.
        # Save the message and return as quickly as possible
        print("received message on {}".format(message.topic))
        self.incoming_messages.add_item(message)

    def main(self) -> None:
        logger.info("Azure IoT Edge Protocol Translation Module (PTM) Sample")

        # Copy/paste code from other sample
        self.auth = SymmetricKeyAuth.create_from_connection_string(
            os.environ["IOTHUB_DEVICE_CONNECTION_STRING"])
        self.mqtt_client = mqtt.Client(self.auth.client_id)
        self.mqtt_client.enable_logger()
        self.mqtt_client.username_pw_set(self.auth.username,
                                         self.auth.password)
        self.mqtt_client.tls_set_context(self.auth.create_tls_context())
        self.mqtt_client.on_connect = self.handle_on_connect
        self.mqtt_client.loop_start()
        self.mqtt_client.connect(self.auth.hostname, self.auth.port)
        if not self.connected.wait(timeout=20):
            logger.error("Failed to connect.")
            sys.exit(1)

        # Paho callsbacks for PUBACK and SUBACK
        self.mqtt_client.on_subscribe = self.handle_on_subscribe
        self.mqtt_client.on_message = self.handle_on_message

        self.subscribe_for_methods()

        end_time = time.time() + 600
        while time.time() < end_time:
            # Wait for any messages to show up.  Don't pop them.
            if self.incoming_messages.wait_for_message(end_time - time.time()):

                # Do we have any methods named "Ping"?  Pop it, with timeout=0 so it returns
                # immediately.  It returns None we don't have any matches.
                ping = self.incoming_messages.pop_next_method_request(
                    method_name="ping", timeout=0)
                if ping:
                    print("ping: {}".format(str(ping.payload)))
                    response_topic = topic_builder.build_method_response_publish_topic(
                        ping.topic, "200")
                    mi = self.mqtt_client.publish(topic=response_topic,
                                                  payload=ping.payload,
                                                  qos=1)
                    mi.wait_for_publish()
                    print("ping response sent")

                # Can also get all method requests with one call and use extract_method_name
                # to build a switch
                method_request = self.incoming_messages.pop_next_method_request(
                    timeout=0)
                if method_request:
                    print("method request: {}".format(
                        str(method_request.payload)))
                    print("method name: {}".format(
                        topic_parser.extract_method_name(
                            method_request.topic)))

                # Or maybe we get a message on some unkonwn topic.  We can get that too and use
                # topic_match.py or topic_parser.py to figure out where to make it go.
                undefined = self.incoming_messages.pop_next_message(timeout=0)
                if undefined:
                    print("Undefined: {}, {}".format(undefined.topic,
                                                     str(undefined.payload)))

        self.mqtt_client.disconnect()

        logger.info("Exiting.")
예제 #3
0
class SampleApp(object):
    def __init__(self) -> None:
        self.mqtt_client: mqtt.Client = None
        self.auth: SymmetricKeyAuth = None
        self.connected = threading.Event()
        self.incoming_subacks: WaitableDict[int, int] = WaitableDict()
        self.incoming_messages = IncomingMessageList()

    def handle_on_connect(self, mqtt_client: mqtt.Client, userdata: Any,
                          flags: Any, rc: int) -> None:
        logger.info("handle_on_connect called with rc={} ({})".format(
            rc, mqtt.connack_string(rc)))
        if rc == mqtt.MQTT_ERR_SUCCESS:
            self.connected.set()

    def handle_on_subscribe(
        self,
        client: mqtt.Client,
        userdata: Any,
        mid: int,
        granted_qos: int,
        properties: Any = None,
    ) -> None:
        # we're inside a Paho thread here.
        # Save the results and return as quickly as possible
        logger.info("Received SUBACK for mid {}".format(mid))
        self.incoming_subacks.add_item(mid, granted_qos)

    def subscribe_for_twins(self) -> None:
        (rc, mid) = self.mqtt_client.subscribe(
            [
                (
                    topic_builder.build_twin_response_subscribe_topic(
                        self.auth.device_id, self.auth.module_id),
                    1,
                ),
                (
                    topic_builder.build_twin_patch_desired_subscribe_topic(
                        self.auth.device_id, self.auth.module_id),
                    1,
                ),
            ],
            1,
        )
        if rc:
            raise Exception("subscribe RC = {}".format(rc))
        print("send SUBSCRIBE for mid {}".format(mid))
        self.incoming_subacks.get_next_item(mid, timeout=10)
        print("SUBACK received for mid {}".format(mid))

    def handle_on_message(self, mqtt_client: mqtt.Client, userdata: Any,
                          message: mqtt.MQTTMessage) -> None:
        # we're inside a Paho thread here.
        # Save the message and return as quickly as possible
        print("received message on {}".format(message.topic))
        self.incoming_messages.add_item(message)

    def get_twin(self) -> Dict[str, Any]:
        """
        Get the device twin
        """

        # Create a topic to publish our GET request.  This is a
        # onetime topic with a request ID built in.
        topic = topic_builder.build_twin_get_publish_topic(
            self.auth.device_id, self.auth.module_id)

        # publish it.
        mi = self.mqtt_client.publish(topic, qos=1)
        if mi.rc:
            raise Exception()

        # use pop_next_twin_response to wait for the response.
        # notice how request_topic is a parameter.  This is used
        # to wait for the response with the specific $rid.
        twin = self.incoming_messages.pop_next_twin_response(
            request_topic=topic, timeout=10)

        # That's it.  We have the Twin
        # TODO: this should use topic_parser.extract_status_code to make sure this didn't fail.
        if twin:
            twin = json.loads(twin.payload)
            print("Got twin = {}".format(twin))
            return twin  # type: ignore
        else:
            print("Error getting twin.")
            return None

    def patch_reported_properties(self, value: str) -> None:
        """
        Patch TWIN reported properties
        """

        # Make the patch
        patch = {"testObject": {"testValue": value}}

        # Build a topic for the PATCH request.
        topic = topic_builder.build_twin_patch_reported_publish_topic(
            self.auth.device_id, self.auth.module_id)

        # publish the patch
        mi = self.mqtt_client.publish(topic, json.dumps(patch), qos=1)
        if mi.rc:
            raise Exception()

        # Use pop_next_twin_response to get our response.
        patch_result = self.incoming_messages.pop_next_twin_response(
            request_topic=topic, timeout=10)

        print("patch_result = {}".format(patch_result))
        # TODO: use topic_parser.extract_status_code

    def main(self) -> None:
        logger.info("Azure IoT Edge Protocol Translation Module (PTM) Sample")

        # Copy/paste code from other sample
        self.auth = SymmetricKeyAuth.create_from_connection_string(
            os.environ["IOTHUB_DEVICE_CONNECTION_STRING"])
        self.mqtt_client = mqtt.Client(self.auth.client_id)
        self.mqtt_client.enable_logger()
        self.mqtt_client.username_pw_set(self.auth.username,
                                         self.auth.password)
        self.mqtt_client.tls_set_context(self.auth.create_tls_context())
        self.mqtt_client.on_connect = self.handle_on_connect
        self.mqtt_client.loop_start()
        self.mqtt_client.connect(self.auth.hostname, self.auth.port)
        if not self.connected.wait(timeout=20):
            logger.error("Failed to connect.")
            sys.exit(1)

        # Paho callsbacks for PUBACK and SUBACK
        self.mqtt_client.on_subscribe = self.handle_on_subscribe
        self.mqtt_client.on_message = self.handle_on_message

        self.subscribe_for_twins()

        self.patch_reported_properties("shazam!")

        self.get_twin()

        end_time = time.time() + 600
        while time.time() < end_time:

            # wait for incoming patches
            patch = self.incoming_messages.pop_next_twin_patch_desired(
                timeout=end_time - time.time())
            if patch:
                print("patch received: {}".format(str(patch.payload)))
                print("patch version: {}".format(
                    topic_parser.extract_twin_version(patch.topic)))

        self.mqtt_client.disconnect()

        logger.info("Exiting.")
예제 #4
0
class SampleApp(object):
    def __init__(self) -> None:
        self.mqtt_client: mqtt.Client = None
        self.auth: SymmetricKeyAuth = None
        self.connected = threading.Event()
        self.incoming_subacks: WaitableDict[int, int] = WaitableDict()
        self.incoming_messages = IncomingMessageList()

    def handle_on_connect(
        self, mqtt_client: mqtt.Client, userdata: Any, flags: Any, rc: int
    ) -> None:
        # Set an event when we're connected so our main thread can continue
        if rc == mqtt.MQTT_ERR_SUCCESS:
            self.connected.set()

    def handle_on_subscribe(
        self,
        client: mqtt.Client,
        userdata: Any,
        mid: int,
        granted_qos: int,
        properties: Any = None,
    ) -> None:
        # we're inside a Paho thread here.
        # Save the results and return as quickly as possible
        logger.info("Received SUBACK for mid {}".format(mid))
        self.incoming_subacks.add_item(mid, granted_qos)

    def subscribe_for_c2d(self) -> None:
        (rc, mid) = self.mqtt_client.subscribe(
            topic_builder.build_c2d_subscribe_topic(
                self.auth.device_id, self.auth.module_id
            ),
            1,
        )
        if rc:
            raise Exception("subscribe RC = {}".format(rc))
        print("send SUBSCRIBE for mid {}".format(mid))
        # wait for the suback.
        self.incoming_subacks.get_next_item(mid, timeout=10)
        print("SUBACK received for mid {}".format(mid))

    def handle_on_message(
        self, mqtt_client: mqtt.Client, userdata: Any, message: mqtt.MQTTMessage
    ) -> None:
        # we're inside a Paho thread here.
        # Save the message and return as quickly as possible
        print("received message on {}".format(message.topic))
        self.incoming_messages.add_item(message)

    def main(self) -> None:
        logger.info("Azure IoT Edge Protocol Translation Module (PTM) Sample")

        # Copy/paste code from other sample
        self.auth = SymmetricKeyAuth.create_from_connection_string(
            os.environ["IOTHUB_DEVICE_CONNECTION_STRING"]
        )
        self.mqtt_client = mqtt.Client(self.auth.client_id)
        self.mqtt_client.enable_logger()
        self.mqtt_client.username_pw_set(self.auth.username, self.auth.password)
        self.mqtt_client.tls_set_context(self.auth.create_tls_context())
        self.mqtt_client.on_connect = self.handle_on_connect
        self.mqtt_client.loop_start()
        self.mqtt_client.connect(self.auth.hostname, self.auth.port)
        if not self.connected.wait(timeout=20):
            logger.error("Failed to connect.")
            sys.exit(1)

        # Paho callsbacks for PUBACK and SUBACK
        self.mqtt_client.on_subscribe = self.handle_on_subscribe
        self.mqtt_client.on_message = self.handle_on_message

        self.subscribe_for_c2d()

        end_time = time.time() + 600
        while time.time() < end_time:
            c2d = self.incoming_messages.pop_next_c2d(
                timeout=end_time - time.time()
            )
            if c2d:
                print("C2d: {}".format(str(c2d.payload)))

        self.mqtt_client.disconnect()

        logger.info("Exiting.")
예제 #5
0
class SampleApp(object):
    def __init__(self) -> None:
        self.mqtt_client: mqtt.Client = None
        self.auth: SymmetricKeyAuth = None
        self.connected = threading.Event()
        self.incoming_subacks: WaitableDict[int, int] = WaitableDict()
        self.incoming_messages = IncomingMessageList()

    def handle_on_connect(
        self, mqtt_client: mqtt.Client, userdata: Any, flags: Any, rc: int
    ) -> None:
        logger.info(
            "handle_on_connect called with rc={} ({})".format(
                rc, mqtt.connack_string(rc)
            )
        )
        # Set an event when we're connected so our main thread can continue
        if rc == mqtt.MQTT_ERR_SUCCESS:
            self.connected.set()
        elif rc == mqtt.CONNACK_REFUSED_SERVER_UNAVAILABLE:
            # actually, server is available, but username is probably wrong
            pass
        elif rc == mqtt.MQTT_ERR_NO_CONN:
            # client_id or password is wrong.  Check the sas token expirey.  That's all we can do
            if self.auth.sas_token_ready_to_renew:
                self.auth.renew_sas_token()

        # TODO: how to handle connection failure?
        # TODO: thread safety

    def handle_sas_token_renewed(self) -> None:
        logger.info("handle_sas_token_renewed")

        # Set the new MQTT auth parameters
        self.mqtt_client.username_pw_set(self.auth.username, self.auth.password)

        # Reconect the client.  (This actually just disconnects it and lets Paho's automatic
        # reconnect connect again.)
        self.mqtt_client.reconnect()

        self.auth.set_sas_token_renewal_timer(self.handle_sas_token_renewed)

    def handle_on_subscribe(
        self,
        client: mqtt.Client,
        userdata: Any,
        mid: int,
        granted_qos: int,
        properties: Any = None,
    ) -> None:
        # we're inside a Paho thread here.
        # Save the results and return as quickly as possible
        logger.info("Received SUBACK for mid {}".format(mid))
        self.incoming_subacks.add_item(mid, granted_qos)

    def subscribe(
        self, topic: Union[str, List[Tuple[Any, int]]], qos: int = 0
    ) -> None:
        (rc, mid) = self.mqtt_client.subscribe(topic, qos)
        if rc:
            raise Exception("subscribe RC = {}".format(rc))
        print("send SUBSCRIBE for mid {}".format(mid))
        self.incoming_subacks.get_next_item(mid, timeout=10)
        print("SUBACK received for mid {}".format(mid))

    def handle_on_message(
        self, mqtt_client: mqtt.Client, userdata: Any, message: mqtt.MQTTMessage
    ) -> None:
        # we're inside a Paho thread here.
        # Save the message and return as quickly as possible
        print("received message on {}".format(message.topic))
        self.incoming_messages.add_item(message)

    def remove_completed_messages(
        self, messages: List[mqtt.MQTTMessageInfo]
    ) -> List[mqtt.MQTTMessageInfo]:
        return [x for x in messages if not x.is_published()]

    def send_telemetry(self) -> None:
        messages_to_send = 20
        outstanding_messages = []
        start = time.time()
        for i in range(0, messages_to_send):
            logger.info("Sending telemetry {}".format(i))
            payload = {
                "index": i,
                "text": "Hello from sample_4.  This is message # {}".format(i),
            }
            msg = Message(payload)

            telemetry_topic = topic_builder.build_telemetry_publish_topic(
                self.auth.device_id, self.auth.module_id, msg
            )
            mi = self.mqtt_client.publish(
                telemetry_topic, msg.get_binary_payload(), qos=1
            )

            outstanding_messages.append(mi)
            outstanding_messages = self.remove_completed_messages(
                outstanding_messages
            )

            print(
                "{} sent, {} awaiting PUBACK".format(
                    i + 1, len(outstanding_messages)
                )
            )

        while len(outstanding_messages):
            print("Waiting for {} messages".format(len(outstanding_messages)))
            outstanding_messages[0].wait_for_publish()
            outstanding_messages = self.remove_completed_messages(
                outstanding_messages
            )
        end = time.time()

        print("Done sending telemetry.")
        print(
            "{} messages sent in {} seconds.  {} messages per second".format(
                messages_to_send, end - start, messages_to_send / (end - start)
            )
        )

    def subscribe_all(self) -> None:
        topics = [
            (
                topic_builder.build_twin_response_subscribe_topic(
                    self.auth.device_id, self.auth.module_id
                ),
                1,
            ),
            (
                topic_builder.build_twin_patch_desired_subscribe_topic(
                    self.auth.device_id, self.auth.module_id
                ),
                1,
            ),
            (
                topic_builder.build_method_request_subscribe_topic(
                    self.auth.device_id, self.auth.module_id
                ),
                1,
            ),
            (
                topic_builder.build_c2d_subscribe_topic(
                    self.auth.device_id, self.auth.module_id
                ),
                1,
            ),
        ]
        print("Subscribing to {}".format(topics))
        self.subscribe(topics)

    def get_twin(self) -> Dict[str, Any]:
        topic = topic_builder.build_twin_get_publish_topic(
            self.auth.device_id, self.auth.module_id
        )
        self.mqtt_client.publish(topic, qos=1)
        # todo wait on mi

        twin = self.incoming_messages.pop_next_twin_response(
            request_topic=topic, timeout=10
        )
        if twin:
            twin = json.loads(twin.payload)
            print("Got twin = {}".format(twin))
            return twin  # type: ignore
        else:
            print("Error getting twin.")
            return None

    def patch_reported_properties(self, value: str) -> None:
        patch = {"testObject": {"testValue": value}}
        topic = topic_builder.build_twin_patch_reported_publish_topic(
            self.auth.device_id, self.auth.module_id
        )
        # todo: easier way to test connection being forced closed: send invalid patch
        self.mqtt_client.publish(topic, json.dumps(patch), qos=1)
        # todo wait on mi

        patch_result = self.incoming_messages.pop_next_twin_response(
            request_topic=topic, timeout=10
        )
        # TODO: raise exception on error
        print("patch_result = {}".format(patch_result))

    def main(self) -> None:
        logger.info("Azure IoT Edge Protocol Translation Module (PTM) Sample")

        # Create an auth object which can help us get the credentials we need
        # in order to connect
        self.auth = SymmetricKeyAuth.create_from_connection_string(
            os.environ["IOTHUB_DEVICE_CONNECTION_STRING"]
        )
        self.auth.set_sas_token_renewal_timer(self.handle_sas_token_renewed)

        # Create an MQTT client object, passing in the credentials we
        # get from the auth object
        self.mqtt_client = mqtt.Client(self.auth.client_id)
        self.mqtt_client.enable_logger()
        self.mqtt_client.username_pw_set(self.auth.username, self.auth.password)
        # In this sample, we use the TLS context that the auth object builds for
        # us.  We could also build our own from the contents of the auth object
        self.mqtt_client.tls_set_context(self.auth.create_tls_context())

        # set a handler to get called when we're connected.
        self.mqtt_client.on_connect = self.handle_on_connect
        self.mqtt_client.on_subscribe = self.handle_on_subscribe
        self.mqtt_client.on_message = self.handle_on_message

        logger.info("Connecting")
        # Start the paho loop.
        self.mqtt_client.loop_start()
        # Connect.  When this returns, we're not actually connected yet.
        # We have to wait for `handle_on_connect` to be called before we
        # know that we're connected.
        self.mqtt_client.connect(self.auth.hostname, self.auth.port)

        # wait for the connectoin, but don't wait too long
        if not self.connected.wait(timeout=20):
            logger.error("Failed to connect.")
            sys.exit(1)

        self.subscribe_all()
        self.send_telemetry()
        self.patch_reported_properties("shazam!")
        self.get_twin()

        start_time = time.time()
        end_time = start_time + 600
        while time.time() < end_time:
            if self.incoming_messages.wait_for_message(end_time - time.time()):

                c2d = self.incoming_messages.pop_next_c2d(timeout=0)
                if c2d:
                    print("C2d: {}".format(str(c2d.payload)))

                twin_patch = self.incoming_messages.pop_next_twin_patch_desired(
                    timeout=0
                )
                if twin_patch:
                    print("twin patch: {}".format(str(twin_patch.payload)))
                    print(
                        "twin version: {}".format(
                            topic_parser.extract_twin_version(twin_patch.topic)
                        )
                    )

                ping = self.incoming_messages.pop_next_method_request(
                    method_name="ping", timeout=0
                )
                if ping:
                    print("ping: {}".format(str(ping.payload)))
                    response_topic = topic_builder.build_method_response_publish_topic(
                        ping.topic, "200"
                    )
                    mi = self.mqtt_client.publish(
                        topic=response_topic, payload=ping.payload, qos=1
                    )
                    mi.wait_for_publish()
                    print("ping response sent")

                fail = self.incoming_messages.pop_next_method_request(
                    method_name="fail", timeout=0
                )
                if fail:
                    print("fail: {}".format(str(fail.payload)))
                    response_topic = topic_builder.build_method_response_publish_topic(
                        fail.topic, "400"
                    )
                    mi = self.mqtt_client.publish(
                        topic=response_topic, payload=fail.payload, qos=1
                    )
                    mi.wait_for_publish()
                    print("fail response sent")

                method_request = self.incoming_messages.pop_next_method_request(
                    timeout=0
                )
                if method_request:
                    print(
                        "method request: {}".format(str(method_request.payload))
                    )
                    print(
                        "method name: {}".format(
                            topic_parser.extract_method_name(
                                method_request.topic
                            )
                        )
                    )

                undefined = self.incoming_messages.pop_next_message(timeout=0)
                if undefined:
                    print(
                        "Undefined: {}, {}".format(
                            undefined.topic, str(undefined.payload)
                        )
                    )

        self.mqtt_client.disconnect()

        logger.info("Exiting.")