using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TCPCLIENT { public class Siemens200_modbusRTU { static String HEXES = "0123456789ABCDEF"; byte uchCRCHi = (byte)0xFF; byte uchCRCLo = (byte)0xFF; private String address; private CRC16 _crc = new CRC16(); #region "ModbusRTU协议说明" /**Modbus 地址 通常 Modbus 地址由 5 位数字组成,包括起始的数据类型代号,以及后面的偏移地址。Modbus Master 协议库把标 准的 Modbus 地址映射为所谓 Modbus 功能号,读写从站的数据。Modbus Master 协议库支持如下地址: · 00001 - 09999:数字量输出(线圈) · 10001 - 19999:数字量输入(触点) · 30001 - 39999:输入数据寄存器(通常为模拟量输入) · 40001 - 49999:数据保持寄存器 * Modbus 地址 S7-200 数据区 00001 ~ 00128 Q0.0 ~ Q15.7 10001 ~ 10128 I0.0 ~ I15.7 30001 ~ 30032 AIW0 ~ AIW62 40001 ~ 4xxxx T ~ T + 2 * (xxxx -1) 其中T 为S7-200 中的缓冲区起始地址,即 HoldStart。 如果已知S7-200 中的V 存储区地址,推算Modbus 地址的公式如下: Modbus 地址 = 40000 + (T/2+1) ; T 为偶数 * *功能码 主站使用相应功能码作用于此从站的效用 1 读取单个/多个线圈(离散量输出点)状态。 功能 1 返回任意个数输出点(Q)的 ON/OFF 状态。 2 读取单个/多个触点(离散量输入点)状态。 功能 2 返回任意个数输入点(I)的 ON/OFF 状态。 3 读取单个/多个保持寄存器。功能 3 返回 V 存储区的内容。在 Modbus 协议下保持寄存器都是“字”值,在一次请求中可以读取最 多 120 个字的数据。 4 读取单个/多个输入寄存器。 功能 4 返回 S7-200 的模拟量数据值。 5 写单个线圈(离散量输出点)。 功能 5 用于将离散量输出点设置为指定的值。这个点不是被强制的,用户程序可以覆盖 Modbus 通 信请求写入的值。 6 写单个保持寄存器。 功能 6 写一个值到 S7-200 的 V 存储区的保持寄存器中。 15 写多个线圈(离散量输出点)。功能 15 把多个离散量输出点的值写到 S7-200 的输出映像寄存器(Q 区)。输出点的地址必须 以字节边界起始(如 Q0.0 或 Q2.0),并且输出点的数目必须是 8 的整数倍。这是此 Modbus RTU 从站指令库的限制。些点不 是被强制的,用户程序可以覆盖 Modbus 通信请求写入的值。 16 些多个保持寄存器。 * 功能 16 写多个值到 S7-200 的 V 存储区的保持寄存器中。在一次请求中可以写最多 120 个字的数据。 */ #endregion /// <summary> /// 地址码 /// </summary> /// <param name="_address"></param> public void SetAddress(String _address) { this.address = _address; } /// <summary> /// 功能码 /// </summary> public enum modbusRTU_Func { FUNC_READ_I=0x2, //读数字输入I FUNC_READ_V=0X3, //读V寄存器 FUNC_READ_AI=0X4, //读模拟AI寄存器 FUNC_WRITE_1Q=0X5, //写单个数字输出寄存器Q FUNC_WRITE_1V=0X6, //写单个数据保存寄存器V FUNC_WRITE_NQ=0XF, //写多个数字输出寄存器Q FUNC_WRITE_NV=0X10 //写多个数据保存寄存器V } public struct strType { public byte HighType; public byte LowType; } private static byte byteLength = 8; // 字节位长 /* 整型32bit转换成字节型8bit数组 @param number 待求数值 @param length 数值所占字节数 */ private static byte[] intToByteArray(int number, int length) { byte[] byteArray = new byte[length]; int shiftNum = 0; // 移位数 for (int i = 0; i < length; i++) { shiftNum = (length - i - 1) * byteLength; byteArray[i] = (byte)((number >> shiftNum) & 0xFF); } return byteArray; } /// <summary> /// 获取读命令字节数组 /// </summary> /// <param name="Address">设备地址</param> /// <param name="Func">功能码</param> /// <param name="Type">寄存器类型</param> /// <param name="BeginAddr">起始地址</param> /// <param name="Count">读取点数</param> /// <returns>返回读取命令字节数组</returns> public byte[] GetReadCmd(byte Address,byte Func ,String Type, String BeginAddr,int Count) { List<strType> HL_Type = new List<strType>(); byte[] buf = new byte[8]; buf[0] = Address; buf[1] = Func; HL_Type=getRealAddrArray(Type, new String[] {BeginAddr}); buf[2] = HL_Type[0].LowType; buf[3] = HL_Type[0].HighType; byte[] Len_Type = intToByteArray(Count, 2); buf[4] = Len_Type[0]; buf[5] = Len_Type[1]; buf = CRC16.GetCRC16Full(new byte[] { buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]}, true); return buf; } /// <summary> ///获取写命令字节数组 /// </summary> /// <param name="Address"></param> /// <param name="Func"></param> /// <param name="Type"></param> /// <param name="Addr"></param> /// <param name="value"></param> /// <returns></returns> public byte[] GetWriteCmd(byte Address, byte Func, String Type, String Addr, byte HighType, byte Lowtype) { List<strType> HL_Type = new List<strType>(); byte[] buf = new byte[8]; buf[0] = Address; buf[1] = Func; HL_Type = getRealAddrArray(Type, new String[] { Addr }); buf[2] = HL_Type[0].LowType; buf[3] = HL_Type[0].HighType; buf[4] = HighType; buf[5] = Lowtype; buf = CRC16.GetCRC16Full(new byte[] { buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] }, true); return buf; } public List<strType> getRealAddrArray(String Type, String[] loc) { List<strType> typeList = new System.Collections.Generic.List<strType>(); switch (Type) { case "Q": Dictionary<String, Int32> Q_Map = new System.Collections.Generic.Dictionary<String, Int32>(); //输出寄存器数量:00001 ~ 00128 共128个 int Index = 0; Int32 Val = 0; for (int j = 0; j < 128; j++) { //0 7 8 17 25 Index=j/8; if (Val == 8 ) { Val = 0; } Q_Map.Add("Q"+Index+"."+Val, j); Val+=1; } for (int i = 0; i < loc.Length; i++) { if (Q_Map.ContainsKey(loc[i])==true) { int buf = Q_Map[loc[i]]; byte[] bytes = BitConverter.GetBytes(buf); strType type = new strType(); if (bytes.Length == 4) { type.HighType = bytes[1]; type.LowType =bytes[0]; } typeList.Add(type); } } break; case "I": Dictionary<String, Int32> I_Map = new System.Collections.Generic.Dictionary<String, Int32>(); //输入寄存器数量:00001 ~ 00128 共128个 Index = 0; Val = 0; for (int j = 0; j < 128; j++) { //0 7 8 17 25 Index=j/8; if (Val == 8 ) { Val = 0; } I_Map.Add("I"+Index+"."+Val, j); Val+=1; } for (int i = 0; i < loc.Length; i++) { if (I_Map.ContainsKey(loc[i])==true) { int buf = I_Map[loc[i]]; byte[] bytes = BitConverter.GetBytes(buf); strType type = new strType(); if (bytes.Length == 4) { type.HighType = bytes[1]; type.LowType =bytes[0]; } typeList.Add(type); } } break; case "AI": break; case "VW": Dictionary<String, Int32> V_Map = new System.Collections.Generic.Dictionary<String, Int32>(); //数据保持寄存器数量:40001 ~ 4XXXX 40000 + (T/2+1) ; T 为偶数 Index = 0; Val = 0; for (int j = 0; j < 9999; j++) { if (j % 2==0) { V_Map.Add("VW"+j, j/2); } } for (int i = 0; i < loc.Length; i++) { if (V_Map.ContainsKey(loc[i])==true) { int buf = V_Map[loc[i]]; byte[] bytes = BitConverter.GetBytes(buf); strType type = new strType(); if (bytes.Length == 4) { type.HighType = bytes[0]; type.LowType =bytes[1]; } typeList.Add(type); } } break; default: break; } return typeList; } public static byte[] ConvertStringToByteArray(string stringToConvert) { return (new UnicodeEncoding()).GetBytes(stringToConvert); } /// <summary> /// 字节数组转16进制字符串 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public string byteToHexStr(byte[] bytes) { string returnStr = ""; if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes[i].ToString("X2"); } } return returnStr; } /// <summary> /// 字符串转16进制字节数组 /// </summary> /// <param name="hexString"></param> /// <returns></returns> private static byte[] strToToHexByte(string hexString) { hexString = hexString.Replace(" ", ""); if ((hexString.Length % 2) != 0) hexString += " "; byte[] returnBytes = new byte[hexString.Length / 2]; for (int i = 0; i < returnBytes.Length; i++) returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); return returnBytes; } } }