示例#1
0
class ServiceHelperSync(object):
    def __init__(self):
        self._client_list = ClientList()
        self._executor = ThreadPoolExecutor()

        self._registry_manager = IoTHubRegistryManager(
            iothub_connection_string)

        self._digital_twin_client = DigitalTwinClient.from_connection_string(
            iothub_connection_string)

        self._eventhub_consumer_client = EventHubConsumerClient.from_connection_string(
            eventhub_connection_string, consumer_group=eventhub_consumer_group)

        self._eventhub_future = self._executor.submit(self._eventhub_thread)

    def start_watching(self, device_id, module_id):
        self._client_list.get_or_create(device_id, module_id)

    def stop_watching(self, device_id, module_id):
        self._client_list.remove(device_id, module_id)

    def get_next_incoming_event(self,
                                device_id,
                                module_id,
                                block=True,
                                timeout=None):
        return self._client_list.get_incoming_event_queue.get(block=block,
                                                              timeout=timeout)

    def set_desired_properties(self, device_id, module_id, desired_props):
        if module_id:
            self._registry_manager.update_module_twin(
                device_id, module_id,
                Twin(properties=TwinProperties(desired=desired_props)), "*")
        else:
            self._registry_manager.update_twin(
                device_id,
                Twin(properties=TwinProperties(desired=desired_props)), "*")

    def invoke_method(
        self,
        device_id,
        module_id,
        method_name,
        payload,
        connect_timeout_in_seconds=None,
        response_timeout_in_seconds=None,
    ):
        request = CloudToDeviceMethod(
            method_name=method_name,
            payload=payload,
            response_timeout_in_seconds=response_timeout_in_seconds,
            connect_timeout_in_seconds=connect_timeout_in_seconds,
        )

        if module_id:
            response = self._registry_manager.invoke_device_module_method(
                device_id, module_id, request)

        else:
            response = self._registry_manager.invoke_device_method(
                device_id, request)

        return response

    def invoke_pnp_command(
        self,
        device_id,
        module_id,
        component_name,
        command_name,
        payload,
        connect_timeout_in_seconds=None,
        response_timeout_in_seconds=None,
    ):
        assert not module_id  # TODO
        if component_name:
            return self._digital_twin_client.invoke_component_command(
                device_id,
                component_name,
                command_name,
                payload,
                connect_timeout_in_seconds,
                response_timeout_in_seconds,
            )
        else:
            return self._digital_twin_client.invoke_command(
                device_id,
                command_name,
                payload,
                connect_timeout_in_seconds,
                response_timeout_in_seconds,
            )

    def get_pnp_properties(self, device_id, module_id):
        assert not module_id  # TODO
        return self._digital_twin_client.get_digital_twin(device_id)

    def update_pnp_properties(self, device_id, module_id, properties):
        assert not module_id  # TODO
        return self._digital_twin_client.update_digital_twin(
            device_id, properties)

    def send_c2d(self, device_id, module_id, payload, properties):
        assert not module_id  # TODO
        self._registry_manager.send_c2d_message(device_id, payload, properties)

    def get_next_eventhub_arrival(self,
                                  device_id,
                                  module_id,
                                  block=True,
                                  timeout=None):
        return self._client_list.get_incoming_event_queue(
            device_id, module_id).get(block=block, timeout=timeout)

    def get_next_reported_patch_arrival(self,
                                        device_id,
                                        module_id,
                                        block=True,
                                        timeout=None):
        return self._client_list.get_incoming_patch_queue(
            device_id, module_id).get(block=block, timeout=timeout)

    def shutdown(self):
        if self._eventhub_consumer_client:
            self._eventhub_consumer_client.close()

    def _convert_incoming_event(self, event):
        event_body = event.body_as_json()
        device_id = get_device_id_from_event(event)
        module_id = None  # TODO: extract module_id

        if get_message_source_from_event(event) == "twinChangeEvents":
            return copy.deepcopy(event_body.get("properties", {}))

        else:
            message = C2dMessage()
            message.device_id = device_id
            message.module_id = module_id
            message.message_body = event_body
            message.content_type = event.message.properties.content_type.decode(
                "utf-8")
            message.system_properties = convert_binary_dict_to_string_dict(
                event.system_properties)
            message.properties = convert_binary_dict_to_string_dict(
                event.properties)
            return message

    def _eventhub_thread(self):
        def on_error(partition_context, error):
            logger.error("EventHub on_error: {}".format(
                str(error) or type(error)))

        def on_partition_initialize(partition_context):
            logger.warning("EventHub on_partition_initialize")

        def on_partition_close(partition_context, reason):
            # commented out because it causes ugly warning spew on shutdown
            # logger.warning("EventHub on_partition_close: {}".format(reason))
            pass

        def on_event(partition_context, event):
            if event:
                device_id = get_device_id_from_event(event)
                module_id = None  # TODO: extract module_id
                if get_message_source_from_event(event) == "twinChangeEvents":
                    queue = self._client_list.get_incoming_patch_queue(
                        device_id, module_id)
                else:
                    queue = self._client_list.get_incoming_event_queue(
                        device_id, module_id)
                if queue:
                    logger.info("Received {} for device {}, module {}".format(
                        get_message_source_from_event(event), device_id,
                        module_id))
                    queue.put(self._convert_incoming_event(event))

        try:
            with self._eventhub_consumer_client:
                logger.info("Starting EventHub receive")
                self._eventhub_consumer_client.receive(
                    on_event,
                    on_error=on_error,
                    on_partition_initialize=on_partition_initialize,
                    on_partition_close=on_partition_close,
                    max_wait_time=3600,
                )
        except Exception:
            logger.error("_eventhub_thread exception", exc_info=True)
            raise
