当前位置: 首页 > article >正文

C#实现数据采集系统-分组查询

分组查询

单点查询:1000个点,10000个点,就要查询一千次,一万次
如果50个一组,100个一组,1千个点就减少到20次,10次,大大提供了通信效率

ModbusTcp查询报文

00 01 00 00 00 06 FF 01 00 01 00 10  -01
00 01 00 00 00 06 01 03 00 05 00 02  -03

创建点位查询组的类

根据modbustcp查询报文可以知道,必要的字段包括:功能码,起始地址,查询地址数量(长度)

然后增加一个点位集合,存储该组相关的点位,用于查询之后的处理

public class PointGroup
{
    /// <summary>
    /// 功能码
    /// </summary>
    public byte FuncNum { get; set; }

    public int StartAddress { get; set; }

    public int Length { get; set; }

    public List<RegisterPoint> Points { get; set; }
}

在ModbusTcp中实现地址分组功能

定义一个PointGroup的集合,和一个地址实现的方法

private List<PointGroup> _pointGroups = new List<PointGroup>();
 
 void AddressGroup()
 {
     //实现地址分组,添加到集合中去
     
 }

先根据寄存器类型分组,不同的寄存器,如线圈,保持寄存器不能一起查询

 var groups = _registers.GroupBy(x => x.RegisterType);

然后在每个寄存器组中进行分组

  1. 获取到功能码
  2. 按地址从小打大排序
foreach (var item in groups)
{
    var func = ReadFuncCodes[item.Key];
    var points = item.OrderBy(x => x.Address).ToList();
}

我们这里设置分组查询最长长度100,正常寄存器不超过127个,实现一下分组,然后再初始化中调用就行

 /// <summary>
 /// 地址分组处理
 /// </summary>
 void AddressGroup()
 {
     var groups = _registers.GroupBy(x => x.RegisterType);
     foreach (var item in groups)
     {
         var func = ReadFuncCodes[item.Key];
         var points = item.OrderBy(x => x.Address).ToList();
         PointGroup group = new PointGroup()
         {
             FuncNum = func,
             StartAddress = points[0].Address,
             Length = points[0].Length,
         };
         group.Points.Add(points[0]);
         //长度100
         for (int i = 1; i < points.Count; i++)
         {
             var length = points[i].Address + points[i].Length - StartAddress;
             if (length <= 100)
             {
                 group.Length = length;
                 group.Points.Add(points[i]);
             }
             else
             {
                 _pointGroups.Add(group);

                 group = new PointGroup()
                 {
                     FuncNum = func,
                     StartAddress = points[i].Address,
                     Length = points[i].Length,
                 };
                 group.Points.Add(points[i]);
             }
         }
         _pointGroups.Add(group);
     }
 }

根据分组进行查询

修改查询代码,根据分组进行查询

修改内容

  1. 修改遍历点位组
  2. 修改设置发送报文
  3. 修改数据处理

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

新的查询

修改循环,和创建两个新的方法,主体中发送、结束,数据报文提取部分不变


    foreach (var group in _pointGroups)
    {
        SetSendBytes(group);
        //发送读报文
        _client.Send(_readbytes);

        int len = ReceviceDataLength(group.Length);

        var errLen = 9;
        var normalLen = 9 + len;
        //接收报文
        byte[] recvBytes = Recevice(normalLen, errLen);

        //提取数据部分
        byte[] dataBytes = new byte[len];
        Array.Copy(recvBytes, 9, dataBytes, 0, len);
        //数据处理
        DealData(group, dataBytes);
    }
    
    
    //SetSendBytes
 private void SetSendBytes(PointGroup group)
{
  //...
}
//数据处理方法
void DealData(PointGroup group, byte[] bytes)
{
    //...
}

设置发送报文方法

  private void SetSendBytes(PointGroup group)
  {
      //修改功能码
      _readbytes[7] = group.FuncNum;

      //修改起始地址
      var addressBytes = BitConverter.GetBytes(group.StartAddress);
      _readbytes[8] = addressBytes[1];
      _readbytes[9] = addressBytes[0];

      //修改读寄存器数量
      var LengthBytes = BitConverter.GetBytes(group.Length);
      _readbytes[10] = LengthBytes[1];
      _readbytes[11] = LengthBytes[0];
  }

实现数据处理

ModbusTcp

IO位处理

计算所在字节数

$$
\frac{Address-Start}8

$$

计算所在位数:(Address-Start) % 8

字节

起始:01

查询:16个

数据:0A 02

[0]:0A→0000 1010

[1]:02→0000 0010

08位 在第0个字节中第7位(0-7)

字节位:(8-1)/8=0

位数:(8-1)%8=7

76543210
地址0807060504030201
00001010
地址100FOE0D0C0B0A09
00000010

然后通过位运算算出当前点位的值

位运算

//IO类型
//当前点位Address-(StartAddress-1)
var num = (item.Address - group.StartAddress) / 8;
var bit = (item.Address - group.StartAddress) % 8;
value = (bytes[num] >> bit & 1) == 1;

寄存器处理

