Simens PLC200 -modbusRTU 协议解析


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;
}

}

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值