工业物联网关-modbus数据采集程序(1-程序设计)

news2024/12/26 23:02:02

写代码之前

最近代码写慢了,磨了好久都没开始动手写代码。考虑的东西越多越多,甚至自己都认为过虑了。就像这个程序,写代码之前估计花了大半天或者一天在思考怎么写,不知道是好事还是年纪大了。所以专门写篇文章,把自己思考的内容写出来,作个回顾。

一, 需求

需求一定要了解清楚才动手,不然后面改出花来。这个程序看起来不算复杂,要实现的功能就是通过串口或者TCP实现Modbus协议,采集数据并存储到redis去,也会处理写操作和透传。(现在回顾起来,当时慢的一个原因是产品定义是自己,需求其实没想清楚,想需求时又考虑了技术实现,应该要优化。)

1.1 架构中的层次及接口

系统业务软件整体架构在《工业物联网关-整体框架》文章有介绍,业务软件架构如下图:
进程模块划分
当前要实现的是上图中"底层采集程序(C语言)"这个软件,从图可以看出,上层接口是redis软件,包括了缓存和消息队列接口。下层是串口和网口,对接的是终端设备,软件要实现modbus-rtumodbus-tcp通信。

1.2 配置

需要通过串口和网口与哪些设备通信,采集的数据区有哪些,数据要解析为什么变量,这些都需要用户配置。我们网关提供了web界面给用户,支持手动和excel(CSV)导入的方式生成配置文件,程序读取这些配置后才能正常工作。
参考了物模型的概念(后面有更多的内容),有产品,设备配置文件,里面包括了数据区和变量的配置。串口和网口配置文件,主要包括通信参数和外挂了哪些设备。
一些思考:web 那边有提过下发配置给网关,可能跟现在差不多,是json格式的。这个兼容性的问题就值思考了,web那边是随时可以改的。网关的话,发货后再改就麻烦,就算是远程升级也是麻烦。目前的想法是找个标准,大家支持,web端可以生成配置文件,网关来导入,例如都支持thingsboard的标准。如果web要改,那恐怕是要支持另一个标准了,同时网关也得跟着改。

1.3 log & debug

开发阶段可以用gdb和printf来调试,后期debug主要靠log了,另外需要在web界面上dump通信的报文,以调试设备通信及协议等问题。log 支持开关和打印等级功能,支持输出到文件功能,要注意log文件不能太大。(调试和LOG相关花了不少时间,包括打印开关和等级控制,输出到文件后怎么清掉,web端怎么读log。其实最开始应该就用printf和gdb好了,log什么的后面再改,反正不影响程序逻辑。)

二, 数据结构

2.1 数据格式转换

在物联网之前,web跟硬件设备交集不多,又因为各自生存土壤的区别,数据存储及格式会有差异。对资源非常吝啬的硬件设备喜欢用寄存器存储数据,1个字节1个bit都斤斤计较,以节省存储空间和减少CPU(MCU)的运算。而web端相对财大气粗,不同含义的数据用变量解耦出来,分开处理和存储。更喜欢考虑分布式存储,负载均衡等技术。一个典型的例子是设备有个寄存器,有8bit,低4个bit代表某个数值,范围就是0-15,高4个bit分别代表某个硬件开关,例如为0就亮灯,为1就灭灯之类。到了web端通常用1个整型变量和1个boolean数组(或4个boolean变量)来存储。
网关的其中一个任务就是把设备的数据格式转化为web端的数据格式。参考过往modbus协议的生态和物模型的概念,常规的转换流程是: 寄存器 - 数据区 - 变量,双向的。网关是不知道数据的含义的,所以是提供配置方式,让用户来配置。
变量可以用一系列的属性来描述,下图是阿里云配置变量属性的界面:
属性配置界面
配置完成后生成一个json格式的数据结构:

{
    "code": "energy_ALL",
    "name": "合相有功电能",
    "unit": "Kwh",
    "area_code": "elec_power",
    "area_offset": 120,
    "value_type": "float",
    "value_size": 4,
    "byte_order": "1234",
    "value_base": 0,
    "value_ratio": 0.1
}

这个数据结构在web端对应的变量可能是:

private double energy_ALL; // 合相有功电能,单位是:Kwh

网关缓存了一个标识名为"elec_power"的数据区,这个变量的值在数据区偏移120字节的位置,占4个字节,字节序是小头(1234),数据类型是浮点型(float)。

2.2 程序数据结构设计

为了适配物联网数据转换的需求,程序里有物模型相关的产品类,设备类,有描述变量的属性类。有硬件设备相关的数据区结构体,整体如下:
数据结构
上图的数据结构还关联了串口类,网口类,与这里无关,可以忽略。
如上图所示,属性描述类"attribute_desc_s"是变量相关的最顶层,产品类里面包含多个这样的描述,设备类其实是产品的一个对象,所以也会继承属性类的内容。这些数据结构代码如下:

// 属性结构体
struct attribute_desc_s
{
    char *code; // 唯一标识
    char *name; // 名字
    char *unit; // 单位
    char *area_code; // 数据区标识符
    unsigned int area_offset;    // 数据区偏移,单位:byte
    unsigned int bit_mask;       // 掩码,只有boolean 类型有用
    unsigned short value_size;   // 值占用空间大小,单位是byte
    unsigned char attr_type;     // 属性类型, ATTR_TYPE_, 目前就属性和遥测2种
    unsigned char value_type;    // 值类型, VAL_TYPE_
    unsigned char byte_order;    // 字节序, 
    double value_base;           // 基值(默认0)
    double value_ratio;          // 系数(默认1.0)
};

// 产品结构体
struct product_s
{
    char *code; // 唯一标识
    char *name; // 名字
    int area_num; // 数据区个数
    struct data_area_desc_s *area_descs; // 数据区描述数组
    int attr_num; // 属性(遥测)个数
    struct attribute_desc_s *attr_descs; // 属性(遥测)描述数组
};

从上图中还可以看出来,数据区"data_area_desc_s"是硬件设备相关最顶层结构体,设备类会继承,代码如下:

// 数据区结构体
struct data_area_desc_s
{
    char *code; // 唯一标识
    char *name; // 名字
    unsigned char  reg_type; // 寄存器类型, REG_TYPE_
    unsigned char  reg_size; // 寄存器占空间大小,单位byte
    unsigned short reg_num;  // 寄存器个数
    unsigned int reg_addr;   // 寄存器地址(modbus), 数据标识(dlt645)
};

// 设备数据区结构体
struct dev_data_area_s
{
    struct data_area_desc_s *desc;  // 数据区描述
    unsigned char *val;             // 数据区指针
};

// 设备结构体
struct device_s
{
    char *code; // 唯一标识
    char *name; // 名字
    char *id;   // id, modbus, dlt645 都需要
    char *product_code;             // 产品标识
    struct product_s *product;      // 产品 = 设备类
    unsigned int area_num;          // 数据区个数
    struct dev_data_area_s *areas;  // 设备数据区指针数组
    unsigned int attr_num;          // 属性个数
    struct dev_attribute_s *attrs;  // 设备属性指针数组
    unsigned char online;           // >0代表在线,=0代表离线
    long next_time_sec;             // 下次轮询的时间
};

三,线程设计

我认为,对于一个规模不大的程序来说,确定好数据结构和程序流程图(线程),基本上程序的框架出来了。这个程序我设计了3个类型的线程,分别是主线程,串口采集线程,网口采集线程。主线程的作用是初始化,外部命令处理。我为每个串口通道分配了1个线程,而所有网络通道处理共用1个线程,这样做是因为串口涉及底层硬件操作,分开线程好处理,网口所有通道其实只有"socket fd"不一样,可以用"fd_set"一并处理,在源码介绍的文章再详解。
程序流程图
上图有画得不好的地方,这里解释一下。主线程收到退出命令,会给其它线程发退出指令,等其它线程都退出后,主线程退出,然后整个程序结束。
下一篇介绍源码,待续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么要选择 Redis?

文章目录前言一、选型二、协议三、客户端1、常见 java 客户端2、常见可视化工具:四、Redis 生态1、模块2、代理3、其他前言 Redis(Remote Dictionary Server),即「远程字典服务」是一个使用 ANSI C 编写的、开源的、支持网络的、…

【电力系统】基于YALMIP 的微网(光伏+风电+蓄电池+微电网+柴油机)优化调度模型附matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

Unity—UGUI

每日一句:读数、学习 去更远的地方,才能摆脱那些你不屑一顾的圈子 目录 InputFiled输入框 例:用户名和密码 Toggle组件 案例:冷却效果 InputFiled输入框 Text Component 输入文本组件 Text输入内容 Character Limit 输入字符…

宝塔后渗透-添加用户_反弹shell

更新时间:2022年11月21日 1. 背景介绍 对于想拿到bt后台来说,非常的艰难:无非是通过bypass之后提权,直接拿到服务器的root权限,然后再去宝塔后台。 当然,还有一种运气十分爆棚的方法:发现了b…

Qt的Q_UNUSED()函数的功能

目录Qt Assistant(Qt 助手)构建场景其他一些平替方法参考Qt Assistant(Qt 助手) 函数名直译过来是【不用的;从未用过的】。 碰到陌生的函数不要慌,直接Qt Assistant查一哈。 Q_UNUSED(name) Indicates to …

负载均衡器 OpenELB ARP 欺骗技术解析

作者:大飞哥,视源电子运维工程师,KubeSphere 用户委员会广州站站长,KubeSphere Ambassador。 K8S 对集群外暴露服务有三种方式:NodePort,Ingress 和 Loadbalancer。NodePort 用于暴露 TCP 服务(4 层)&#…

基于5G智能网关的水泵远程监控系统方案

