C#上位机序列9: 批量读写+事件广播

news2025/1/18 4:41:49

1. 读取配置文件及创建变量信息(点位名称,地址,数据类型(bool/short/int/float/long/double))

2. 读任务&写任务,数据有变化时事件广播通知

复制代码

using HslCommunication;
using HslCommunication.Core;
using HslCommunication.ModBus;
using PLCEvent.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using UtilHelper;

namespace PLCEvent.Service
{
    public class PLCService
    { 
        public static Action<string> OnDataChange;
        public static ConcurrentDictionary<string, bool> DicBoolData = new ConcurrentDictionary<string, bool>();
        public static ConcurrentDictionary<string, Word> DicAllData = new ConcurrentDictionary<string, Word>();
        public static ConcurrentDictionary<string, Word> DicChangeData = new ConcurrentDictionary<string, Word>();
        static ModbusTcpNet client = null;
        static IByteTransform byteTransform;
        static ConcurrentQueue<PLCModel> queueWrite = new ConcurrentQueue<PLCModel>();
         
        public static void DataChange(string address)
        {
            OnDataChange?.Invoke(address);
        }

        /// <summary>
        /// 自定义控件内接收到数据变化事件,根据传入address,以及DataType查询监听地址所需要的监听范围(40120_int 监听两个word:40120 40121;40130_long 监听四个word:40130 40131 40132 40133),判断是否属于本控件监听
        /// </summary>
        public static List<string> RangeAddress(string address, int length)
        {
            List<string> lstaddress = new List<string>();

            if (0 == length)
            {
                lstaddress.Add(address);
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    lstaddress.Add(FillAddress((DataHelper.Obj2Short(address) + i).ToString()));
                }
            }
            return lstaddress;
        }

        /// <summary>
        /// 读取时,按位补充0
        /// </summary>
        public static string FillAddress(string val, int length = 5)
        {
            return val.PadLeft(length, '0');
        }

        /// <summary>
        /// 写入时,格式化地址,如:40101 -> 101
        /// </summary> 
        public static string FormatAddress(string val)
        {
            if (val.Length < 5) return val;

            return val.Substring(1, val.Length - 1);
        }

        public static void Init()
        {
            client = new ModbusTcpNet(CommonMethods.PLCConfig.HostAddress, CommonMethods.PLCConfig.PortNumber);
            client.AddressStartWithZero = false;
            client.DataFormat = DataFormat.CDAB;
            byteTransform = client.ByteTransform;

            TskPlcRead();
            TskPlcWrite();
        }

        public static bool GetBool(string address)
        { 
            try
            {
                bool exist = DicBoolData.TryGetValue(address, out var value);// 字典存储
                if (!exist)
                {
                    Logger.Info($"[Error] PLCService,GetBool,errmsg:查无点位数据({address})");
                }
                return value;
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetBool,errmsg:" + ex.Message);
            }
            return false;
        }

        /// <summary>
        /// 获取字节(1个word,2个字节)
        /// </summary> 
        static Word GetAddressWord(string address, int add)
        {
            address = FillAddress((Convert.ToInt32(address) + add).ToString());
            bool exist = DicAllData.TryGetValue(address, out var value);
            if (!exist)
            {
                Logger.Info($"[Error] PLCService,GetAddressWord,errmsg:查无点位数据({address})");
            }
            return value;
        }

