开发 C# IoT Edge 模块以使用 Azure Stack Edge Pro FPGA 移动文件

重要

Azure Stack Edge Pro FPGA 设备于 2024 年 2 月达到生命周期结束。

本文逐步介绍如何使用 Azure Stack Edge Pro FPGA 设备创建用于部署的 IoT Edge 模块。 Azure Stack Edge Pro FPGA 是一种存储解决方案,可用于处理数据并通过网络将其发送到 Azure。

可以将 Azure IoT Edge 模块与 Azure Stack Edge Pro FPGA 配合使用,在数据移动到 Azure 时转换数据。 本文中使用的模块实现了将文件从本地共享复制到 Azure Stack Edge Pro FPGA 设备上的云共享的逻辑。

本文教你如何:

  • 创建容器注册表来存储和管理模块(Docker 映像)。
  • 创建 IoT Edge 模块以在 Azure Stack Edge Pro FPGA 设备上部署。

关于 IoT Edge 模块

Azure Stack Edge Pro FPGA 设备可以部署和运行 IoT Edge 模块。 Edge 模块本质上是执行特定任务的 Docker 容器,例如从设备引入消息、转换消息或将消息发送到 IoT 中心。 在本文中,你将创建一个模块,用于将文件从本地共享复制到 Azure Stack Edge Pro FPGA 设备上的云共享。

  1. 文件将写入 Azure Stack Edge Pro FPGA 设备上的本地共享。
  2. 文件事件生成器为写入本地共享的每个文件创建文件事件。 修改文件时也会生成文件事件。 然后,文件事件将发送到 IoT Edge 中心(在 IoT Edge 运行时中)。
  3. IoT Edge 自定义模块处理文件事件以创建文件事件对象,该对象还包含文件的相对路径。 该模块使用相对文件路径生成绝对路径,并将文件从本地共享复制到云共享。 然后,该模块将从本地共享中删除该文件。

Azure IoT Edge 模块在 Azure Stack Edge Pro FPGA 的工作原理

文件进入云共享后,它会自动上传到 Azure 存储帐户。

先决条件

在开始之前,请确保具备:

创建容器注册表

Azure 容器注册表是 Azure 中的专用 Docker 注册表,可在其中存储和管理专用 Docker 容器映像。 云中提供的两种常用 Docker 注册表服务是 Azure 容器注册表和 Docker 中心。 本文使用容器注册表。

  1. 从浏览器登录到 Azure 门户

  2. 选择 创建资源 > 容器 > 容器注册表。 单击 创建

  3. 提供:

    1. 在 Azure 中具有唯一性且包含 5 到 50 个字母数字字符的 注册表名称

    2. 请选择 订阅

    3. 新建或选择现有 资源组

    4. 选择 位置。 建议此位置与与 Azure Stack Edge 资源关联的位置相同。

    5. 管理员用户 切换为 “启用”。

    6. 将 SKU 设置为 基本

      创建容器注册表

  4. 选择 创建

  5. 创建容器注册表后,浏览到该注册表,然后选择 访问密钥

    获取访问密钥

  6. 复制 登录服务器用户名密码的值。 稍后使用这些值将 Docker 映像发布到注册表,并将注册表凭据添加到 Azure IoT Edge 运行时。

创建 IoT Edge 模块项目

以下步骤基于 .NET Core 2.1 SDK 创建 IoT Edge 模块项目。 该项目使用 Visual Studio Code 和 Azure IoT Edge 扩展。

创建新解决方案