方案背景 水泵作为一种常见的水务设备,在日常的生产、生活中发挥重要的作用。为了保证生产、生活用水,也为了预防异常天气带来的过度降水,水泵具备的供水、排水作用都是不可忽视的。然而,很多地区的水泵管理模式依然停留在专人看…

毕业设计-基于机器视觉的手写字识别系统

目录 前言 课题背景和意义 实现技术思路 实现效果图样例 前言 📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科…

变焦镜头内参数如何获得?

很多时候,我们是使用相应的棋盘格标定进行相机内参数的获取,但是对于相机而言,如果要是焦距范围是测量比较远的物体,那么我们要进行注意相应的内参数就是不能够使用棋盘格标定法进行获取,因为不准. 由于项目的需要,这里我就是需要进行探究一下,如何通过自己调试直接设定内参数…

CSS 实现卡片边框渐变动画

前言 &#x1f44f;CSS实现卡片边框渐变动画&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 父容器添加背景渐变色 <div class"card"></div>.card {background: linear-gradient(0deg, …

Tensorboader图片和模型可视化

非常简单&#xff0c;10分钟搞懂1 Tensorboader介绍2 进行写入和运行&#xff08;共2步&#xff09;3 图像显示3.1 显示图片3.2 显示模型3.3 动态显示学习率等参考文献1 Tensorboader介绍 它就是1个可视化工具&#xff0c;需要用pip进行下载&#xff1b; 下载以后进行显示&a…

SpringCloud框架(三):微服务优化,Feign的最佳实现方案

SpringCloud环境搭建&#xff1a;生产和消费 RestTemplate Spring章节复习已经过去&#xff0c;新的章节SpringCloud开始了&#xff0c;这个章节中将会回顾微服务相关 主要依照以下几个原则 每一个组件的Demo和Coding上传到我的代码仓库在原有基础上加入一些设计模式&#xf…

数据结构与算法之图的应用

一.树之习题选讲-Tree Traversals Again 树习题-TTA.1 题意理解 非递归中序遍历的过程 1. Push的顺序为先序遍历(pre)2. Pop的顺序给出中序遍历(in) 树习题-TTA.2 核心算法 上图分别是先序、中序、后序遍历通过规律我们可以看到他们之间的位置分配 //伪代码 void solve(int …

Zabbix在X86服务器上的部署流程

服务器资源:Centos7、X86架构 部署zabbix服务端 #设置SELinux 成为permissive模式临时关闭selinux防火墙 setenforce 0 #获取zabbix的下载源和更换阿里源 https://mirrors.aliyun.com/zabbix/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm #解压zabbix包 …

探花交友_第4章_MongoDB基础(新版)

探花交友_探花交友_第4章_MongoDB基础(新版) 文章目录探花交友_探花交友_第4章_MongoDB基础(新版)课程介绍1. 通用设置1.1 需求分析1.1.1 需求分析1.1.2 数据库表1.1.3 实体类SettingsQuestionBlackList1.2 查询通用设置1.2.1 接口文档1.2.2 代码实现vo对象tanhua-app-serverSe…

易知微11月更新速递 | 预案集如何实现“一屏多端”联动指挥体系

伴随着“2022Easy Future秋季产品发布会”的举行&#xff0c;易知微也迎来了EasyV6.0的全新升级&#xff0c;通过新产品、新服务&#xff0c;助力实现一个能“数智视融合&#xff0c;虚实人联动”的数字增强世界。近一个月我们依旧奋力于产品迭代优化&#xff0c;又给大家带来了…

留学生Paper写作怎么进行深度解析?

对于留学生Paper而言&#xff0c;要想文章显得井井有条&#xff0c;逻辑结构在这个时候就显得很重要&#xff01;为什么这样说呢&#xff1f;首先我们要认识到一点&#xff0c;就是Paper必须做到内容和形式上统一。内容就是文章的主题和材料&#xff0c;形式是指逻辑结构和语言…

视觉小目标检测论文速读

视觉小目标检测论文速读 本文主要针对三篇文章典型文章。 一. SuperYOLO Super Resolution Assisted Object Detection in Multimodal Remote Sensing Imagery 1. 主要工作: 首先去掉Focus模块取保持HR特征, 避免分辨率下降&#xff0c;有效克服小目标空间损失的减少。利…

(一) SpringCloud+Security+Oauth2微服务授权初步认识

一 引言 再前面的security专题中 我们学习了单体架构基于SpringSecurity实现的授权方案,这种在业务量较小及业务的复杂度较低时比较实用,随着业务的复杂度越来越高,微服务架构也越来越被更多的公司使用&#xff0c;本文就微服务中的主流授权方案及oauth2中基本概念做简要概述。…

厨神之蛋糕制作

失败了7次&#xff0c;成功了6次。成功的6次里有好有坏&#xff0c;总结一下蛋糕制作的过程与要点。 原料 低筋面粉&#xff08;筋度越高越偏向包子馒头的口感&#xff0c;松软度越低&#xff09;、白糖、鸡蛋、水&#xff08;也可以用牛奶或其他含水的物质&#xff09;、食用…