        /// <summary>
        /// 拼接字节(多个word)
        /// </summary> 
        static byte[] JoinAddressWord(string address, DataType datatype)
        {
            byte[] ret = null;
            switch (datatype)
            { 
                case DataType.Short:
                    { 
                        var buff = GetAddressWord(address, 0);
                        ret = new byte[2] { buff.Byte1, buff.Byte2 };
                    }
                    break;
                case DataType.Int:
                case DataType.Float:
                    {
                        var buff1 = GetAddressWord(address, 0);
                        var buff2 = GetAddressWord(address, 1);
                        ret = new byte[4] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2 };
                    } 
                    break;
                case DataType.Long: 
                case DataType.Double:
                    {
                        var buff1 = GetAddressWord(address, 0);
                        var buff2 = GetAddressWord(address, 1);
                        var buff3 = GetAddressWord(address, 2);
                        var buff4 = GetAddressWord(address, 3);
                        ret = new byte[8] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2, buff3.Byte1, buff3.Byte2, buff4.Byte1, buff4.Byte2 };
                    } 
                    break;
            }
            return ret;
        }

        public static ushort GetShort(string address)
        {
            try
            { 
                var buff = JoinAddressWord(address, DataType.Short);
                return byteTransform.TransUInt16(buff, 0); 
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetShort,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static uint GetInt(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Int);
                return byteTransform.TransUInt32(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetInt,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static float GetFloat(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Float);
                return byteTransform.TransSingle(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetFloat,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static ulong GetLong(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Long);
                return byteTransform.TransUInt64(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetLong,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static double GetDouble(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Double);
                return byteTransform.TransDouble(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetDouble,errmsg:" + ex.Message);
            }
            return 0;
        }

        /// <summary>
        /// 定时读取
        /// </summary>
        static void TskPlcRead()
        {
            Task.Factory.StartNew(async () =>
            {
                var start_c = CommonMethods.PLCConfig.ReadStart_Coil;
                var start_h = CommonMethods.PLCConfig.ReadStart_Holding;

                bool[] temp_c = null; byte[] temp_h = null;

                while (!CommonMethods.CTS.IsCancellationRequested)
                {
                    try
                    {
                        DicChangeData.Clear();

                        var array_c = (await client.ReadBoolAsync(start_c, (ushort)CommonMethods.PLCConfig.ReadCount_Coil)).Content;
                        var array_h = (await client.ReadAsync(start_h, (ushort)(CommonMethods.PLCConfig.ReadCount_Holding * 2))).Content;// ushort占两个字节
                         
                        if (null != array_c)
                        {
                            // bool类型只占1位,数据有变化直接通知
                            if (null == temp_c) temp_c = new bool[array_c.Length];
                            CheckBoolChange("0", start_c, temp_c, array_c);
                            Array.Copy(array_c, temp_c, array_c.Length);
                        }

                        if (null != array_h)
                        {
                            // word类型数据位(2,4,8),所以要先读取全部的数据,再通知变化
                            if (null == temp_h) temp_h = new byte[array_h.Length];
                            CheckWordChange("4", start_h, temp_h, array_h);
                            Array.Copy(array_h, temp_h, array_h.Length);

                            if (DicChangeData.Count > 0)
                            {
                                foreach (var item in DicChangeData)
                                {
                                    DataChange(item.Key);
                                }
                            }
                        } 
                    }
                    catch (Exception ex)
                    { 
                        Logger.Info("[Error] PLCMgr,TskPlcRead,errmsg" + ex.Message);
                    } 

                    await Task.Delay(1000);
                }
            }, TaskCreationOptions.LongRunning);
        }

        /// <summary>
        /// 检查数据是否有变化(bool类型)
        /// </summary> 
        public static void CheckBoolChange(string flg, string start, bool[] oldbuffer, bool[] newbuffer)
        {
            for (int i = 0; i < newbuffer.Length; i++)
            {
                string address = flg + FillAddress((i + Convert.ToInt32(start)).ToString(), 4);// 00101
                bool value = newbuffer[i];

                DicBoolData.AddOrUpdate1(address, value); 
               
                if (oldbuffer[i] != value)
                { 
                    OnDataChange(address);
                }
            }
        }

        /// <summary>
        /// 检查数据是否有变化(word类型)
        /// </summary> 
        public static void CheckWordChange(string flg, string start, byte[] oldbuffer, byte[] newbuffer)
        {
            int index = 0;
            for (int i = 0; i < newbuffer.Length; i = i + 2)
            {
                string address = flg + FillAddress((index + Convert.ToInt32(start)).ToString(), 4);// 40101

                if (address == "40130")
                {

                }
                index++;
                byte byte1 = newbuffer[i];
                byte byte2 = newbuffer[i + 1];

                Word buff = new Word() { Byte1 = byte1, Byte2 = byte2 };

                DicAllData.AddOrUpdate1(address, buff);

                if (oldbuffer[i] != byte1 || oldbuffer[i + 1] != byte2)
                {
                    DicChangeData.AddOrUpdate1(address, buff);
                }
            }
        }

        /// <summary>
        /// 添加写入值
        /// </summary> 
        public static void AddWriteVariable(string address, object value, DataType datatype)
        {
            queueWrite.Enqueue(new PLCModel() { Address = address, Value = value, PLCDataType = datatype });//加载值进队列
        } 

        /// <summary>
        /// 定时写入
        /// </summary>
        static void TskPlcWrite()
        { 
            Task.Factory.StartNew(async () =>
            {
                while (!CommonMethods.CTS.IsCancellationRequested)
                {
                    try
                    {
                        if (!queueWrite.IsEmpty)
                        {
                            PLCModel model = null; OperateResult result = null;
                            queueWrite.TryDequeue(out model);

                            var dataype = model.PLCDataType;
                            switch (dataype)
                            {
                                case DataType.Bool:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToBoolean(model.Value));
                                    break;
                                case DataType.Short:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt16(model.Value)); 
                                    break;
                                case DataType.Int:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt32(model.Value));
                                    break;
                                case DataType.Float:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToSingle(model.Value));
                                    break;
                                case DataType.Long:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt64(model.Value));
                                    break;
                                case DataType.Double:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToDouble(model.Value));
                                    break;
                            }

                            if (!result.IsSuccess)
                            {
                                Logger.Info("[Error] PLCMgr,TskPlcWrite,errmsg:写入失败," + result.Message);
                            }
                        }
                    }
                    catch (Exception ex)
                    { 
                        Logger.Info("[Error] PLCMgr,TskPlcWrite,errmsg:" + ex.Message);
                    } 

                    await Task.Delay(500);
                }
            }, TaskCreationOptions.LongRunning);
        }
    }
}

