C#上位机与三菱PLC的通信09---开发自己的通讯库(A-3E版)
1、A-3E报文回顾
具体细节请看:
C#上位机与三菱PLC的通信05--MC协议之QnA-3E报文解析
C#上位机与三菱PLC的通信06--MC协议之QnA-3E报文测试
2、为何要开发自己的通讯库
前面开发了自己的A-1E协议的通讯库,实现了数据的读写,对于封装的通讯库,其实是一个dll文件,请看上节的dll文件,有了这个文件,就可以在项目中直接引用 。
我们只要引用并调用相关的方法即可实现目的, 但写一个通讯库需要非凡的技术,需要考虑的东西很多,比如扩展性,通用性,等等之类的。通过封装通讯库达到更高的层次, 大师就是这样锻造出来的,接下来马上安排A-3E协议的封装,代码是基于上节的基础上添加。
3、说干就干
1、添加类文件
2、编写核心的通信类A3E.cs
A3E.cs完整代码
using Mitsubishi.Communication.MC.Mitsubishi.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace Mitsubishi.Communication.MC.Mitsubishi
{/// <summary>/// A3E报文通讯库/// </summary>public class A3E : MelsecBase{byte _netCode = 0x00, _stationCode = 0x00;public A3E(string ip, short port, byte net_code = 0x00, byte station_code = 0x00) : base(ip, port){_netCode = net_code;_stationCode = station_code;}#region 读数据/// <summary>/// 读取数据/// </summary>/// <typeparam name="T">读取的数据类型</typeparam>/// <param name="address">存储区地址</param>/// <param name="count">读取长度</param>/// <returns></returns>public Result<T> Read<T>(string address, short count){AreaCode areaCode; string start;(areaCode, start) = this.AnalysisAddress(address);return Read<T>(areaCode, start, count);}/// <summary>/// 读取数据/// </summary>/// <typeparam name="T">读取的数据类型</typeparam>/// <param name="areaCode">存储区代码</param>/// <param name="startAddr">开始地址</param>/// <param name="count">读取长度</param>/// <returns></returns>public Result<T> Read<T>(AreaCode areaCode, string startAddr, short count){Result<T> result = new Result<T>();try{// 连接var connectState = this.Connect();if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}// 子命令(位/字)byte readCode = (byte)(typeof(T) == typeof(bool) ? 0x01 : 0x00);//开始地址List<byte> startBytes = this.StartToBytes(areaCode, startAddr);// 读取长度int typeLen = this.CalculatLength<T>();// 读取报文List<byte> bytes = new List<byte>{0x50,0x00,//请求副头部,固定50 00_netCode,// 网络号,根据PLC的设置0xFF,//PLC编号,固定值0xFF,0x03,//目标模块IO编号,固定FF 03_stationCode,// 目标模块站号 0x0C,0x00, // 剩余字节长度0x0A,0x00,//PLC响应超时时间,以250ms为单位计算 0x01,0x04,// 成批读取readCode,0x00,// 字操作 0x0001 startBytes[0],startBytes[1],startBytes[2],// 起始地址(byte)areaCode,// 区域代码 (byte)(typeLen*count%256),(byte)(typeLen*count/256%256) //长度};//发送报文List<byte> dataBytes = this.Send(bytes, 0);//数据解析result.Datas = this.AnalysisDatas<T>(dataBytes, typeLen);}catch (Exception ex){result = new Result<T>(false, ex.Message);}return result;}#endregion#region 写数据/// <summary>/// 写入数据/// </summary>/// <typeparam name="T">写入的数据类型</typeparam>/// <param name="values">写入的数据列表</param>/// <param name="address">开始地址</param>/// <returns></returns>public Result Write<T>(List<T> values, string address){AreaCode areaCode; string start;(areaCode, start) = this.AnalysisAddress(address);return this.Write<T>(values, areaCode, start);}/// <summary>/// 写入数据/// </summary>/// <typeparam name="T">写入的数据类型</typeparam>/// <param name="values">写入的数据列表</param>/// <param name="areaCode">存储区代码</param>/// <param name="address">开始地址</param>/// <returns></returns>public Result Write<T>(List<T> values, AreaCode areaCode, string startAddr){Result result = new Result();try{// 连接var connectState = this.Connect();if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}// 子命令(位/字)byte writeCode = (byte)(typeof(T) == typeof(bool) ? 0x01 : 0x00);// 起始地址 XY 直接翻译 100 00 01 00 D100 64 00 00List<byte> startBytes = this.StartToBytes(areaCode, startAddr);//计算数据类型的长度int typeLen = this.CalculatLength<T>();int count = values.Count;//计算数据的字节列表List<byte> datas = this.GetDataBytes<T>(values);List<byte> baseBytes = new List<byte>{0x50,0x00,this._netCode,// 可变,根据PLC的设置0xFF,//PLC编号,固定值0xFF,0x03,//目标模块IO编号,固定FF 03this._stationCode,// 可变,目标模块站号};//0x0E,0x00, // 剩余字节长度List<byte> commandBytes = new List<byte> {0x0A,0x00,//超时时间0x01,0x14,// 成批写入writeCode,0x00,// 字操作startBytes[0],startBytes[1],startBytes[2],// 起始地址(byte)areaCode,// 区域代码 (byte)(typeLen*count%256),(byte)(typeLen*count/256%256), //长度};commandBytes.AddRange(datas);baseBytes.Add((byte)(commandBytes.Count % 256));baseBytes.Add((byte)(commandBytes.Count / 256 % 256));baseBytes.AddRange(commandBytes);socket.Send(baseBytes.ToArray());// 解析响应byte[] respBytes = new byte[11];socket.Receive(respBytes, 0, 11, SocketFlags.None);// 状态if ((respBytes[9] | respBytes[10]) != 0x00){throw new Exception("响应异常。" + respBytes[9].ToString() + respBytes[10].ToString());}}catch (Exception ex){result.IsSuccessed = false;result.Message = ex.Message;}return result;}#endregion#region 私有方法/// <summary>/// 地址解析/// </summary>/// <param name="address">地址字符串</param>/// <returns></returns>public Tuple<AreaCode, string> AnalysisAddress(string address){// 取两个字符string area = address.Substring(0, 2);if (!new string[] { "TN", "TS", "CS", "CN" }.Contains(area)){area = address.Substring(0, 1);}string start = address.Substring(area.Length);// 返回一个元组对象 return new Tuple<AreaCode, string>((AreaCode)Enum.Parse(typeof(AreaCode), area), start);}/// <summary>/// 发送报文/// </summary>/// <param name="reqBytes">字节列表</param>/// <param name="count"></param>/// <returns></returns>/// <exception cref="Exception"></exception>public override List<byte> Send(List<byte> reqBytes, int count){socket.Send(reqBytes.ToArray());// 解析byte[] respBytes = new byte[11];socket.Receive(respBytes, 0, 11, SocketFlags.None);// 状态if ((respBytes[9] | respBytes[10]) != 0x00){throw new Exception("响应异常。" + respBytes[9].ToString() + respBytes[10].ToString());}// 数据长度 int dataLen = BitConverter.ToUInt16(new byte[] { respBytes[7], respBytes[8] },0) - 2; // -2 的意思去除响应代码(状态)byte[] dataBytes = new byte[dataLen];socket.Receive(dataBytes, 0, dataLen, SocketFlags.None);return new List<byte>(dataBytes);}#endregion#region plc控制/// <summary>/// PLC远程启动/// </summary>/// <returns></returns>public Result Run(){return PlcStatus(0x01, new List<byte> { 0x00, 0x00 });}/// <summary>/// PLC远程停止/// </summary>/// <returns></returns>public Result Stop(){return PlcStatus(0x02);}/// <summary>/// PLC运行状态/// </summary>/// <param name="cmdCode"></param>/// <param name="cmd"></param>/// <returns></returns>private Result PlcStatus(byte cmdCode, List<byte> cmd = null){Result result = new Result();try{var connectState = this.Connect();if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}List<byte> commandBytes = new List<byte>{0x50,0x00,this._netCode,// 可变,根据PLC的设置0xFF,0xFF,0x03,this._stationCode,// 可变};//0x08,0x00, // 剩余字节长度List<byte> cmdBytes = new List<byte> {0x0A,0x00,cmdCode,0x10,0x00,0x00,0x01,0x00,//模式};if (cmd != null){cmdBytes.AddRange(cmd);}commandBytes.Add((byte)(commandBytes.Count % 256));commandBytes.Add((byte)(commandBytes.Count / 256 % 256));commandBytes.AddRange(cmdBytes);socket.Send(commandBytes.ToArray());byte[] respBytes = new byte[11];socket.Receive(respBytes, 0, 11, SocketFlags.None);// 状态if ((respBytes[9] | respBytes[10]) != 0x00){throw new Exception("响应异常。" + respBytes[1].ToString());}}catch (Exception ex){result.IsSuccessed = false;result.Message = ex.Message;}return result;}#endregion}
}
4、测试通讯库
1、启动MC服务器
2、利用通讯库读写数据
1、读取D区100开始的3个short数据
2、读取M区100开始的5个float数据
3、读取X区100开始的4个bool数据
4、写入M区200开始的2个short数据
5、写入D区200开始的5个float数据
3、完整代码
/// <summary>/// 测试A-3E通讯库/// </summary>static void MCLibTestA3E(){A3E qNA3E = new A3E("192.168.1.7", 6000);#region 读数据//Console.WriteLine("读取D区100开始的3个short数据");//var result1 = qNA3E.Read<short>("D100", 3);//if (result1.IsSuccessed)//{// result1.Datas.ForEach(d => Console.WriteLine(d));//}//else//{// Console.WriteLine(result1.Message);//}//Console.WriteLine("读取M区100开始的5个float数据");//var result2 = qNA3E.Read<float>("M100", 5);//if (result2.IsSuccessed)//{// result2.Datas.ForEach(d => Console.WriteLine(d));//}//else//{// Console.WriteLine(result2.Message);//}//Console.WriteLine("读取X区100开始的4个bool数据");//var result3 = qNA3E.Read<bool>(AreaCode.X, "100", 4);//if (result3.IsSuccessed)//{// result3.Datas.ForEach(d => Console.WriteLine(d));//}//else//{// Console.WriteLine(result3.Message);//}#endregion#region 写数据Console.WriteLine("写入M区200开始的2个short数据");var result4 = qNA3E.Write<short>(new List<short> { -541, 982 }, "M200");if (result4.IsSuccessed){Console.WriteLine(result4.Message);}Console.WriteLine("写入D区200开始的5个float数据");var result5 = qNA3E.Write<float>(new List<float> { 111, 0, -8076, 13.67f, -985.325f }, "D200");if (result5.IsSuccessed){Console.WriteLine(result5.Message);}#endregion }
5、小结
原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富。
原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富。
原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富
相关文章:

C#上位机与三菱PLC的通信09---开发自己的通讯库(A-3E版)
1、A-3E报文回顾 具体细节请看: C#上位机与三菱PLC的通信05--MC协议之QnA-3E报文解析 C#上位机与三菱PLC的通信06--MC协议之QnA-3E报文测试 2、为何要开发自己的通讯库 前面开发了自己的A-1E协议的通讯库,实现了数据的读写,对于封装的通…...
【LeetCode】70. 爬楼梯(简单)——代码随想录算法训练营Day38
题目链接:70. 爬楼梯 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入:n 2 输出:2 解释:有两种方法可以爬到…...
图数据库 之 Neo4j - Cypher语法基础(5)
节点(Nodes) Cypher使用()来表示一个节点。 () # 最简单的节点形式,表示一个任意无特征的节点,其实就是一个空节点(movie) # 如果想指向一个节点在其他地方,我们可以给节点添加一个变量名(如movie),表示一个变量名为 movie的节点。(:Movie) # 表示一个标签为 Movie 的匿名…...

打造智能物品租赁平台:Java与SpringBoot的实践
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...

盘点那些世界名校计算机专业采用的教材
清华、北大、MIT、CMU、斯坦福的学霸们在新学期里要学什么?今天我们来盘点一下那些世界名校计算机专业采用的教材。 书单目录 1.《深入理解计算机系统》(原书第3版)2. 《算法导论》(原书第3版)3. 《计算机程序的构造和…...
编程笔记 Golang基础 013 格式化输入输出
编程笔记 Golang基础 013 格式化输入输出 一、格式化输出1. fmt.Print系列函数2. Printf格式说明3. 格式化布尔类型 二、格式化输入1. fmt.Scan系列函数注意事项 三、练习小结 Go语言中的格式化输入和输出主要通过标准库 fmt 包来实现。主要是输出需要格式化。 一、格式化输出 …...
身份证实名认证接口-简单的身份认证API调用方法
还在为复杂的API调用头疼不已?今天为大家带来一种超简单的身份认证API调用方法,让你的工作效率瞬间起飞! Java调用代码如下: import java.io.*; import okhttp3.*; public class main { public static void main(String []ar…...

数据结构·顺序表
1数据结构简介 学习数据结构与算法之前,一般是先学数据结构,方便之后学习算法,那么数据结构拆开介绍,就是数据 和 结构,数据,生活中到处都是,结构,就是数据存储的方式,即…...

玩转网络抓包利器:Wireshark常用协议分析讲解
Wireshark是一个开源的网络协议分析工具,它能够捕获和分析网络数据包,并以用户友好的方式呈现这些数据包的内容。Wireshark 被广泛应用于网络故障排查、安全审计、教育及软件开发等领域。关于该工具的安装请参考之前的文章:地址 ,…...

静态时序分析:SDC约束命令set_drive详解
相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html 本章将讨论使用set_drive命令,它用于对输入端口的驱动能力建模。首先需要说明的是,默认情况下,DC在STA时默认输入端口的转换时间是0,这对于…...

C#算法(12)—对图像像素做X/Y方向的偏移
我们在上位机开发领域有时候需要对获取的图像的像素做整体的偏移,比如所有像素在X方向上偏移几个像素,或者所有像素在Y方向上偏移几个像素,本文就是开发了像素整体偏移算法来解决这个问题。 比如有一个图像大小为3*3,像素值如下图1,如果我想实现将这个幅图像的像素整体往右…...

说一说Eclipse的项目类型和常用项目的区别
Eclipse在新建项目的时候有很多类型,包括Java project、Web project等等,如下: 那么这些项目类型有什么区别呢?我们在创建项目的时候应该如何选择,了解清楚这一点还是非常重要的,但记住一个出发点ÿ…...
[opencv][windows]cmake opencv opencv_contrib所需的缓存文件下载
这个是windows上源码编译opencvopencv-contrib时候cmake时候缓存文件,只需要将压缩文件夹解压到源码目录下面,cmake-gui上configure时候就不会报错,注意解压后文件夹名字是.cache,文件夹名字不能改变,比如opencv/.cache,有的人解压…...

五步解决 Ubuntu 18.04 出现GLIBC_2.28 not found的解决方法
Ubuntu 18.04 出现GLIBC_2.28 not found的解决方法 参考debian网址https://packages.debian.org/buster/并搜索想要的软件或者工具等,如libc6,有结果如下: 具体就不介绍了,请浏览官网了解。 第一步:添加软件源,在/et…...

【Java EE初阶二十一】http的简单理解(二)
2. 深入学习http 2.5 关于referer Referer 描述了当前页面是从哪个页面跳转来的,如果是直接在地址栏输入 url(或者点击收藏夹中的按钮) 都是没有 Referer。如下图所示: HTTP 最大的问题在于"明文传输”,明文传输就容易被第三方获取并篡改. …...
STM32 与 ARM 谁比较强大?
STM32 和 ARM 是两个不同的概念,STM32 是一种微控制器产品,而 ARM 是一家处理器架构设计和许可的公司。因此,无法简单地比较它们的强大程度。 STM32 是基于 ARM Cortex-M 核的微控制器产品,具有高性能、低功耗、低成本和易于开发等…...

四、分类算法 - 朴素贝叶斯算法
目录 1、朴素贝叶斯算法 1.1 案例 1.2 联合概率、条件概率、相互独立 1.3 贝叶斯公式 1.4 朴素贝叶斯算法原理 1.5 应用场景 2、朴素贝叶斯算法对文本进行分类 2.1 案例 2.2 拉普拉斯平滑系数 3、API 4、案例:20类新闻分类 4.1 步骤分析 4.2 代码分析 …...

Javascript中var和let之间的区别
文章目录 一.变量提升(声)二.let和var的区别 区别: 1、var有变量提升,而let没有; 2、let不允许在相同的作用域下重复声明,而var允许; 3、let没有暂时性死区问题; 4、let创建的全局变量没有给window设置对应…...

不要抱怨,不如抱 Java 运算符吧 (1)
本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…...
python之ftp小工具
文章目录 python之FTP小工具 python之FTP小工具 源码 #!/usr/bin/python3 import os import sys from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler, ThrottledDTPHandler from pyftpdlib.servers import FTPServer import logg…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...

PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
基于 HTTP 的单向流式通信协议SSE详解
SSE(Server-Sent Events)详解 🧠 什么是 SSE? SSE(Server-Sent Events) 是 HTML5 标准中定义的一种通信机制,它允许服务器主动将事件推送给客户端(浏览器)。与传统的 H…...