KubeEdge边缘设备管理系列(六):Mapper-Framework开发示例

举报
云容器大未来 发表于 2025/04/18 14:22:14 2025/04/18
【摘要】 在本系列的前几篇文章中,我们详细介绍了基于物模型的设备管理 API、DMI 数据面能力的增强、Mapper-Framework 开发框架的原理、使用 Mapper 进行视频流数据采集上报以及设备数据写入等核心特性,作为系列文章的最终部分,我们将展示如何从零开始开发一个 Mapper 工程,并通过该工程实现对边缘设备的管理。

作者:王彬丞&杨志佳&刘家伟

针对新版本 Device-IoT 领域的更新,我们计划推出一系列的文章对这些特性进行详细的介绍,大致的文章大纲为:

1. 基于物模型的设备管理 API 设计与实现

2. DMI 数据面能力设计与实现

3. Mapper 开发框架 Mapper-Framework 设计与实现

4. 如何使用 Mapper 完成视频流数据处理

5. 如何使用 Mapper 实现设备数据写入

6. 如何从头开发一个 Mapper(以 modbus 为例) 

封面2.png

在本系列的前几篇文章中,我们详细介绍了基于物模型的设备管理 API、DMI 数据面能力的增强、Mapper-Framework 开发框架的原理、使用 Mapper 进行视频流数据采集上报以及设备数据写入等核心特性,作为系列文章的最终部分,我们将展示如何从零开始开发一个 Mapper 工程,并通过该工程实现对边缘设备的管理。

  