class ServiceHelperSync(object):
    def __init__(self):
        self._executor = ThreadPoolExecutor()

        self._registry_manager = IoTHubRegistryManager(
            iothub_connection_string)

        logger.info(
            "Creating EventHubConsumerClient with consumer_group = {}".format(
                eventhub_consumer_group))

        self._eventhub_consumer_client = EventHubConsumerClient.from_connection_string(
            eventhub_connection_string, consumer_group=eventhub_consumer_group)

        self._eventhub_future = self._executor.submit(self._eventhub_thread)
        self.device_id = None
        self.module_id = None
        self.incoming_patch_queue = queue.Queue()
        self.cv = threading.Condition()
        self.incoming_eventhub_events = {}

    def set_identity(self, device_id, module_id):
        if device_id != self.device_id or module_id != self.module_id:
            self.device_id = device_id
            self.module_id = module_id
            self.incoming_patch_queue = queue.Queue()
            with self.cv:
                if self.incoming_eventhub_events:
                    logger.warning(
                        "Abandoning incoming events with IDs {}".format(
                            str(list(self.incoming_eventhub_events.keys()))))
                self.incoming_eventhub_events = {}

    def set_desired_properties(self, desired_props):
        if self.module_id:
            self._registry_manager.update_module_twin(
                self.device_id,
                self.module_id,
                Twin(properties=TwinProperties(desired=desired_props)),
                "*",
            )
        else:
            self._registry_manager.update_twin(
                self.device_id,
                Twin(properties=TwinProperties(desired=desired_props)), "*")

    def invoke_method(
        self,
        method_name,
        payload,
        connect_timeout_in_seconds=30,
        response_timeout_in_seconds=None,
    ):
        request = CloudToDeviceMethod(
            method_name=method_name,
            payload=payload,
            response_timeout_in_seconds=response_timeout_in_seconds,
            connect_timeout_in_seconds=connect_timeout_in_seconds,
        )

        if self.module_id:
            response = self._registry_manager.invoke_device_module_method(
                self.device_id, self.module_id, request)

        else:
            response = self._registry_manager.invoke_device_method(
                self.device_id, request)

        return response

    def send_c2d(self, payload, properties):
        if self.module_id:
            raise TypeError("sending C2D to modules is not supported")
        self._registry_manager.send_c2d_message(self.device_id, payload,
                                                properties)

    def wait_for_eventhub_arrival(self, message_id, timeout=600):
        def get_event(inner_message_id):
            with self.cv:
                arrivals = self.incoming_eventhub_events

                # if message_id is not set, return any message
                if not inner_message_id and len(arrivals):
                    id = list(arrivals.keys())[0]
                    logger.info(
                        "wait_for_eventhub_arrival(None) returning msgid={}".
                        format(id))
                else:
                    id = inner_message_id

                if id and (id in arrivals):
                    value = arrivals[id]
                    del arrivals[id]
                    return value
                else:
                    return None

        if timeout:
            end_time = time.time() + timeout
        else:
            end_time = None
        with self.cv:
            while True:
                ev = get_event(message_id)
                if ev:
                    return ev
                elif time.time() >= end_time:
                    logger.warning(
                        "timeout waiting for message with msgid={}".format(
                            message_id))
                    return None
                elif end_time:
                    self.cv.wait(timeout=end_time - time.time())
                else:
                    self.cv.wait()

    def get_next_reported_patch_arrival(self, block=True, timeout=20):
        try:
            return self.incoming_patch_queue.get(block=block, timeout=timeout)
        except queue.Empty:
            raise Exception(
                "reported patch did not arrive within {} seconds".format(
                    timeout))

    def shutdown(self):
        if self._eventhub_consumer_client:
            self._eventhub_consumer_client.close()

    def _convert_incoming_event(self, event):
        try:
            event_body = event.body_as_json()
        except TypeError:
            event_body = event.body_as_str()
        device_id = get_device_id_from_event(event)
        module_id = get_module_id_from_event(event)

        if get_message_source_from_event(event) == "twinChangeEvents":
            return copy.deepcopy(event_body.get("properties", {}))

        else:
            message = EventhubEvent()
            message.device_id = device_id
            message.module_id = module_id
            message.message_body = event_body
            if event.message.properties:
                message.properties = convert_binary_dict_to_string_dict(
                    event.properties)
                message.content_type = event.message.properties.content_type.decode(
                    "utf-8")
            message.system_properties = convert_binary_dict_to_string_dict(
                event.system_properties)
            return message

    def _store_eventhub_arrival(self, converted_event):
        message_id = converted_event.system_properties.get(
            "message-id", "no-message-id-{}".format(uuid.uuid4()))
        if message_id:
            with self.cv:
                self.incoming_eventhub_events[message_id] = converted_event
                self.cv.notify_all()

    def _store_patch_arrival(self, converted_event):
        self.incoming_patch_queue.put(converted_event)

    def _eventhub_thread(self):
        def on_error(partition_context, error):
            logger.error("EventHub on_error: {}".format(
                str(error) or type(error)))

        def on_partition_initialize(partition_context):
            logger.warning("EventHub on_partition_initialize")

        def on_partition_close(partition_context, reason):
            logger.warning("EventHub on_partition_close: {}".format(reason))

        def on_event_batch(partition_context, events):
            try:
                for event in events:
                    device_id = get_device_id_from_event(event)
                    module_id = get_module_id_from_event(event)

                    if device_id == self.device_id and module_id == self.module_id:

                        converted_event = self._convert_incoming_event(event)
                        if type(converted_event) == EventhubEvent:
                            if "message-id" in converted_event.system_properties:
                                logger.info(
                                    "Received event with msgid={}".format(
                                        converted_event.
                                        system_properties["message-id"]))
                            else:
                                logger.info(
                                    "Received event with no message id")

                        else:
                            logger.info(
                                "Received {} for device {}, module {}".format(
                                    get_message_source_from_event(event),
                                    device_id,
                                    module_id,
                                ))

                        if isinstance(converted_event, EventhubEvent):
                            self._store_eventhub_arrival(converted_event)
                        else:
                            self._store_patch_arrival(converted_event)
            except Exception:
                logger.error("Error on on_event_batch", exc_info=True)
                raise

        try:
            with self._eventhub_consumer_client:
                logger.info("Starting EventHub receive")
                self._eventhub_consumer_client.receive_batch(
                    max_wait_time=2,
                    on_event_batch=on_event_batch,
                    on_error=on_error,
                    on_partition_initialize=on_partition_initialize,
                    on_partition_close=on_partition_close,
                )
        except Exception:
            logger.error("_eventhub_thread exception", exc_info=True)
            raise
    new_twin = module_twin
    new_twin.properties = TwinProperties(desired={"telemetryInterval": 9000})
    print(new_twin)
    print("")

    replaced_module_twin = iothub_registry_manager.replace_module_twin(
        device_id, module_id, new_twin
    )
    print(replaced_module_twin)
    print("")

    # Update twin
    twin_patch = Twin()
    twin_patch.properties = TwinProperties(desired={"telemetryInterval": 3000})
    updated_module_twin = iothub_registry_manager.update_module_twin(
        device_id, module_id, twin_patch, module_twin.etag
    )
    print(updated_module_twin)
    print("")

    # Get all modules on the device
    all_modules = iothub_registry_manager.get_modules(device_id)
    for module in all_modules:
        print_module_info("", module)

    # Delete Module
    iothub_registry_manager.delete_module(device_id, module_id)
    print("Deleted Module {0}".format(module_id))

except Exception as ex:
    print("Unexpected error {0}".format(ex))