创建一个 C# 解决方案模板,可以使用自己的代码进行自定义。

  1. 在 Visual Studio Code 中,选择 视图 > 命令面板 打开 VS Code 命令面板。

  2. 在命令面板中,输入并运行 azure 命令:登录 并按照说明登录 Azure 帐户。 如果已登录,可以跳过此步骤。

  3. 在命令面板中,输入并运行 Azure IoT Edge:新 IoT Edge 解决方案命令。 在命令面板中,提供以下信息来创建解决方案:

    1. 选择要在其中创建解决方案的文件夹。

    2. 为解决方案提供名称或接受默认的 EdgeSolution

      创建新解决方案 1

    3. 选择 C# 模块 作为模块模板。

    4. 将默认模块名称替换为要分配的名称,在本例中,FileCopyModule

      创建新的解决方案 2

    5. 将上一部分中创建的容器注册表指定为第一个模块的映像存储库。 将 localhost:5000 替换为复制的登录服务器值。

      最后一个字符串看起来像 <Login server name>/<Module name>。 在此示例中,字符串为:mycontreg2.azurecr.io/filecopymodule

      创建新的解决方案 3

  4. 转到 文件 > 打开文件夹

    创建新的解决方案 4

  5. 浏览并指向之前创建的 EdgeSolution 文件夹。 VS Code 窗口使用其五个顶级组件加载 IoT Edge 解决方案工作区。 不会编辑 .vscode 文件夹、.gitignore 文件、.env 文件和本文中的 deployment.template.json

    您修改的唯一组件是 modules 文件夹。 此文件夹包含模块和 Docker 文件的 C# 代码,以容器映像的形式生成模块。

    创建新的解决方案 5