定义Device CRD  

    用户需要按照 KubeEdge v1beta1 版本的 Device CRD 定义,以物模型的形式构建 device-model 与 device-instance,具体的定义细节可以参考本系列的第一篇文章

    👇🏻 以下是一个具体的示例:
    apiVersion: devices.kubeedge.io/v1beta1
    kind: DeviceModel
    metadata:
      name: temperature-model
    spec:
      properties:
        - name: temp                      # define device property
          description: The temperature collected by the sensor
          type: INT                       # date type of device property
          accessMode: ReadWrite
      protocol: modbus                    # protocol for device, need to be same with device instance
    

    首先,我们定义了一个名为 temperature-model 的 device-model,该模型用于定义一类温度传感器的共同特性。在这个示例中,temperature-model 描述了温度传感器的一个重要属性 temp。该属性表示温度传感器采集到的温度值,数据类型为 int。此外,设备的通信协议采用了 Modbus 协议,并且 temp 属性的访问方式被设置为“读写”模式。这意味着,Mapper 将会对从设备采集到的数据进行归一化或其他处理后,返回标准化的结果。

    apiVersion: devices.kubeedge.io/v1beta1
    kind: Device
    metadata:
      name: temperature-instance-01
      labels:
        model: temperature-model
    spec:
      deviceModelRef:
        name: temperature-model
      protocol:
        protocolName: modbus
        configData:
          serialPort: '/dev/ttyS0'
          baudRate: 9600
          dataBits: 8
          parity: even
          stopBits: 1
      nodeName: edge-node
      properties:
        - name: temp
          visitors:
            protocolName: modbus
            configData:
              register: HoldingRegister
              offset: 2
              limit: 1
              scale: 1
              isSwap: true
              isRegisterSwap: true
          reportCycle: 10000
          collectCycle: 10000
          reportToCloud: true
          pushMethod:
            mqtt:
              address: tcp://127.0.0.1:1883
              topic: temperature
              qos: 0
              retained: false

    接下来,我们定义了一个名为 temperature-instance-01 的 device-instance,代表一个实际的 Modbus 温度传感器设备。在定义中,spec.protocol 字段需要具体说明设备的通信协议参数,在示例中使用 Modbus 协议,并为其定义了典型的通信参数,如串口、波特率等。Mapper 将根据这些参数与设备进行通信,完成数据的采集与交互。

    在 spec.properties 字段中,我们定义了与设备模型中一致的 temp 属性,并进一步细化了访问方式。在 visitors 字段中,我们详细列出了 temp 属性的访问方式,包括寄存器类型、寄存器地址以及偏移量等信息。根据这些定义,Mapper 会准确地访问设备的寄存器,从而获取所需的数据。

    最后,在 spec.properties.pushMethod 字段中,我们定义了设备数据的推送方式。DMI 数据面支持将设备数据推送到多种目标,包括数据库、用户应用等。具体的推送方式可以参考本系列的第二篇文章

     构建Modbus Mapper工程

    如果开发者需要构建自定义的 Mapper 插件,可以通过 KubeEdge 的 Mapper-Framework 子仓库生成 Mapper 工程模板。接下来,开发者只需根据设备的具体信息,填充设备初始化、数据采集等功能,即可完成自定义 Mapper 插件的构建。

    👇🏻 以下是相关示例:

    1、生成 Mapper 工程

    开发者首先通过 git clone 命令克隆 KubeEdge 的 Mapper-Framework 仓库[1] 随后使用 git checkout 指令切换到稳定版本(目前支持的版本为1.16至1.20)并执行 make generate 命令,输入自定义 Mapper 的名称及是否处理流数据等信息,最终生成 Mapper 工程:

    11.png

    生成的 modbus mapper 工程将会被构建到 Mapper-Framework 的同级目录,工程结构可以参考本系列的第三篇文章中提供的详细说明。


    2、实现设备驱动功能

    在大多数情况下,开发者需要填充 Mapper 工程中的 driver 目录下的两个文件以及 Mapper 的配置文件。

    主要的工作集中在 devicetype.go 和 driver.go 文件中。在 devicetype.go 文件中,开发者需要填充设备协议的配置信息及属性访问的字段,而在 driver.go 文件中,需要实现设备初始化、数据获取等具体功能。

    👇🏻 以下是相关文件的示例:
    type ProtocolConfig struct {
        ProtocolName string `json:"protocolName"`
        ConfigData   `json:"configData"`
    }
    
    type ConfigData struct {
        // TODO: add your protocol config data
        SerialPort string `json:"serialPort"`
        DataBits   int    `json:"dataBits"`
        BaudRate   int    `json:"baudRate"`
        Parity     string `json:"parity"`
        StopBits   int    `json:"stopBits"`
    }
    
    type VisitorConfig struct {
        ProtocolName      string `json:"protocolName"`
        VisitorConfigData `json:"configData"`
    }
    
    type VisitorConfigData struct {
        // TODO: add your visitor config data
        DataType       string  `json:"dataType"`
        Register       string  `json:"register"`
        Offset         uint16  `json:"offset"`
        Limit          int     `json:"limit"`
        Scale          float64 `json:"scale"`
        IsSwap         bool    `json:"isSwap"`
        IsRegisterSwap bool    `json:"isRegisterSwap"`
    }

    在 devicetype.go 文件中,开发者需要填充协议参数(如 ConfigData)和属性访问参数(如 VisitorConfigData)。这些定义必须与 device-instance 中的字段保持一致,以确保 Mapper 能正确解析来自 device-instance 配置文件的参数值,从而实现设备与应用之间的数据交互。

    var clients *sync.Map
    
    var clientInit sync.Once
    
    func initMap() {
        clientInit.Do(func() {
            if clients == nil {
                clients = new(sync.Map)
            }
         })
    }
    
    func NewClient(protocol ProtocolConfig) (*CustomizedClient, error) {
        client := &CustomizedClient{
            ProtocolConfig: protocol,
            deviceMutex:    sync.Mutex{},
            // TODO initialize the variables you added
         }
         return client, nil
    }
    
    func (c *CustomizedClient) InitDevice() error {
    
        initMap()
        klog.Infoln("SerialPort : ", c.ProtocolConfig.SerialPort)
        v, ok := clients.Load(c.ProtocolConfig.SerialPort)
        if ok {
            c.ModbusClient = v.(modbus.Client)
            return nil
        }
    
        handler := modbus.NewRTUClientHandler(c.ProtocolConfig.SerialPort)
        handler.BaudRate = c.ProtocolConfig.BaudRate
        handler.DataBits = c.ProtocolConfig.DataBits
        handler.Parity = parity(c.ProtocolConfig.Parity)
        handler.StopBits = c.ProtocolConfig.StopBits
        client := modbus.NewClient(handler)
        clients.Store(c.ProtocolConfig.SerialPort, &client)
        c.ModbusClient = client
    
        return nil
    }
    
    func (c *CustomizedClient) GetDeviceData(visitor *VisitorConfig) (interface{}, error) {
        c.deviceMutex.Lock()
        defer c.deviceMutex.Unlock()
    
        var results []byte
        var err error
        switch visitor.Register {
        case "CoilRegister":
            results, err = c.ModbusClient.ReadCoils(visitor.Offset, uint16(visitor.Limit))
        case "DiscreteInputRegister":
            results, err = c.ModbusClient.ReadDiscreteInputs(visitor.Offset, uint16(visitor.Limit))
        case "HoldingRegister":
            results, err = c.ModbusClient.ReadHoldingRegisters(visitor.Offset, uint16(visitor.Limit))
        case "InputRegister":
            results, err = c.ModbusClient.ReadInputRegisters(visitor.Offset, uint16(visitor.Limit))
        default:
            return nil, errors.New("Bad register type")
        }
        klog.V(2).Info("Get result: ", results)
        return results, err
    }
    
    func (c *CustomizedClient) StopDevice() error {
        // TODO: stop device
        // you can use c.ProtocolConfig
        err := c.ModbusClient.Close()
        if err != nil {
            return err
        }
        return nil
    }

    在 driver.go 文件中,开发者根据 devicetype.go 中解析得到的协议配置字段和属性访问参数,实现设备驱动的具体功能。例如,在上面的示例中,我们实现了 Modbus 设备的初始化、数据获取以及设备停止等功能。具体实现需要根据所使用设备的协议和操作需求进行定制。

    除了设备驱动的实现外,开发者还需要修改 Mapper 的配置文件 config.yaml。一般来说,开发者只需填充 Mapper 的协议名字段(protocol)即可:

    grpc_server:
      socket_path: /etc/kubeedge/modbus.sock
    common:
      name: Modbus-mapper
      version: v1.13.0
      api_version: v1.0.0
      protocol: modbus # TODO add your protocol name
      address: 127.0.0.1
      edgecore_sock: /etc/kubeedge/dmi.sock
    


      部署自定义Mapper插件

    完成自定义 Mapper 工程的构建后,接下来就可以进行编译并将其部署到 KubeEdge 集群中。目前,推荐使用两种部署方式:二进制部署和 Deployment 部署

     二进制部署

    二进制部署适合在开发和调试阶段使用。这种方式操作简便,适合快速运行和排查错误。通过以下命令,开发者可以直接在本地编译并运行 Mapper 插件:

    go run cmd/main.go --v <log level,like 3> --config-file <path to config yaml>

    在这条命令中,--v 参数用来设置日志级别,日志级别越高,输出的信息越详细;--config-file 参数则指定了 Mapper 配置文件的路径。

     Deployment 部署

    当 Mapper 插件经过充分调试并能够稳定运行后,建议将其以容器的形式部署到 KubeEdge 集群中。通过使用 Mapper-Framework 生成的 Dockerfile,开发者可以构建 Docker 镜像并创建 Kubernetes Deployment,以便在生产环境中稳定运行。

    👇🏻 以下是一个示例 Deployment 配置:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: modbus-mapper
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demo
      template:
        metadata:
          labels:
            app: demo
        spec:
          nodeName: edge-node # replace with your edge node name
          containers:
            - name: demo
              volumeMounts: # Required, mapper need to communicate with grpcclient and get the config
                - name: test-volume
                  mountPath: /etc/kubeedge
              image: modbus-mapper:v1.0.0 # Replace with your mapper image name
              imagePullPolicy: IfNotPresent
              resources:
                limits:
                  cpu: 300m
                  memory: 500Mi
                requests:
                  cpu: 100m
                  memory: 100Mi
              command: ["/bin/sh","-c"]
              args: ["/kubeedge/main --config-file /kubeedge/config.yaml --v 4"]
          volumes:
            - name: test-volume
              hostPath:
                path: /etc/kubeedge
                type: Directory

    完成自定义 Mapper 插件的部署后,用户可以向集群创建 device-model 和 device-instance 资源,随后 Mapper 将收到通知并开始进行设备的管理,定期获取设备数据并进行推送。这样,就完成了边缘设备的云原生化管理,使设备的生命周期、数据采集、处理和推送过程都能够在云原生架构下高效、可靠地进行。

    通过本系列技术文章的深入探讨,我们全面分析了 KubeEdge 在 Device-IoT 领域的创新与实践,重点涵盖了基于物模型的设备管理 API、DMI 数据面能力的增强、Mapper-Framework 的设计与实现、使用 Mapper 完成视频流数据处理、设备数据写入以及自定义 Mapper 工程开发的实际案例。这些内容充分展示了 KubeEdge 如何有效推动边缘计算与物联网技术的融合,为边缘设备的云原生化管理提供了切实可行的解决方案。

    随着物联网设备的日益普及以及边缘计算需求的持续增长,KubeEdge 在设备管理和数据处理方面的独特优势将愈发突出。通过这一系列功能的实现,KubeEdge 为物联网设备的管理、数据流的处理以及边缘计算任务的执行提供了强大支持。我们期待更多开发者加入 KubeEdge 社区,共同为全球的边缘计算生态贡献力量。 


    相关链接:

    [1] Mapper-Framework 仓库:https://github.com/kubeedge/mapper-framework


    kubeedge小助手.png

    扫码回复“KubeEdge”

    进入技术交流群



    【更多KubeEdge资讯推荐】玩转KubeEdge保姆级攻略——环境搭建篇


    玩转KubeEdge保姆级攻略——环境搭建篇

    《玩转KubeEdge保姆级攻略——环境搭建篇》课程主要介绍如何通过华为云服务快速搭建一套KubeEdge边缘计算开发平台及部署Sedna、EdgeMesh等KubeEdge生态组件。

    课程免费学习链接:https://connect.huaweicloud.com/courses/learn/course-v1:HuaweiX+CBUCNXNX022+Self-paced/about

    KubeEdge社区介绍:KubeEdge是业界首个云原生边缘计算框架、云原生计算基金会(CNCF)唯一毕业级边缘计算开源项目,社区已完成业界最大规模云原生边云协同高速公路项目(统一管理10万边缘节点/50万边缘应用)、业界首个云原生星地协同卫星、业界首个云原生车云协同汽车、业界首个云原生油田项目,开源业界首个分布式协同AI框架Sedna及业界首个边云协同终身学习范式,并在持续开拓创新中。

    KubeEdge网站 :  https://kubeedge.io

    GitHub地址 : https://github.com/kubeedge/kubeedge

    Slack地址 : https://kubeedge.slack.com

    邮件列表 : https://groups.google.com/forum/#!forum/kubeedge

    每周社区例会 : https://zoom.us/j/4167237304

    Twitter : https://twitter.com/KubeEdge

    文档地址 : https://docs.kubeedge.io/en/latest/

    【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
    • 点赞
    • 收藏
    • 关注作者

    评论(0

    0/1000
    抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。