写点什么

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

  • 2025-04-24
    中国香港
  • 本文字数:5951 字

    阅读完需:约 20 分钟

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

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


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


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

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

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

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

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

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



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


定义 Device CRD


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


👇🏻 以下是一个具体的示例:

apiVersion: devices.kubeedge.io/v1beta1kind: DeviceModelmetadata:  name: temperature-modelspec:  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/v1beta1kind: Devicemetadata:  name: temperature-instance-01  labels:    model: temperature-modelspec:  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 工程:

图片生成的 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.sockcommon:  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/v1kind: Deploymentmetadata:  name: modbus-mapperspec:  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 资讯推荐】玩转 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/

用户头像

还未添加个人签名 2020-02-11 加入

还未添加个人简介

评论

发布
暂无评论
KubeEdge边缘设备管理系列(六):Mapper-Framework开发示例_云计算_华为云原生团队_InfoQ写作社区