在之前的数据处理方法中已经完整实现了一个点位的数据处理,包括字节序处理,转换类型等,在这里就是要做的就是根据每个的地址和长度信息,从总的数据报文中提取出当前点的数据部分

 var start = item.Address - group.StartAddress;
 var valueBytes = new byte[item.Length * 2];
 Array.Copy(bytes, start*2, valueBytes, 0, item.Length * 2);
 value = DealData(item, valueBytes);

然后修改原方法,让其返回计算出的value值,(删除里面的IO线圈处理操作)

        /// <summary>
        /// 数据处理方法
        /// </summary>
        /// <param name="point"></param>
        /// <param name="bytes"></param>
        object DealData(RegisterPoint point, byte[] bytes)
        {
            object value;
            #region 原方法寄存器处理

            if (point.Type == typeof(byte))
            {
                value = bytes[1];
            }
            else
            {
                //字节序处理
                byte[] newbytes = new byte[point.Length * 2];

                //优化
                for (int i = 0; i < point.Length; i++)
                {
                    newbytes[2 * i] = bytes[2 * i + 1];
                    newbytes[2 * i + 1] = bytes[2 * i];
                }

                // 优化
                var converterMothed = typeof(BitConverter).GetMethod(
                    "To" + point.Type.Name,
                    BindingFlags.Public | BindingFlags.Static,
                    new[] { typeof(byte[]), typeof(int) }
                );
                value = converterMothed.Invoke(null, new object[] { newbytes, 0 });
            }
            #endregion

            return value;
        }

完整的实现

根据自己需要修改命名

        void DealData(PointGroup group, byte[] bytes)
        {
            foreach (var item in group.Points)
            {
                object value;
                if (group.FuncNum <= 2)
                {
                    //IO类型
                    //当前点位Address-(StartAddress-1)
                    var num = (item.Address - group.StartAddress) / 8;
                    var bit = (item.Address - group.StartAddress) % 8;
                    value = (bytes[num] >> bit & 1) == 1;
                }
                else
                {
                    var start = item.Address - group.StartAddress;
                    var valueBytes = new byte[item.Length * 2];
                    Array.Copy(bytes, start, valueBytes, 0, item.Length * 2);
                    value = DealData(item, valueBytes);
                }
                //赋值
                if (!value.Equals(item.Value))
                {
                    item.Value = value;
                    ValueUpdated?.Invoke(item, value);
                }
            }
        }

        /// <summary>
        /// 数据处理方法
        /// </summary>
        /// <param name="point"></param>
        /// <param name="bytes"></param>
        object DealData(RegisterPoint point, byte[] bytes)
        {
            object value;
            #region 原方法寄存器处理

            if (point.Type == typeof(byte))
            {
                value = bytes[1];
            }
            else
            {
                //字节序处理
                byte[] newbytes = new byte[point.Length * 2];

                //优化
                for (int i = 0; i < point.Length; i++)
                {
                    newbytes[2 * i] = bytes[2 * i + 1];
                    newbytes[2 * i + 1] = bytes[2 * i];
                }

                // 优化
                var converterMothed = typeof(BitConverter).GetMethod(
                    "To" + point.Type.Name,
                    BindingFlags.Public | BindingFlags.Static,
                    new[] { typeof(byte[]), typeof(int) }
                );
                value = converterMothed.Invoke(null, new object[] { newbytes, 0 });
            }
            #endregion

            return value;
        }

http://www.kler.cn/a/402981.html

相关文章:

  • 在CentOS 7上配置Nginx的TCP端口转发
  • 二分排序
  • react中useMemo的使用场景
  • 20.100ASK_T113-PRO 开发板开机自动QT程序简单的方法一
  • #Verilog HDL# Verilog中的UDP原语
  • 深入理解索引(一)
  • 云原生学习
  • 图形学笔记 - 4. 几何 - 基本表示方法及曲线和曲面
  • 大数据入门-什么是Flink
  • 南京邮电大学算法设计-二叉树先序遍历算法动态演示
  • Springboot项目搭建(2)-用户详细信息查询
  • k8s搭建1.23版本
  • 从零开始深度学习:全连接层、损失函数与梯度下降的详尽指南
  • 【西瓜书】对数几率回归(逻辑回归)的概念与表示
  • 生成式语言模型 三范式 预训练、微调、强化反馈学习
  • 深度神经网络中不同的卷积层提取的特征有什么不同?
  • 企业项目级IDEA编辑器设置类注释、方法注释模板(仅增加@author和@date)
  • 【Linux系统编程】第四十七弹---深入探索:POSIX信号量与基于环形队列的生产消费模型实现
  • React中常用的钩子
  • 深度学习神经网络中的优化器的使用
  • Fundamental Analysis and Mean-Variance Optimal Portfolios论文阅读
  • python3 Flask应用 使用 Flask-SQLAlchemy操作MySQL数据库
  • 鸿蒙开发:ForEach中为什么键值生成函数很重要
  • # 07_ Python基础到实战一飞冲天(二)-python基础(七)--变量类型计算与输入输出
  • 鸿蒙HarmonyOS开发:一次开发,多端部署(工程级)三层工程架构
  • Hadoop 架构