使用自定义代码更新模块

  1. 在 VS Code 资源管理器中,打开 模块,> FileCopyModule > Program.cs

  2. FileCopyModule 命名空间顶部,为稍后使用的类型添加以下 using 语句。 Microsoft.Azure.Devices.Client.Transport.Mqtt 是向 IoT Edge 中心发送消息的协议。

    namespace FileCopyModule
    {
        using Microsoft.Azure.Devices.Client.Transport.Mqtt;
        using Newtonsoft.Json;
    
  3. InputFolderPathOutputFolderPath 变量添加到 Program 类。

    class Program
        {
            static int counter;
            private const string InputFolderPath = "/home/input";
            private const string OutputFolderPath = "/home/output";
    
  4. 紧接在上一步之后,添加 FileEvent 类以定义消息正文。

    /// <summary>
    /// The FileEvent class defines the body of incoming messages. 
    /// </summary>
    private class FileEvent
    {
        public string ChangeType { get; set; }
    
        public string ShareRelativeFilePath { get; set; }
    
        public string ShareName { get; set; }
    }
    
  5. Init 方法中,代码创建并配置 ModuleClient 对象。 此对象允许模块使用 MQTT 协议发送和接收消息连接到本地 Azure IoT Edge 运行时。 Init 方法中使用的连接字符串由 IoT Edge 运行时提供给模块。 该代码注册 FileCopy 回调,以便通过 input1 终结点从 IoT Edge 中心接收消息。 将 Init 方法 替换为以下代码。

    /// <summary>
    /// Initializes the ModuleClient and sets up the callback to receive
    /// messages containing file event information
    /// </summary>
    static async Task Init()
    {
        MqttTransportSettings mqttSetting = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only);
        ITransportSettings[] settings = { mqttSetting };
    
        // Open a connection to the IoT Edge runtime
        ModuleClient ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
        await ioTHubModuleClient.OpenAsync();
        Console.WriteLine("IoT Hub module client initialized.");
    
        // Register callback to be called when a message is received by the module
        await ioTHubModuleClient.SetInputMessageHandlerAsync("input1", FileCopy, ioTHubModuleClient);
    }
    
  6. 删除 PipeMessage 方法的代码,并在该位置插入 FileCopy的代码。

        /// <summary>
        /// This method is called whenever the module is sent a message from the IoT Edge Hub.
        /// This method deserializes the file event, extracts the corresponding relative file path, and creates the absolute input file path using the relative file path and the InputFolderPath.
        /// This method also forms the absolute output file path using the relative file path and the OutputFolderPath. It then copies the input file to output file and deletes the input file after the copy is complete.
        /// </summary>
        static async Task<MessageResponse> FileCopy(Message message, object userContext)
        {
            int counterValue = Interlocked.Increment(ref counter);
    
            try
            {
                byte[] messageBytes = message.GetBytes();
                string messageString = Encoding.UTF8.GetString(messageBytes);
                Console.WriteLine($"Received message: {counterValue}, Body: [{messageString}]");
    
                if (!string.IsNullOrEmpty(messageString))
                {
                    var fileEvent = JsonConvert.DeserializeObject<FileEvent>(messageString);
    
                    string relativeFileName = fileEvent.ShareRelativeFilePath.Replace("\\", "/");
                    string inputFilePath = InputFolderPath + relativeFileName;
                    string outputFilePath = OutputFolderPath + relativeFileName;
    
                    if (File.Exists(inputFilePath))                
                    {
                        Console.WriteLine($"Moving input file: {inputFilePath} to output file: {outputFilePath}");
                        var outputDir = Path.GetDirectoryName(outputFilePath);
                        if (!Directory.Exists(outputDir))
                        {
                            Directory.CreateDirectory(outputDir);
                        }
    
                        File.Copy(inputFilePath, outputFilePath, true);
                        Console.WriteLine($"Copied input file: {inputFilePath} to output file: {outputFilePath}");
                        File.Delete(inputFilePath);
                        Console.WriteLine($"Deleted input file: {inputFilePath}");
                    } 
                    else
                    {
                        Console.WriteLine($"Skipping this event as input file doesn't exist: {inputFilePath}");   
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught exception: {0}", ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
    
            Console.WriteLine($"Processed event.");
            return MessageResponse.Completed;
        }
    
  7. 保存此文件。

  8. 还可以 下载此项目的现有代码示例。 然后,您可以验证您保存的文件是否与此示例中的 program.cs 文件一致。

生成 IoT Edge 解决方案

在上一部分中,你创建了一个 IoT Edge 解决方案,并将代码添加到 FileCopyModule,以便将文件从本地共享复制到云共享。 现在,您需要将解决方案构建为容器镜像,并将其推送到您的容器注册表。

  1. 在 VSCode 中,转到终端 > 新终端打开新的 Visual Studio Code 集成终端。

  2. 通过在集成终端中输入以下命令登录到 Docker。

    docker login <ACR login server> -u <ACR username>

    使用从容器注册表复制的登录服务器和用户名。

    生成和推送 IoT Edge 解决方案

  3. 出现密码提示时,请提供密码。 还可以从 Azure 门户中容器注册表中的 访问密钥 检索登录服务器、用户名和密码的值。

  4. 提供凭据后,可以将模块映像推送到 Azure 容器注册表。 在 VS Code 资源管理器中,右键单击 module.json 文件,然后选择 生成和推送 IoT Edge 解决方案

    生成和推送 IoT Edge 解决方案 2

    当你告诉 Visual Studio Code 生成解决方案时,它会在集成终端中运行两个命令:docker 生成和 docker 推送。 这两个命令生成代码,容器化 CSharpModule.dll,然后将代码推送到初始化解决方案时指定的容器注册表。

    系统将提示你选择模块平台。 选择与 Linux 对应的 amd64

    选择平台

    重要

    仅支持 Linux 模块。

    你可能会看到以下可以忽略的警告:

    Program.cs(77,44): 警告 CS1998:此异步方法缺少“await”运算符,并将同步运行。 请考虑使用“await”运算符等待非阻塞 API 调用,或“await Task.Run(...)”在后台线程上执行 CPU 绑定工作。

  5. 可以在 VS Code 集成终端中看到包含标记的完整容器映像地址。 图像地址是从 module.json 文件中 <repository>:<version>-<platform>格式的信息生成的。 在本文中,它应类似于 mycontreg2.azurecr.io/filecopymodule:0.0.1-amd64

后续步骤

若要在 Azure Stack Edge Pro FPGA 上部署并运行此模块,请参阅 添加模块中的步骤。