步骤 2:开发可延迟更新的组件 - Amazon IoT Greengrass
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

步骤 2:开发可延迟更新的组件

在本节中,您将在 Python 中开发一个 Hello World 组件,当核心设备的电池电量低于您在部署组件时配置的阈值时,该组件会延迟组件更新。在此组件中,您将使用适用于 Python 的 Amazon IoT Device SDK v2 中的进程间通信 (IPC) 接口。当核心设备收到部署时,您可以使用该SubscribeToComponentUpdatesIPC操作来接收通知。然后,您可以使用该DeferComponentUpdateIPC操作根据设备的电池电量来推迟或确认更新。

要开发可延迟更新的 Hello World 组件,请执行以下步骤:
  1. 在开发计算机上,为组件源代码创建一个文件夹。

    mkdir com.example.BatteryAwareHelloWorld cd com.example.BatteryAwareHelloWorld
  2. 使用文本编辑器创建名为 gdk-config.json 的文件。从名gdk-config.json为的GDKCLI配置文件中GDKCLI读取内容以生成和发布组件。此配置文件存在于组件文件夹的根目录中。

    例如,在基于 Linux 的系统上,你可以运行以下命令来使用 GNU nano 来创建文件。

    nano gdk-config.json

    将以下内容复制JSON到文件中。

    • Amazon用你的名字替换。

    • us-west-2替换为核心设备的运行 Amazon Web Services 区域 位置。在GDKCLI其中发布了组件 Amazon Web Services 区域。

    • greengrass-component-artifacts替换为要使用的 S3 存储桶前缀。当您使用发布组件时,会使用以下格式将组件的构件GDKCLI上传到 S3 存储桶,该存储桶的名称由此值 Amazon Web Services 区域、和您的 Amazon Web Services 账户 ID 组成:bucketPrefix-region-accountId。GDK CLI

      例如,如果您指定greengrass-component-artifactsus-west-2,且您的 Amazon Web Services 账户 ID 为123456789012,则GDKCLI使用名为的 S3 存储桶greengrass-component-artifacts-us-west-2-123456789012

    { "component": { "com.example.BatteryAwareHelloWorld": { "author": "Amazon", "version": "NEXT_PATCH", "build": { "build_system" : "zip" }, "publish": { "region": "us-west-2", "bucket": "greengrass-component-artifacts" } } }, "gdk_version": "1.0.0" }

    配置文件可指定以下内容:

    • 将 Greengrass GDK CLI 组件发布到云服务时要使用的版本。 Amazon IoT Greengrass NEXT_PATCH指定在 Amazon IoT Greengrass 云服务中可用的最新版本之后选择下一个补丁版本。如果该组件在 Amazon IoT Greengrass 云服务中还没有版本,则GDKCLI使用1.0.0

    • 组件生成系统。当你使用zip编译系统时,会将组件的源代码打GDKCLI包成一个ZIP文件,该文件将成为组件的单个工件。

    • GDKCLI发布 Greengrass 组件 Amazon Web Services 区域 的地方。

    • GDKCLI上传组件工件的 S3 存储桶的前缀。

  3. 使用文本编辑器在名为 main.py 的文件中创建组件源代码。

    例如,在基于 Linux 的系统上,你可以运行以下命令来使用 GNU nano 来创建文件。

    nano main.py

    将以下 Python 代码复制到该文件中。

    import json import os import sys import time import traceback from pathlib import Path from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2 HELLO_WORLD_PRINT_INTERVAL = 15 # Seconds DEFER_COMPONENT_UPDATE_INTERVAL = 30 * 1000 # Milliseconds class BatteryAwareHelloWorldPrinter(): def __init__(self, ipc_client: GreengrassCoreIPCClientV2, battery_file_path: Path, battery_threshold: float): self.battery_file_path = battery_file_path self.battery_threshold = battery_threshold self.ipc_client = ipc_client self.subscription_operation = None def on_component_update_event(self, event): try: if event.pre_update_event is not None: if self.is_battery_below_threshold(): self.defer_update(event.pre_update_event.deployment_id) print('Deferred update for deployment %s' % event.pre_update_event.deployment_id) else: self.acknowledge_update( event.pre_update_event.deployment_id) print('Acknowledged update for deployment %s' % event.pre_update_event.deployment_id) elif event.post_update_event is not None: print('Applied update for deployment') except: traceback.print_exc() def subscribe_to_component_updates(self): if self.subscription_operation == None: # SubscribeToComponentUpdates returns a tuple with the response and the operation. _, self.subscription_operation = self.ipc_client.subscribe_to_component_updates( on_stream_event=self.on_component_update_event) def close_subscription(self): if self.subscription_operation is not None: self.subscription_operation.close() self.subscription_operation = None def defer_update(self, deployment_id): self.ipc_client.defer_component_update( deployment_id=deployment_id, recheck_after_ms=DEFER_COMPONENT_UPDATE_INTERVAL) def acknowledge_update(self, deployment_id): # Specify recheck_after_ms=0 to acknowledge a component update. self.ipc_client.defer_component_update( deployment_id=deployment_id, recheck_after_ms=0) def is_battery_below_threshold(self): return self.get_battery_level() < self.battery_threshold def get_battery_level(self): # Read the battery level from the virtual battery level file. with self.battery_file_path.open('r') as f: data = json.load(f) return float(data['battery_level']) def print_message(self): message = 'Hello, World!' if self.is_battery_below_threshold(): message += ' Battery level (%d) is below threshold (%d), so the component will defer updates' % ( self.get_battery_level(), self.battery_threshold) else: message += ' Battery level (%d) is above threshold (%d), so the component will acknowledge updates' % ( self.get_battery_level(), self.battery_threshold) print(message) def main(): # Read the battery threshold and virtual battery file path from command-line args. args = sys.argv[1:] battery_threshold = float(args[0]) battery_file_path = Path(args[1]) print('Reading battery level from %s and deferring updates when below %d' % ( str(battery_file_path), battery_threshold)) try: # Create an IPC client and a Hello World printer that defers component updates. ipc_client = GreengrassCoreIPCClientV2() hello_world_printer = BatteryAwareHelloWorldPrinter( ipc_client, battery_file_path, battery_threshold) hello_world_printer.subscribe_to_component_updates() try: # Keep the main thread alive, or the process will exit. while True: hello_world_printer.print_message() time.sleep(HELLO_WORLD_PRINT_INTERVAL) except InterruptedError: print('Subscription interrupted') hello_world_printer.close_subscription() except Exception: print('Exception occurred', file=sys.stderr) traceback.print_exc() exit(1) if __name__ == '__main__': main()

    该 Python 应用程序执行以下操作:

    • 从您稍后将在核心设备上创建的虚拟电池电量文件中读取核心设备的电池电量。该虚拟电池电量文件模拟真实电池,因此您可以在没有电池的核心设备上完成本教程。

    • 读取电池电量阈值和虚拟电池电量文件路径的命令行参数。组件配方根据配置参数设置这些命令行参数,因此您可以在部署组件时自定义这些值。

    • 使用适用于 Python 的Amazon IoT Device SDK v2 中的IPC客户端 V2 与 Amazon IoT Greengrass 核心软件进行通信。与原始IPC客户端相比,IPC客户端 V2 减少了IPC在自定义组件中使用所需编写的代码量。

    • 使用SubscribeToComponentUpdatesIPC操作订阅更新通知。C Amazon IoT Greengrass ore 软件在每次部署之前和之后都会发送通知。每次收到通知时,该组件都会调用以下函数。如果通知涉及即将进行的部署,则组件会检查电池电量是否低于阈值。如果电池电量低于阈值,则组件会使用该DeferComponentUpdateIPC操作将更新推迟 30 秒。否则,如果电池电量不低于阈值,则组件会确认更新,因此更新可以继续进行。

      def on_component_update_event(self, event): try: if event.pre_update_event is not None: if self.is_battery_below_threshold(): self.defer_update(event.pre_update_event.deployment_id) print('Deferred update for deployment %s' % event.pre_update_event.deployment_id) else: self.acknowledge_update( event.pre_update_event.deployment_id) print('Acknowledged update for deployment %s' % event.pre_update_event.deployment_id) elif event.post_update_event is not None: print('Applied update for deployment') except: traceback.print_exc()
      注意

      Amazon IoT Greengrass Core 软件不发送本地部署的更新通知,因此您可以使用 Amazon IoT Greengrass 云服务部署此组件来对其进行测试。

  4. 使用文本编辑器在名为 recipe.jsonrecipe.yaml 的文件中创建组件配方。组件配方定义了组件的元数据、默认配置参数和平台特定的生命周期脚本。

    JSON

    例如,在基于 Linux 的系统上,你可以运行以下命令来使用 GNU nano 来创建文件。

    nano recipe.json

    将以下内容复制JSON到文件中。

    { "RecipeFormatVersion": "2020-01-25", "ComponentName": "COMPONENT_NAME", "ComponentVersion": "COMPONENT_VERSION", "ComponentDescription": "This Hello World component defers updates when the battery level is below a threshold.", "ComponentPublisher": "COMPONENT_AUTHOR", "ComponentConfiguration": { "DefaultConfiguration": { "BatteryThreshold": 50, "LinuxBatteryFilePath": "/home/ggc_user/virtual_battery.json", "WindowsBatteryFilePath": "C:\\Users\\ggc_user\\virtual_battery.json" } }, "Manifests": [ { "Platform": { "os": "linux" }, "Lifecycle": { "install": "python3 -m pip install --user awsiotsdk --upgrade", "Run": "python3 -u {artifacts:decompressedPath}/com.example.BatteryAwareHelloWorld/main.py \"{configuration:/BatteryThreshold}\" \"{configuration:/LinuxBatteryFilePath}\"" }, "Artifacts": [ { "Uri": "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/com.example.BatteryAwareHelloWorld.zip", "Unarchive": "ZIP" } ] }, { "Platform": { "os": "windows" }, "Lifecycle": { "install": "py -3 -m pip install --user awsiotsdk --upgrade", "Run": "py -3 -u {artifacts:decompressedPath}/com.example.BatteryAwareHelloWorld/main.py \"{configuration:/BatteryThreshold}\" \"{configuration:/WindowsBatteryFilePath}\"" }, "Artifacts": [ { "Uri": "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/com.example.BatteryAwareHelloWorld.zip", "Unarchive": "ZIP" } ] } ] }
    YAML

    例如,在基于 Linux 的系统上,你可以运行以下命令来使用 GNU nano 来创建文件。

    nano recipe.yaml

    将以下内容复制YAML到文件中。

    --- RecipeFormatVersion: "2020-01-25" ComponentName: "COMPONENT_NAME" ComponentVersion: "COMPONENT_VERSION" ComponentDescription: "This Hello World component defers updates when the battery level is below a threshold." ComponentPublisher: "COMPONENT_AUTHOR" ComponentConfiguration: DefaultConfiguration: BatteryThreshold: 50 LinuxBatteryFilePath: "/home/ggc_user/virtual_battery.json" WindowsBatteryFilePath: "C:\\Users\\ggc_user\\virtual_battery.json" Manifests: - Platform: os: linux Lifecycle: install: python3 -m pip install --user awsiotsdk --upgrade Run: python3 -u {artifacts:decompressedPath}/com.example.BatteryAwareHelloWorld/main.py "{configuration:/BatteryThreshold}" "{configuration:/LinuxBatteryFilePath}" Artifacts: - Uri: "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/com.example.BatteryAwareHelloWorld.zip" Unarchive: ZIP - Platform: os: windows Lifecycle: install: py -3 -m pip install --user awsiotsdk --upgrade Run: py -3 -u {artifacts:decompressedPath}/com.example.BatteryAwareHelloWorld/main.py "{configuration:/BatteryThreshold}" "{configuration:/WindowsBatteryFilePath}" Artifacts: - Uri: "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/com.example.BatteryAwareHelloWorld.zip" Unarchive: ZIP

    此配方可指定以下内容:

    • 电池阈值、Linux 核心设备上的虚拟电池文件路径以及 Windows 核心设备上的虚拟电池文件路径的默认配置参数。

    • 安装适用于 Python 的最新版本 Amazon IoT Device SDK v2 的 install 生命周期。

    • run 中运行 Python 应用程序的 main.py 生命周期。

    • 占位符,例如COMPONENT_NAMECOMPONENT_VERSION,在构建组件配方时会GDKCLI替换信息。

    有关组件配方的更多信息,请参阅 Amazon IoT Greengrass 组件配方参考