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

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

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

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

开发可延迟更新的 Hello World 组件
  1. 在开发计算机上,为组件源代码创建一个文件夹。

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

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

    nano gdk-config.json

    将以下 JSON 复制到文件中。

    • 用你的名字替换 Amazon

    • us-west-2 替换为核心Amazon Web Services 区域设备的运行位置。GDK CLI 将在此Amazon Web Services 区域发布该组件。

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

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

    { "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" }

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

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

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

    • GDK CLI Amazon Web Services 区域 在那里发布 Greengrass 组件。

    • GDK CLI 上传组件工件的 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 应用程序执行以下操作:

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

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

    • 使用Amazon IoT Device SDK适用于 Python 的 v2 中的 IPC 客户端 V 2 与核心软件进行通信。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 GreengrassCore 软件不发送本地部署的更新通知,因此您可以使用Amazon IoT Greengrass云服务部署此组件来对其进行测试。

  4. 使用文本编辑器在名为recipe.json或的文件中创建组件配方recipe.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生命周期。

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

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

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