复制代码

3. 自定义控件绑定参数,监听数据变化事件

注意点:
1. bool类型只占1位,数据有变化直接通知
2. word类型数据位(short:2,int/float:4,long/double:8),所以要先读取全部的数据,再通知变化
3. 自定义控件内接收到数据变化事件,根据传入address,以及DataType查询监听地址所需要的监听范围(40120_int 监听两个word:40120 40121;40130_long 监听四个word:40130 40131 40132 40133),判断是否属于本控件监听
4. 自定义控件继承BaseParams类, PLCValue get:通过不同的数据类型,获取字典中的word数据,并拼接合成相应的数据类型;set:传入地址(写入时格式化地址,如:40101->101)及类型

实现效果:

开启Modbus Server工具

 双击数字,编辑值,点击更新后,写入modbus

数据有变化时,自动更新

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

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

相关文章

基于YOLOv8模型的水下目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的水下目标检测系统可用于日常生活中检测与定位鱼、水母、企鹅、海鹦、鲨鱼、海星、黄貂鱼&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统…

34 机器学习(二):数据准备|knn

文章目录 数据准备数据下载数据切割转换器估计器 kNN正常的流程网格多折交叉训练原理讲解距离度量欧式距离(Euclidean Distance)曼哈顿距离(Manhattan Distance)切比雪夫距离 (Chebyshev Distance)还有一些自定义的距离 就请读者自行研究 再识K-近邻算法API选择n邻居的思辨总结…

MySQL分组查询每组最新的一条数据(提供三种实现方法,附带三种方法查询性能比较和分析查询原理)

目录 一、前言二、注意事项三、准备SQL四、错误查询分析4.1、错误原因 五、实现方法5.1、实现方法一&#xff08;使用 LIMIT 查询&#xff09;5.2、实现方法二&#xff08;使用 DISTINCT 查询&#xff09;5.3、实现方法三&#xff08;使用 MAX(id) 查询&#xff0c;只适用于自增…

一篇文章讲明白double、float丢失精度的问题

1.背景 1.10.1 1.2000000000000002 发现上面计算的值竟然和数学计算不一致 2. 问题 计算机是通过二进制计算的&#xff0c;如果我们在二进制的视角来看待上面问题&#xff0c;就很容易发现问题了。 例如&#xff1a;把「0.1」转成二进制的表示&#xff0c;然后还原成十进制&…

846. 树的重心

输入样例 9 1 2 1 7 1 4 2 8 2 5 4 3 3 9 4 6输出样例&#xff1a; 4 分析&#xff1a;因为有n-1条边&#xff0c;所以每个点必然会连接到其他点&#xff0c;不存在孤立点&#xff0c;因此&#xff0c;我们从1-n任意点开始dfs都是可以的&#xff0c;因为无论怎么样&#xff0…

mrRobot

一、信息收集 1.访问地址 没啥信息&#xff0c;尝试扫下目录 2.目录扫描 key1 发现有wp-admin/和robots.txt robots.txt里面还拿到了一个密码字典&#xff0c;猜测是爆破wp的网站账号密码的 3.访问wp-admin/ ┌──(root&#x1f480;kali)-[~/桌面] └─# sort -u fsoci…

Java并发面试题:(六)悲观锁和乐观锁和Java内存模型和CAS原理

悲观锁和乐观锁的区别 什么是悲观锁&#xff1f; 基本上我们理解的操作前对资源加锁&#xff0c;操作完后释放锁。说的都是悲观锁。悲观锁认为所有的资源都是不安全的&#xff0c;随时会被其他线程操作、更改。所以操作资源前一定要加一把锁、防止其他线程访问。 什么是乐观锁&…

基于geojson-vt和canvas的高性能出图

概述 本文介绍基于geojson-vt和canvas&#xff0c;实现node端高性能出图。 效果 实现 1. canvas绘图 import { createCanvas } from canvasconst tileSize 256; const canvas createCanvas(tileSize, tileSize) const ctx canvas.getContext(2d)2. 处理geojson const g…

python二次开发Solidworks:圆形弹簧

