深圳做积分商城网站建设,网页设计公司有哪些国内,手机网站分辨率做多大,360商场内部1、介绍
前面已经完成了7项工作#xff1a;
C#上位机与欧姆龙PLC的通信01----项目背景-CSDN博客
C#上位机与欧姆龙PLC的通信02----搭建仿真环境-CSDN博客
C#上位机与欧姆龙PLC的通信03----创建项目工程-CSDN博客
C#上位机与欧姆龙PLC的通信04---- 欧姆龙plc的存储区
C#上…1、介绍
前面已经完成了7项工作
C#上位机与欧姆龙PLC的通信01----项目背景-CSDN博客
C#上位机与欧姆龙PLC的通信02----搭建仿真环境-CSDN博客
C#上位机与欧姆龙PLC的通信03----创建项目工程-CSDN博客
C#上位机与欧姆龙PLC的通信04---- 欧姆龙plc的存储区
C#上位机与欧姆龙PLC的通信05---- HostLink协议(C-Mode版)
C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)
C#上位机与欧姆龙PLC的通信07----使用第3方通讯库读写数据 这当中06是重点的重点需要非常熟悉才能自己写通讯库封装自己的库需要掌握socket通讯串口通讯同步异步集合数组字节序列等技能点这是走向武林高手的必经之路这样才能强大自己丰满的肌肉需要一步步啃。
2、开搞
1、创建VS解决方案项目 2、添加类库项目 类库项目创建目录Omron及Base及5个基础类 添加对类库的引用 3、编写基础类库文件 1AreaType.cs
存储区对应厂家手册规定的值不能自己改。
(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0,H字:B2) /// summary/// 存储区枚举/// /summarypublic enum AreaType{CIOBIT 0x30,WBIT 0x31,DMBIT 0x02,ABIT 0x33,HBIT 0x32,CIOWORD 0xB0,WWORD 0xB1,DMWORD 0x82,AWORD 0xB3,HWORD 0xB2 } 2DataAddress.cs
/// summary
/// 地址模型类
/// /summary
public class DataAddress
{/// summary/// 区域类型/// /summarypublic AreaType AreaType { get; set; }/// summary/// Word起始地址/// /summarypublic ushort WordAddress { get; set; }/// summary/// Bit起始地址/// /summarypublic byte BitAddress { get; set; }} 3EndianType.cs 这就是大小端的列举涉及字节序列中的高位低位交换等处理
namespace Zhaoxi.Communication.Omron.Base
{/// summary/// 字节序列/// /summarypublic enum EndianType{AB, BA,ABCD, CDAB, BADC, DCBA,ABCDEFGH, GHEFCDAB, BADCFEHG, HGFEDCBA}
} 4Result.cs
通信结果类
namespace Omron.Communimcation.Fins.Omron
{/// summary/// 通讯结果类/// /summary/// typeparam nameT/typeparampublic class ResultT{/// summary/// 状态/// /summarypublic bool IsSuccessed { get; set; }/// summary/// 对应的消息/// /summarypublic string Message { get; set; }/// summary/// 数据列表/// /summarypublic ListT Datas { get; set; }public Result() : this(true, OK) { }public Result(bool state, string msg) : this(state, msg, new ListT()) { }public Result(bool state, string msg, ListT datas){this.IsSuccessed state; Message msg; Datas datas;}}public class Result : Resultbool { }
} 4、编写核心的通信类Fins
这个类就是实现读取和写入数据的方法封装了报文的每个组成部分象请求的报文头命令码数据处理发送和接收处理等字节处理寄存器的数据类型处理辅助方法等很多。该类很强大能很好的处理shortushortboolfloatOOP的思想在这个类中得到了很好的发挥注释很详细代码很规范需要很深的功底才可以写得出来的需要对fins的报文结构指令内容非常精通才能写好各位高僧能人自行阅读欢迎留言。
using Omron.Communimcation.Fins.Omron.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace Omron.Communimcation.Fins.Omron
{/// summary/// 欧姆龙FINS协议通讯库/// /summarypublic class FinsTcp{/// summary/// tcp服务器地址/// /summarystring _ip;/// summary/// tcp端口号/// /summaryint _port;/// summary/// 目标节点/// /summarybyte _da;/// summary/// 源节点/// /summarybyte _sa;/// summary/// socket连接对象全局变量/// /summarySocket socket null;/// summary/// 超时对象/// /summaryManualResetEvent TimeoutObject new ManualResetEvent(false);/// summary/// 连接状态/// /summarybool connectState false;/// summary/// 头部字节/// /summarybyte[] finsTcpHeader new byte[] { 0x46, 0x49, 0x4E, 0x53 };/// summary/// 构造方法/// /summary/// param nameipTCP服务器IP/param/// param nameportTCP服务器端口/param/// param namedaPLC节点/param/// param namesaPC端节点/parampublic FinsTcp(string ip, int port, byte da 0x00, byte sa 0x00){_ip ip;_port port;_da da;_sa sa;// 初始化一个通信对象socket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);}#region 操作PLC/// summary/// 连接PLC/// /summary/// param nametimeout超时时间/param/// returns/returnspublic Result Connect(int timeout 50){TimeoutObject.Reset();Result result new Result();try{if (socket null){throw new Exception(通信对象未初始化);}int count 0;while (count timeout){// 断线重连// 1、被服务端主动踢掉服务连接列表里已经没有当前客户端信息了// 2、断网拔网线 客户端不知道-》新的端口进行连接、服务端可以知道、检查客户端的心跳if (!(!socket.Connected || (socket.Poll(200, SelectMode.SelectRead) (socket.Available 0)))){return result;}try{socket?.Close();socket.Dispose();socket null;socket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//异步连接PLCsocket.BeginConnect(_ip, _port, callback {connectState false;var cbSocket callback.AsyncState as Socket;if (cbSocket ! null){connectState cbSocket.Connected;if (cbSocket.Connected){cbSocket.EndConnect(callback);}}TimeoutObject.Set();}, socket);TimeoutObject.WaitOne(2000, false);if (!connectState){throw new Exception(网络连接异常);}else{break;}}catch (SocketException ex){if (ex.ErrorCode 10060){throw new Exception(ex.Message);}}catch (Exception){throw new Exception(网络连接异常);}finally{count;}}if (socket null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) (socket.Available 0)))){throw new Exception(网络连接失败);}else{// 2、建立连接result this.SetupConnect();if (!result.IsSuccessed){return result;}}}catch (Exception ex){result.IsSuccessed false;result.Message ex.Message;}return result;}/// summary/// 启动PLC/// /summary/// returns/returnspublic Result Run(){return PlcSate(0x01);}/// summary/// 停止PLC/// /summary/// returns/returnspublic Result Stop(){return PlcSate(0x02);}/// summary/// PLC状态/// /summary/// param namestate0401启动0402停止/param/// returns/returnspublic Result PlcSate(byte state){Result result new Result();try{var connectState this.Connect();if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}Listbyte baseBytes this.GetBaseCommand(0x04, state); //构建命令this.Send(baseBytes);//发送命令}catch (Exception ex){result.IsSuccessed false;result.Message ex.Message;}return result;}#endregion#region 读取数据/ summary/ 读取命令原有方法/ /summary/ typeparam nameT返回的数据类型如ushort,short,int32,float等/typeparam/ param nameareaCode存储区代码/param/ param namewStartAddr开始地址16进制格式如ox64/param/ param namebStartAddrBit地址16进制格式如ox00/param/ param namecount读取个数16进制格式如读取12个就是ox0b/param/ returns/returns//public ResultT ReadT(AreaType areaCode, ushort wStartAddr, byte bStartAddr, ushort count)//{// //发送: 46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 02 00 00 0A 00 00 01 01 82 00 C8 00 00 01// //接收: 46 49 4E 53 00 00 00 18 00 00 00 02 00 00 00 00 C0 00 02 00 02 00 00 0A 00 00 01 01 00 00 02 2E// ResultT result new ResultT();// int typeLen 1;//数据类型所占的字节宽度// if (!typeof(T).Equals(typeof(bool)))// {// typeLen Marshal.SizeOfT() / 2; // 每一个数据需要多少个寄存器// }// try// {// byte[] bytes new byte[] {// 0x00,0x00,0x00,0x02, // 读写的时候固定传这个值 // 0x00,0x00,0x00,0x00, // 错误代码// 0x80,// ICF // 0x00, // Rev // 0x02, // GCT // 0x00,_da,0x00, // DNA DA1 DA2 即目标网络号目标节点号目标单元号// 0x00,_sa,0x00,// SNA SA1 SA2即源网络号源节点号源单元号// 0x00, // SID固定值 // 0x01,0x01,// 命令码读操作固定值为0101 // (byte)areaCode, // 存储区// (byte)(wStartAddr/256%256),//Word起始地址占2个字节// (byte)( wStartAddr%256),// bStartAddr,//起始位地址占1个字节// (byte)(count*typeLen/256%256),//读取个数占2个字节// (byte)(count*typeLen%256)// };// //命令字节// Listbyte commandBytes new Listbyte();// // 计算字节长度占4个字节// Listbyte lengthbyte new Listbyte();// lengthbyte.Add((byte)(bytes.Length / 256 / 256 / 256 % 256));// lengthbyte.Add((byte)(bytes.Length / 256 / 256 % 256));// lengthbyte.Add((byte)(bytes.Length / 256 % 256));// lengthbyte.Add((byte)(bytes.Length % 256));// commandBytes.AddRange(finsTcpHeader);//加入头部// commandBytes.AddRange(lengthbyte.ToArray());//加入长度 // commandBytes.AddRange(bytes);//加入协议内容// socket.Send(commandBytes.ToArray());//发送报文// //1.1接收数据中的前8个字节即fins头部 // byte[] respHeader new byte[8];// socket.Receive(respHeader, 0, 8, SocketFlags.None);// //1.2 判断前面4个字节是否是FINS// for (int i 0; i 4; i)// {// if (respHeader[i] ! finsTcpHeader[i])// {// throw new Exception(响应报文无效);// }// } // //1.3、获取剩余报文长度 00 00 00 18// byte[] lenBytes new byte[4];// lenBytes[0] respHeader[7];// lenBytes[1] respHeader[6];// lenBytes[2] respHeader[5];// lenBytes[3] respHeader[4];// int len BitConverter.ToInt32(lenBytes);// Console.WriteLine(剩余报文长度 len); // //1.4 接收剩余字节// byte[] dataBytes new byte[len];// socket.Receive(dataBytes, 0, len, SocketFlags.None);// // 1.5 错误信息判断 确定没有ErrorCode // var code dataBytes[4] | dataBytes[5] | dataBytes[6] | dataBytes[7];// if (code 0)// {// Console.WriteLine($有错误[{code}]);// } // code (dataBytes[20] 8) | dataBytes[21]; // 01 01 0000 0001 0000 0001// if (code 0)// {// Console.WriteLine($有错误[{code}]); // 字典处理257当前网络环境中无法匹配此节点// } // // 1.6 开始解析数据部分// Listbyte dataList new Listbyte(dataBytes);// dataList.RemoveRange(0, 22);//移除指定部分保留数据内容// //循环处理数据// for (int i 0; i dataList.Count;)// {// if (typeof(T) typeof(bool))//布尔类型// { // Type tConvert typeof(Convert); // //查找 convert这个类中的toboolean方法// MethodInfo method tConvert.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(mi mi.Name ToBoolean) as MethodInfo;// result.Datas.Add((T)method.Invoke(tConvert, new object[] { int.Parse(dataList[i].ToString()) }));// }// else //shortushort,float,double类型// {// Listbyte datas new Listbyte();// // Word - ShortDWrod- FloatDD- Double// for (int j 0; j typeLen * 2; j)// { // datas.Add(dataList[i]); // 只能处理2个字节的情况其他的处理不了需要进行字节序的调整// } // if (typeLen 1)//2个字节适合ushort,short,int16// {// datas new Listbyte(this.SwitchEndian(datas.ToArray(), EndianType.AB));// } // else if (typeLen 2)//4个字节,适合int32,float// {// datas new Listbyte(this.SwitchEndian(datas.ToArray(), EndianType.CDAB));// } // Type tBitConverter typeof(BitConverter);// MethodInfo method tBitConverter.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(mi mi.ReturnType typeof(T)) as MethodInfo;// if (method null)// {// Console.WriteLine(未找到匹配的数据类型转换方法);// } // result.Datas.Add((T)method?.Invoke(tBitConverter, new object[] { datas.ToArray(), 0 }));// } // }// }// catch (Exception ex)// {// return new ResultT(false, ex.Message);// }// return result;//}/// summary/// 读取操作/// /summary/// typeparam nameT返回的数据类型/typeparam/// param nameaddress存储区地址/param/// param namecount读取数量/param/// returns/returnspublic ResultT ReadT(string address, ushort count){ResultT result new ResultT();try{DataAddress dataAddress this.AnalysisAddress(address);result this.ReadT(dataAddress, count);}catch (Exception ex){result.IsSuccessed false;result.Message ex.Message;}return result;}/// summary/// 读取操作/// /summary/// typeparam nameT返回的数据类型/typeparam/// param namedataAddress地址模型/param/// param namecount读取数量/param/// returns/returnspublic ResultT ReadT(DataAddress dataAddress, ushort count){ResultT result new ResultT();int typeLen 1;if (!typeof(T).Equals(typeof(bool))){typeLen Marshal.SizeOfT() / 2;// 每一个数据需要多少个寄存器}try{//1、判断连接状态var connectState this.Connect();//没有连接成功时则返回if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}//2、构建基本命令0101表示读取0102表示写入//命令头部Listbyte baseBytes this.GetBaseCommand(0x01, 0x01);//存储区类型baseBytes.Add((byte)dataAddress.AreaType);//开始地址占3个字节baseBytes.Add((byte)(dataAddress.WordAddress / 256 % 256));baseBytes.Add((byte)(dataAddress.WordAddress % 256));baseBytes.Add(dataAddress.BitAddress);//读取数量占2个字节baseBytes.Add((byte)(count * typeLen / 256 % 256));baseBytes.Add((byte)(count * typeLen % 256));//3、发送命令this.Send(baseBytes);//4、获取响应的数据报文Listbyte dataList this.CheckResponseBytes();//5、解析数据报文for (int i 0; i dataList.Count;){if (typeof(T) typeof(bool))//bool类型的处理{dynamic boolValue dataList[i] 0x01;result.Datas.Add(boolValue);}else //shortushortfloatdouble类型的处理{Listbyte datas new Listbyte();for (int j 0; j typeLen * 2; j){datas.Add(dataList[i]); // 只能处理2个字节的情况其他的处理不了需要进行字节序的调整}datas this.SwitchBytes(datas); //交换字节顺序Type tBitConverter typeof(BitConverter);MethodInfo method tBitConverter.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(mi mi.ReturnType typeof(T)) as MethodInfo;if (method null){throw new Exception(未找到匹配的数据类型转换方法);}result.Datas.Add((T)method?.Invoke(tBitConverter, new object[] { datas.ToArray(), 0 }));}}}catch (Exception ex){return new ResultT(false, ex.Message);}return result;}#endregion#region 写入数据/// summary/// 写入操作/// /summary/// typeparam nameT写入的数据类型/typeparam/// param namevalues写入的具体数据/param/// param nameaddress写入的具体地址/param/// returns/returnspublic Result WriteT(ListT values, string address){Result result new Result();try{DataAddress dataAddress this.AnalysisAddress(address);result this.WriteT(values, dataAddress);}catch (Exception ex){result.IsSuccessed false;result.Message ex.Message;}return result;}/// summary/// 写入操作/// /summary/// typeparam nameT写入的数据类型/typeparam/// param namevalues写入的具体数据/param/// param namedataAddress地址模型/param/// returns/returnspublic Result WriteT(ListT values, DataAddress dataAddress){Result result new Result();int typeLen 1;if (!typeof(T).Equals(typeof(bool))){typeLen Marshal.SizeOfT() / 2; // 每一个数据需要多少个寄存器}try{//1、建立连接如果连接失败则抛出异常var connectState this.Connect();if (!connectState.IsSuccessed){throw new Exception(connectState.Message);}//2、加入基本命令0102表示写0101表示读Listbyte baseBytes this.GetBaseCommand(0x01, 0x02);//2.1加入存储区baseBytes.Add((byte)dataAddress.AreaType);//2.2加入地址占3个字节baseBytes.Add((byte)(dataAddress.WordAddress / 256 % 256));baseBytes.Add((byte)(dataAddress.WordAddress % 256));baseBytes.Add(dataAddress.BitAddress);//2.3 加入长度 short 是1字占1个长度 float是2字占2个长度baseBytes.Add((byte)(values.Count * typeLen / 256 % 256));baseBytes.Add((byte)(values.Count * typeLen % 256));//处理数值foreach (dynamic item in values){if (typeof(T) typeof(bool))//bool类型处理{baseBytes.Add((byte)(bool.Parse(item.ToString()) ? 0x01 : 0x00));}else // short,ushort,int32,float类型处理{Listbyte vBytes new Listbyte(BitConverter.GetBytes(item));vBytes this.SwitchBytes(vBytes);baseBytes.AddRange(vBytes);}}//3、发送命令this.Send(baseBytes);//4、检查报文写入时不需要处理返回数据this.CheckResponseBytes();}catch (Exception ex){result.IsSuccessed false;result.Message ex.Message;}return result;}#endregion#region 基本方法/// summary/// 初始化内容/// /summary/// returns/returnsprivate Result SetupConnect(){byte[] connectBytes new byte[] {// Header FINS对应的Ascii编码16进制0x46,0x49,0x4E,0x53,// Length0x00,0x00,0x00,0x0C,// Command0x00,0x00,0x00,0x00,// Error code0x00,0x00,0x00,0x00,// Client node addr0x00,0x00,0x00,0x04};try{socket.Send(connectBytes);//发送握手报文byte[] respBytes new byte[24];int count socket.Receive(respBytes, 0, 24, SocketFlags.None);// 判断是否FINS开头的报文for (int i 0; i 4; i){if (respBytes[i] ! finsTcpHeader[i])throw new Exception(连接请求响应报文无效);}// 确定没有ErrorCodevar state respBytes[12] | respBytes[13] | respBytes[14] | respBytes[15];if (state 0)throw new Exception($有错误[{state}]);}catch (Exception ex){return new Result() { IsSuccessed false, Message ex.Message };}return new Result();}/// summary/// 调整字节序/// /summary/// param namevalue/param/// param nameendianType/param/// returns/returnsbyte[] SwitchEndian(byte[] value, EndianType endianType){Listbyte result new Listbyte(value);switch (endianType){case EndianType.AB:case EndianType.ABCD:case EndianType.ABCDEFGH:result.Reverse();return result.ToArray();case EndianType.CDAB: // 4字节处理if (value.Length 4){result[3] value[2];result[2] value[3];result[1] value[0];result[0] value[1];}return result.ToArray();case EndianType.BADC: // 4字节处理if (value.Length 4){result[3] value[1];result[2] value[0];result[1] value[3];result[0] value[2];}return result.ToArray();case EndianType.GHEFCDAB: // 8字节处理if (value.Length 8){result[7] value[6];result[6] value[7];result[5] value[4];result[4] value[5];result[3] value[2];result[2] value[3];result[1] value[0];result[0] value[1];}return result.ToArray();case EndianType.BADCFEHG: // 8字节处理if (value.Length 8){result[7] value[1];result[6] value[0];result[5] value[3];result[4] value[2];result[3] value[5];result[2] value[4];result[1] value[7];result[0] value[6];}return result.ToArray();case EndianType.BA:case EndianType.DCBA:case EndianType.HGFEDCBA:return value;default:break;}return null;}/// summary/// 字节交换/// /summary/// param namebytes/param/// returns/returnsprivate Listbyte SwitchBytes(Listbyte bytes){byte temp;for (int i 0; i bytes.Count; i 2){temp bytes[i];bytes[i] bytes[i 1];bytes[i 1] temp;}return bytes;}/// summary/// 地址解析/// /summary/// param nameaddr地址/param/// returns/returns/// exception crefException/exceptionprivate DataAddress AnalysisAddress(string addr){DataAddress dataAddress new DataAddress();addr addr.ToUpper();AreaType wordCode 0x00, bitCode 0x00;if (addr.Substring(0, 3) CIO){wordCode AreaType.CIOWORD;bitCode AreaType.CIOBIT;addr addr.Substring(3);}else if (addr.Substring(0, 2) DM){wordCode AreaType.DMWORD;bitCode AreaType.DMBIT;addr addr.Substring(2);}else{switch (addr[0]){case W:bitCode AreaType.WBIT;wordCode AreaType.WWORD;break;case A:bitCode AreaType.ABIT;wordCode AreaType.AWORD;break;case H:bitCode AreaType.HBIT;wordCode AreaType.HWORD;break;}addr addr.Substring(1);}if (bitCode 0x00){throw new Exception(地址类型暂不支持);}string[] tempAddr addr.Split(.);dataAddress.AreaType wordCode;if (string.IsNullOrEmpty(tempAddr[0])){throw new Exception(地址标记错误);}ushort ws 0;if (ushort.TryParse(tempAddr[0], out ws)){dataAddress.WordAddress ws;}// 如果有小数点 if (tempAddr.Length 1){dataAddress.AreaType bitCode;if (string.IsNullOrEmpty(tempAddr[1])){throw new Exception(地址标记错误);}byte bs 0;if (byte.TryParse(tempAddr[1], out bs)){dataAddress.BitAddress bs;}}return dataAddress;}/// summary/// 创建基本命令/// /summary/// param namemCommand主命令/param/// param namesCommand次命令/param/// returns/returnsprivate Listbyte GetBaseCommand(byte mCommand, byte sCommand){return new Listbyte {// Command 读写的时候固定传这个值0x00,0x00,0x00,0x02,// Error code 错误代码0x00,0x00,0x00,0x00, // ICF0x80,// Rev 0x00,// GCT0x02,// DNA DA1 DA2即目标网络号目标节点号目标单元号也就是指PLC网络0x00,_da,0x00,// SNA SA1 SA2即源网络号源节点号源单元号也就是指PC网络0x00,_sa,0x00,// SID 固定值 0x00,//读写命令mCommand,sCommand};}/// summary/// 检查响应报文返回数据内容/// /summary/// returns/returns/// exception crefException/exceptionprivate Listbyte CheckResponseBytes(){byte[] respHeader new byte[8];//头部数据占8个字节socket.Receive(respHeader, 0, 8, SocketFlags.None); //接收fins头部//1、判断前面4个字节是否是FINSfor (int i 0; i 4; i){if (respHeader[i] ! finsTcpHeader[i]){throw new Exception(响应报文无效);}}//2、获取剩余报文长度 00 00 00 18byte[] lenBytes new byte[4];lenBytes[0] respHeader[7];lenBytes[1] respHeader[6];lenBytes[2] respHeader[5];lenBytes[3] respHeader[4];int len BitConverter.ToInt32(lenBytes, 0);//字节数组转换成int32//Console.WriteLine(剩余报文长度 len); //3、接收剩余字节byte[] dataBytes new byte[len];socket.Receive(dataBytes, 0, len, SocketFlags.None);//4、错误信息解读 确定没有ErrorCode ResponseCodevar code dataBytes[4] | dataBytes[5] | dataBytes[6] | dataBytes[7];if (code 0){throw new Exception($有错误[{code}]);}code (dataBytes[20] 8) | dataBytes[21]; /// 判断End Code 01 01 0000 0001 0000 0001 0x10 0x03 1003if (code 0){throw new Exception($有错误[{code}]); // 字典处理257当前网络环境中无法匹配此节点}//5、获取数据部分Listbyte dataList new Listbyte(dataBytes);dataList.RemoveRange(0, 22);//移除前面22个字节剩下的就是数据部分//6、返回数据部分return dataList;}/// summary/// 发送TCP数据/// /summary/// param namebaseBytes命令字节集合/paramprivate void Send(Listbyte baseBytes){//命令字节Listbyte commandBytes new Listbyte();// 计算字节长度占4个字节Listbyte lengthbyte new Listbyte();lengthbyte.Add((byte)(baseBytes.Count / 256 / 256 / 256 % 256));lengthbyte.Add((byte)(baseBytes.Count / 256 / 256 % 256));lengthbyte.Add((byte)(baseBytes.Count / 256 % 256));lengthbyte.Add((byte)(baseBytes.Count % 256));commandBytes.AddRange(finsTcpHeader);//加入头部commandBytes.AddRange(lengthbyte.ToArray());//加入长度 commandBytes.AddRange(baseBytes);//加入协议内容 socket.Send(commandBytes.ToArray());//发送命令}#endregion}
}5、测试通讯库-读取数据
这里还是以前面的存储区数据进行测试
读取CIO区0.0开始的6个bool数据
读取D区100开始的4个数据ushort类型
读取H区100开始的4个short类型的数据
读取W区100开始的5个float浮点字
读取W区104开始的2个float数据
1连接PLC
namespace Omron.Communimcation.Test
{internal class Program{static void Main(string[] args){#region 通讯库 FinsTcpLibTest();Console.WriteLine(执行完成!); #endregion Console.ReadKey();}private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;}}}
} 通讯的TCP报文 2读取CIO区0.0开始的6个bool数据
设置PLC内存区数据 private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;}//1读取CIO区0.0开始的6个bool数据 var datas5 finsTcp.Readbool(CIO0.0, 6);if (!datas5.IsSuccessed){Console.WriteLine(datas5.Message);return;}Console.WriteLine(读取CIO区0.0开始的6个bool数据);datas5.Datas.ForEach(dd Console.WriteLine(dd)); } 通信报文结果 3读取D区100开始的4个数据ushort类型 private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;} 1读取CIO区0.0开始的6个bool数据 // var datas5 finsTcp.Readbool(CIO0.0, 6);//if (!datas5.IsSuccessed)//{// Console.WriteLine(datas5.Message);// return;//}//Console.WriteLine(读取CIO区0.0开始的6个bool数据);//datas5.Datas.ForEach(dd Console.WriteLine(dd));//2、读取D区100开始的4个数据ushort类型 var datas1 finsTcp.Readushort(DM100, 4);if (!datas1.IsSuccessed){Console.WriteLine(datas1.Message);return;}Console.WriteLine(读取D区100开始的4个ushort类型数据);datas1.Datas.ForEach(dd Console.WriteLine(dd));} 通讯报文结构 4读取H区100开始的4个short类型的数据 private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;} 1读取CIO区0.0开始的6个bool数据 // var datas5 finsTcp.Readbool(CIO0.0, 6);//if (!datas5.IsSuccessed)//{// Console.WriteLine(datas5.Message);// return;//}//Console.WriteLine(读取CIO区0.0开始的6个bool数据);//datas5.Datas.ForEach(dd Console.WriteLine(dd));2、读取D区100开始的4个数据ushort类型 // var datas1 finsTcp.Readushort(DM100, 4);//if (!datas1.IsSuccessed)//{// Console.WriteLine(datas1.Message);// return;//}//Console.WriteLine(读取D区100开始的4个ushort类型数据);//datas1.Datas.ForEach(dd Console.WriteLine(dd));//3、 读取H区100开始的4个short类型的数据 var datas2 finsTcp.Readshort(H100, 4);if (!datas2.IsSuccessed){Console.WriteLine(datas2.Message);return;}Console.WriteLine(读取H区100开始的4个short类型数据);datas2.Datas.ForEach(dd Console.WriteLine(dd));} 可以看到通讯库对负数的处理报文返回的数据需要对short类型的负数进行处理因为short是有符号的10进制数包括正负整数如908-85
5读取W区100开始的5个float浮点字 private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;}1读取CIO区0.0开始的6个bool数据 // var datas5 finsTcp.Readbool(CIO0.0, 6);//if (!datas5.IsSuccessed)//{// Console.WriteLine(datas5.Message);// return;//}//Console.WriteLine(读取CIO区0.0开始的6个bool数据);//datas5.Datas.ForEach(dd Console.WriteLine(dd));2、读取D区100开始的4个数据ushort类型 // var datas1 finsTcp.Readushort(DM100, 4);//if (!datas1.IsSuccessed)//{// Console.WriteLine(datas1.Message);// return;//}//Console.WriteLine(读取D区100开始的4个ushort类型数据);//datas1.Datas.ForEach(dd Console.WriteLine(dd));3、 读取H区100开始的4个short类型的数据 // var datas2 finsTcp.Readshort(H100, 4);//if (!datas2.IsSuccessed)//{// Console.WriteLine(datas2.Message);// return;//}//Console.WriteLine(读取H区100开始的4个short类型数据);//datas2.Datas.ForEach(dd Console.WriteLine(dd));//4、读取W区100开始的5个float浮点字包括正负整数如223-987和正负小数如2.34-87.65var datas3 finsTcp.Readfloat(W100, 5);if (!datas3.IsSuccessed){Console.WriteLine(datas3.Message);return;}Console.WriteLine(读取W区100开始的5个float类型数据);datas3.Datas.ForEach(dd Console.WriteLine(dd));} fins报文 6读取W区104开始的2个float数据 private static void FinsTcpLibTest(){FinsTcp finsTcp new FinsTcp(192.168.1.4, 7788, (byte)10, (byte)04);// 创建连接 var result finsTcp.Connect();// 开始连接PLCif (!result.IsSuccessed){Console.WriteLine(result.Message);return;}1读取CIO区0.0开始的6个bool数据 // var datas5 finsTcp.Readbool(CIO0.0, 6);//if (!datas5.IsSuccessed)//{// Console.WriteLine(datas5.Message);// return;//}//Console.WriteLine(读取CIO区0.0开始的6个bool数据);//datas5.Datas.ForEach(dd Console.WriteLine(dd));2、读取D区100开始的4个数据ushort类型 // var datas1 finsTcp.Readushort(DM100, 4);//if (!datas1.IsSuccessed)//{// Console.WriteLine(datas1.Message);// return;//}//Console.WriteLine(读取D区100开始的4个ushort类型数据);//datas1.Datas.ForEach(dd Console.WriteLine(dd));3、 读取H区100开始的4个short类型的数据 // var datas2 finsTcp.Readshort(H100, 4);//if (!datas2.IsSuccessed)//{// Console.WriteLine(datas2.Message);// return;//}//Console.WriteLine(读取H区100开始的4个short类型数据);//datas2.Datas.ForEach(dd Console.WriteLine(dd));4、读取W区100开始的5个float浮点字包括正负整数如223-987和正负小数如2.34-87.65// var datas3 finsTcp.Readfloat(W100, 5);//if (!datas3.IsSuccessed)//{// Console.WriteLine(datas3.Message);// return;//}//Console.WriteLine(读取W区100开始的5个float类型数据);//datas3.Datas.ForEach(dd Console.WriteLine(dd));//4读取W区104开始的2个float数据var datas4 finsTcp.Readfloat(W104, 2);if (!datas4.IsSuccessed){Console.WriteLine(datas4.Message);return;}Console.WriteLine(读取W区104开始的2个float类型数据);datas4.Datas.ForEach(dd Console.WriteLine(dd));} 通讯报文 可以对照前面讲的C#上位机与欧姆龙PLC的通信05---- HostLink协议(C-Mode版) C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)
串口报文和TCP报文熟悉每个报文的组成部分Hostlink通讯协议有两种模式C-mode和FINS
C-mode报文在串口上通信的FINS报文在网络上通信的。
6、测试通讯库-写入数据
这里测试4种数据的写入。
写入CIO区1.0开始的6个bool数据
写入D区30开始的4个数据ushort类型
写入H区30开始的4个short类型的数据
写入W区30开始的5个float浮点字 1写入CIO区1.0开始的6个bool数据true, true, false, , falsetrue , true 写入成功看PLC内存数据 通讯报文 2写入D区30开始的4个数据ushort类型 写入成功 3写入H区30开始的4个short类型的数据 通讯报文 4写入W区30开始的5个float浮点字 可以对照前面讲的C#上位机与欧姆龙PLC的通信05---- HostLink协议(C-Mode版) C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)
串口报文和TCP报文熟悉每个报文的组成部分Hostlink通讯协议有两种模式C-mode和FINS
C-mode报文在串口上通信的FINS报文在网络上通信的。
7、小结
自己写的通讯库很强大能读取写入单个或多个数据值可以是多种数据类型可以是多个存储区的操作通讯库最后就是一个dll文件项目中直接引用就可以啦。