目录 1、手动建模 2、python自动建模 1、手动建模 第一步​&#xff1a;草图1&#xff0c;在上视基准面画一个圆心在原点&#xff0c;直径50mm的圆​&#xff1b; 第二步​&#xff1a;草图2&#xff0c;在上视基准面画两条构造线&#xff0c;一条经过原点方向竖直&#xff0…

【jvm】虚拟机栈之局部变量表

目录 一、说明二、代码分析2.1 代码示例2.2 执行javap2.3 jclasslib插件查看 三、对slot的理解3.1 说明3.2 slot索引图3.3 实例方法的局部变量表3.4 long和double类型变量占2个slot 四、slot的重复利用4.1 说明4.2 变量c复用变量b的槽位 五、静态变量与局部变量对比 一、说明 1…

NEFU计算机网络实验一常见网络命令的使用

一、实验目的 1、理解、验证常用网络命令的原理和功能。 2、掌握常用的网络命令使用方法&#xff0c;合理使用相关命令对网络进行管理与维护。 二、实验内容 网络参数查询命令&#xff1a;IPCONFIG 网络测试命令&#xff1a;ping 路由表命令ROUTE 网络端口查询命令&…

chatglm配置

推荐看这个链接&#xff0c;有些问题解决出处https://zhuanlan.zhihu.com/p/643824521 以及这个https://blog.csdn.net/weixin_40547993/article/details/131775275 1.需要pytorch2.0&#xff0c;所以CUDA推荐11.8 ChatGLM2-6B版本要装PYTORCH2.0&#xff0c;而且要2.0.1 &a…

resultMap 和 resultType的用法和区别详解

resultMap 和 resultType的用法和区别详解 《resultMap 和 resultType的用法和区别详解》摘要引言resultType - 用法和映射示例了解resultType示例演示 resultMap - 区别、高级用法和自定义映射规则详解resultType vs. resultMap高级用法示例演示 Mybatis的CRUD操作总结参考资料…

信息保卫战:揭秘迅软DSE护航企业免受泄密之害

随着网络技术的发展&#xff0c;通过网络应用如网盘、网页、邮件、即时通讯工具传输分享文件变得越来越多&#xff0c;这些工具传输速度快&#xff0c;能够将大容量的文档快速传送给他人&#xff0c;在工作中受到许多人的青睐。 然而由这些传输工具引发的泄密事件也不断增多&am…

进程概念[下]

一、 进程优先级 0x01 什么叫进程优先级 CPU资源分配的先后顺序 0x02 为什么要有进程优先级 因为资源不足,是分配资源的一种方式,优先权高的进程有优先执行权利 0x03 查看更加详细的进程信息 ①运行代码 #include<iostream> #include<unistd.h> using na…

Cesium 空间量算——方位角量测

文章目录 需求分析需求 实现对方位角的量测功能 分析 可以通过Cesium API提供的方法手动实现方位角测量。下面是一个可以帮助你开始实现方位角测量的代码示例: // 初始化Cesium Viewer var viewer = new Cesium.Viewer(cesiumContainer);// 创建材质

第六章redux的使用(餐饮版)

文章目录 一、redux的使用1、redux原理图解析 二、同步计算器案例2、创建src/redux/constant.js&#xff08;食材库&#xff09;3、创建src/redux/store.js&#xff08;厨房&#xff09;3-1、安装redux3-2、store.js 4、count_reducer.js&#xff08;厨师&#xff09;5、count_…

如何从SEO角度写好原创文章并吸引人

不会写原创文章的站长&#xff0c;不能算是好的站长哦。SEO原创文章对于网站优化来说&#xff0c;就像吃饭对于人的生存一样重要。如果一个SEO博客全是复制粘贴别人的文章&#xff0c;那这个博客还有多少意义呢&#xff1f;这就好比别人辛苦种田&#xff0c;你却轻易地把人家的…

Profinet转Modbus RTU网关连接PLC与多功能电表modbus通讯配置案例

Profinet是一种工业以太网通讯协议&#xff0c;广泛用于工业自动化系统中。而Modbus RTU是一种串行通信协议&#xff0c;常用于PLC和仪表之间的通讯。Profinet转Modbus RTU网关(XD-MDPN100)的作用就是将Profinet协议转换为Modbus RTU协议&#xff0c;从而实现PLC和多功能电表之…

zabbix-agnet连接zabbix-proxy

先配置好zabbix-proxy zabbix-proxy配置http://t.csdnimg.cn/RpaCI 在zabbix-proxy服务器上 [rootcloudserver ~]# grep ^[a-Z] /etc/zabbix/zabbix_agentd.conf PidFile/var/run/zabbix/zabbix_agentd.pid LogFile/var/log/zabbix/zabbix_agentd.log LogFileSize0 Server19…