modbusslave数据无法读取c#

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了modbusslave数据无法读取c#相关的知识,希望对你有一定的参考价值。

modbusslave数据无法读取:
private TcpClient tcpClient;
public bool Open(string ip,int port)
try
tcpClient = new TcpClient();
tcpClient.Connect(IPAddress.Parse(ip), port);
return true;
catch(SocketException e)
//LogHelper.WriteLog(e.Message);
return false;


LogHelper代码这里就不写,与主题无关,个人感觉这样的代码也没必要去运行一下,看看能明白就行了。去运行这个事情,有时会耽误太多的时间。这里IP和端口号都是由设备方提供的。真实程序一般都把这两个参数写配置文件中。
设备连上以后,下一步当然就是读取数据。Modbus的基本原理就是程序向设备请求,需要读取哪个数据,设备就会返回相应的数据。我们知道机器或者说是电脑是只认识01001这样的字符串的。所以所谓的Modbus协议,说得简单一点,就是规定这样一个0101字符各代表什么含义。
/// <summary>
/// 读取数据 Modbus
/// </summary>
/// <param name="rData">结果</param>
/// <param name="id">设备号</param>
/// <param name="address">设备地址</param>
/// <param name="len">长度-多少个设备</param>
/// <returns>数据读取结果 是否成功</returns>
public bool ReceiveData(ref short[] rData, short id, short address, short len)

try

short m = Convert.ToInt16(new Random().Next(2, 20));
rData = null;
byte[] bs = Receive(m, id, address, len);
byte[] b = TrimModbus(bs, m, id, len);
if (b==null) return false;
List<short> data = new List<short>(255);
for (int i = 0; i < b.Length-1; i++)

if (!Convert.ToBoolean(i & 1))

byte[] temp = new byte[] b[i+1], b[i] ;
data.Add(BitConverter.ToInt16(temp, 0));


rData = data.ToArray();
return true;

catch (Exception e)
LogHelper.WriteLog("返回Modbus数据错误"+ e.Message);
return false;


这个其实更多的是处理数据异常,LogHelper与前面一样,核心好像还不在了,就是那个Receive方法。
/// <summary>
/// 读取 Modbus
///00 00 00 00 00 0d 01 03 0A 14 00 14 00 14 00 14 00 14 00
/// </summary>
/// <param name="m">标示</param>
/// <param name="id">设备码</param>
/// <param name="address">开始地址</param>
/// <param name="len">设备数量</param>
/// <returns></returns>
private byte[] Receive(short m, short id, short address, short len)

try

if (tcpClient == null || !tcpClient.Connected) return null;
byte[] data = GetSrcData(m, id, address, len);
//00 00 00 00 00 06 01 03 00 00 00 05
tcpClient.Client.Send(data, data.Length, SocketFlags.None);
int size = len * 2 + 9;
byte[] rData = new byte[size];
tcpClient.Client.Receive(rData, size, SocketFlags.None);
//string t1 = TranBytes(rData);
return rData;
catch(SocketException e)
if (e.ErrorCode != 10004)

LogHelper.WriteLog(e.Message);

if (tcpClient != null)
tcpClient.Close();
tcpClient = null;

return null;


#endregion
上面的代码可以说是Modbus协议核心,其实就是SOCKET发送数据和接受数据,发送是告诉主机需要取那些的数据。接受就是把主机返回来的数据接受过来。
//发送
//00 00 00 00 00 06 01 03 00 00 00 05
/// <summary>
/// 发送字节数
/// </summary>
/// <param name="m"></param>
/// <param name="len"></param>
/// <param name="id"></param>
/// <param name="address"></param>
/// <returns></returns>
private byte[] GetSrcData(short m, short id, short add, short len)

List<byte> data = new List<byte>(255);
data.AddRange(ValueHelper.Instance.GetBytes(m)); // 00 01
data.AddRange(new byte[] 0x00, 0x00 ); // 00 00
data.AddRange(ValueHelper.Instance.GetBytes(Convert.ToInt16(6))); //字节数 00 06
data.Add(Convert.ToByte(id)); //路由码 01
data.Add(Convert.ToByte(3)); //功能码 3-读 03
data.AddRange(ValueHelper.Instance.GetBytes(add)); //开始地址 00 00
data.AddRange(ValueHelper.Instance.GetBytes(len)); //设备数量 00 05
return data.ToArray();

好,到这里基本上搞定了。其实很多逻辑都在代码中,说简单的就是某个ID设备,从哪个地址开始,读几个设备的值,这里需要注意是short 不要用32位的int去替换,结果会不一样的。仔细看看估计大家都能明白,也没有什么神秘的东西。
哦,对了还ValueHelper代码
using System;
using System.Collections.Generic;
using System.Text;
namespace Modbus
public class ValueHelper

#region 大小端判断
public static bool LittleEndian = false;
static ValueHelper()

unsafe

int tester = 1;
LittleEndian = (*(byte*)(&tester)) == (byte)1;


#endregion
#region Factory
public static ValueHelper _Instance = null;
internal static ValueHelper Instance

get

if (_Instance == null)

_Instance = LittleEndian ? new LittleEndianValueHelper() : new ValueHelper();
//_Instance = new ValueHelper();

return _Instance;


#endregion
protected ValueHelper()


public virtual Byte[] GetBytes(short value)

return BitConverter.GetBytes(value);

public virtual Byte[] GetBytes(int value)

return BitConverter.GetBytes(value);

public virtual Byte[] GetBytes(float value)

return BitConverter.GetBytes(value);

public virtual Byte[] GetBytes(double value)

return BitConverter.GetBytes(value);

public virtual short GetShort(byte[] data)

return BitConverter.ToInt16(data, 0);

public virtual int GetInt(byte[] data)

return BitConverter.ToInt32(data, 0);

public virtual float GetFloat(byte[] data)

return BitConverter.ToSingle(data, 0);

public virtual double GetDouble(byte[] data)

return BitConverter.ToDouble(data, 0);


internal class LittleEndianValueHelper : ValueHelper

public override Byte[] GetBytes(short value)

return this.Reverse(BitConverter.GetBytes(value));

public override Byte[] GetBytes(int value)

return this.Reverse(BitConverter.GetBytes(value));

public override Byte[] GetBytes(float value)

return this.Reverse(BitConverter.GetBytes(value));

publ
参考技术A SOCKET客户端与设备建立连接,关于SOCKET,我们已经说过了,那么下面的代码就非常easy了。说简单点,先不要去想什么Modbus,就认为有一台服务器,开了SOCKET服务在哪里。所以如下代码就水到渠成了。
private TcpClient tcpClient;
public bool Open(string ip,int port)
try
tcpClient = new TcpClient();

tcpClient.Connect(IPAddress.Parse(ip), port);

return true;
catch(SocketException e)
//LogHelper.WriteLog(e.Message);
return false;


LogHelper代码这里就不写,与主题无关,个人感觉这样的代码也没必要去运行一下,看看能明白就行了。去运行这个事情,有时会耽误太多的时间。这里IP和端口号都是由设备方提供的。真实程序一般都把这两个参数写配置文件中。

设备连上以后,下一步当然就是读取数据。Modbus的基本原理就是程序向设备请求,需要读取哪个数据,设备就会返回相应的数据。我们知道机器或者说是电脑是只认识01001这样的字符串的。所以所谓的Modbus协议,说得简单一点,就是规定这样一个0101字符各代表什么含义。

/// <summary>
/// 读取数据 Modbus
/// </summary>
/// <param name="rData">结果</param>
/// <param name="id">设备号</param>
/// <param name="address">设备地址</param>
/// <param name="len">长度-多少个设备</param>
/// <returns>数据读取结果 是否成功</returns>
public bool ReceiveData(ref short[] rData, short id, short address, short len)

try

short m = Convert.ToInt16(new Random().Next(2, 20));
rData = null;

byte[] bs = Receive(m, id, address, len);
byte[] b = TrimModbus(bs, m, id, len);

if (b==null) return false;

List<short> data = new List<short>(255);
for (int i = 0; i < b.Length-1; i++)

if (!Convert.ToBoolean(i & 1))

byte[] temp = new byte[] b[i+1], b[i] ;
data.Add(BitConverter.ToInt16(temp, 0));


rData = data.ToArray();

return true;

catch (Exception e)
LogHelper.WriteLog("返回Modbus数据错误"+ e.Message);
return false;


这个其实更多的是处理数据异常,LogHelper与前面一样,核心好像还不在了,就是那个Receive方法。

/// <summary>
/// 读取 Modbus
///00 00 00 00 00 0d 01 03 0A 14 00 14 00 14 00 14 00 14 00
/// </summary>
/// <param name="m">标示</param>
/// <param name="id">设备码</param>
/// <param name="address">开始地址</param>
/// <param name="len">设备数量</param>
/// <returns></returns>
private byte[] Receive(short m, short id, short address, short len)

try

if (tcpClient == null || !tcpClient.Connected) return null;

byte[] data = GetSrcData(m, id, address, len);

//00 00 00 00 00 06 01 03 00 00 00 05
tcpClient.Client.Send(data, data.Length, SocketFlags.None);

int size = len * 2 + 9;

byte[] rData = new byte[size];

tcpClient.Client.Receive(rData, size, SocketFlags.None);

//string t1 = TranBytes(rData);

return rData;

catch(SocketException e)
if (e.ErrorCode != 10004)

LogHelper.WriteLog(e.Message);


if (tcpClient != null)
tcpClient.Close();
tcpClient = null;


return null;


#endregion
上面的代码可以说是Modbus协议核心,其实就是SOCKET发送数据和接受数据,发送是告诉主机需要取那些的数据。接受就是把主机返回来的数据接受过来。

//发送
//00 00 00 00 00 06 01 03 00 00 00 05
/// <summary>
/// 发送字节数
/// </summary>
/// <param name="m"></param>
/// <param name="len"></param>
/// <param name="id"></param>
/// <param name="address"></param>
/// <returns></returns>
private byte[] GetSrcData(short m, short id, short add, short len)

List<byte> data = new List<byte>(255);

data.AddRange(ValueHelper.Instance.GetBytes(m)); // 00 01
data.AddRange(new byte[] 0x00, 0x00 ); // 00 00
data.AddRange(ValueHelper.Instance.GetBytes(Convert.ToInt16(6))); //字节数 00 06
data.Add(Convert.ToByte(id)); //路由码 01
data.Add(Convert.ToByte(3)); //功能码 3-读 03
data.AddRange(ValueHelper.Instance.GetBytes(add)); //开始地址 00 00
data.AddRange(ValueHelper.Instance.GetBytes(len)); //设备数量 00 05
return data.ToArray();

好,到这里基本上搞定了。其实很多逻辑都在代码中,说简单的就是某个ID设备,从哪个地址开始,读几个设备的值,这里需要注意是short 不要用32位的int去替换,结果会不一样的。仔细看看估计大家都能明白,也没有什么神秘的东西。

哦,对了还ValueHelper代码

using System;
using System.Collections.Generic;
using System.Text;

namespace Modbus
public class ValueHelper

#region 大小端判断

public static bool LittleEndian = false;

static ValueHelper()

unsafe

int tester = 1;
LittleEndian = (*(byte*)(&tester)) == (byte)1;


#endregion

#region Factory
public static ValueHelper _Instance = null;
internal static ValueHelper Instance

get

if (_Instance == null)

_Instance = LittleEndian ? new LittleEndianValueHelper() : new ValueHelper();
//_Instance = new ValueHelper();

return _Instance;


#endregion

protected ValueHelper()




public virtual Byte[] GetBytes(short value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(int value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(float value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(double value)

return BitConverter.GetBytes(value);


public virtual short GetShort(byte[] data)

return BitConverter.ToInt16(data, 0);


public virtual int GetInt(byte[] data)

return BitConverter.ToInt32(data, 0);


public virtual float GetFloat(byte[] data)

return BitConverter.ToSingle(data, 0);


public virtual double GetDouble(byte[] data)

return BitConverter.ToDouble(data, 0);



internal class LittleEndianValueHelper : ValueHelper

public override Byte[] GetBytes(short value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] GetBytes(int value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] GetBytes(float value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] GetBytes(double value)

return this.Reverse(BitConverter.GetBytes(value));


public virtual short GetShort(byte[] data)

return BitConverter.ToInt16(this.Reverse(data), 0);


public virtual int GetInt(byte[] data)

return BitConverter.ToInt32(this.Reverse(data), 0);


public virtual float GetFloat(byte[] data)

return BitConverter.ToSingle(this.Reverse(data), 0);


public virtual double GetDouble(byte[] data)

return BitConverter.ToDouble(this.Reverse(data), 0);


private Byte[] Reverse(Byte[] data)

Array.Reverse(data);
return data;



不好意思,有个方法给忘记了

private byte[] TrimModbus(byte[] d, short m, short id, short len)

int size = Convert.ToInt32(len) * 2;
int dLen = size + 9;

if (d == null || d.Length != dLen || m != Convert.ToInt16(d[1]) || id != Convert.ToInt16(d[6]))

return null;

byte[] n = new byte[size];
Array.Copy(d, 9, n, 0, size);
return n;

所有代码都贴出来了,但是如果实在想运行的,也是需要简单整理的。
参考技术B SOCKET客户端与设备建立连接,关于SOCKET,我们已经说过了,那么下面的代码就非常easy了。说简单点,先不要去想什么Modbus,就认为有一台服务器,开了SOCKET服务在哪里。所以如下代码就水到渠成了。
private TcpClient tcpClient;
public bool Open(string ip,int port)
try
tcpClient = new TcpClient();

tcpClient.Connect(IPAddress.Parse(ip), port);

return true;
catch(SocketException e)
//LogHelper.WriteLog(e.Message);
return false;


LogHelper代码这里就不写,与主题无关,个人感觉这样的代码也没必要去运行一下,看看能明白就行了。去运行这个事情,有时会耽误太多的时间。这里IP和端口号都是由设备方提供的。真实程序一般都把这两个参数写配置文件中。

设备连上以后,下一步当然就是读取数据。Modbus的基本原理就是程序向设备请求,需要读取哪个数据,设备就会返回相应的数据。我们知道机器或者说是电脑是只认识01001这样的字符串的。所以所谓的Modbus协议,说得简单一点,就是规定这样一个0101字符各代表什么含义。

/// <summary>
/// 读取数据 Modbus
/// </summary>
/// <param name="rData">结果</param>
/// <param name="id">设备号</param>
/// <param name="address">设备地址</param>
/// <param name="len">长度-多少个设备</param>
/// <returns>数据读取结果 是否成功</returns>
public bool ReceiveData(ref short[] rData, short id, short address, short len)

try

short m = Convert.ToInt16(new Random().Next(2, 20));
rData = null;

byte[] bs = Receive(m, id, address, len);
byte[] b = TrimModbus(bs, m, id, len);

if (b==null) return false;

List<short> data = new List<short>(255);
for (int i = 0; i < b.Length-1; i++)

if (!Convert.ToBoolean(i & 1))

byte[] temp = new byte[] b[i+1], b[i] ;
data.Add(BitConverter.ToInt16(temp, 0));


rData = data.ToArray();

return true;

catch (Exception e)
LogHelper.WriteLog("返回Modbus数据错误"+ e.Message);
return false;


这个其实更多的是处理数据异常,LogHelper与前面一样,核心好像还不在了,就是那个Receive方法。

/// <summary>
/// 读取 Modbus
///00 00 00 00 00 0d 01 03 0A 14 00 14 00 14 00 14 00 14 00
/// </summary>
/// <param name="m">标示</param>
/// <param name="id">设备码</param>
/// <param name="address">开始地址</param>
/// <param name="len">设备数量</param>
/// <returns></returns>
private byte[] Receive(short m, short id, short address, short len)

try

if (tcpClient == null || !tcpClient.Connected) return null;

byte[] data = GetSrcData(m, id, address, len);

//00 00 00 00 00 06 01 03 00 00 00 05
tcpClient.Client.Send(data, data.Length, SocketFlags.None);

int size = len * 2 + 9;

byte[] rData = new byte[size];

tcpClient.Client.Receive(rData, size, SocketFlags.None);

//string t1 = TranBytes(rData);

return rData;

catch(SocketException e)
if (e.ErrorCode != 10004)

LogHelper.WriteLog(e.Message);


if (tcpClient != null)
tcpClient.Close();
tcpClient = null;


return null;


#endregion
上面的代码可以说是Modbus协议核心,其实就是SOCKET发送数据和接受数据,发送是告诉主机需要取那些的数据。接受就是把主机返回来的数据接受过来。

//发送
//00 00 00 00 00 06 01 03 00 00 00 05
/// <summary>
/// 发送字节数
/// </summary>
/// <param name="m"></param>
/// <param name="len"></param>
/// <param name="id"></param>
/// <param name="address"></param>
/// <returns></returns>
private byte[] GetSrcData(short m, short id, short add, short len)

List<byte> data = new List<byte>(255);

data.AddRange(ValueHelper.Instance.GetBytes(m)); // 00 01
data.AddRange(new byte[] 0x00, 0x00 ); // 00 00
data.AddRange(ValueHelper.Instance.GetBytes(Convert.ToInt16(6))); //字节数 00 06
data.Add(Convert.ToByte(id)); //路由码 01
data.Add(Convert.ToByte(3)); //功能码 3-读 03
data.AddRange(ValueHelper.Instance.GetBytes(add)); //开始地址 00 00
data.AddRange(ValueHelper.Instance.GetBytes(len)); //设备数量 00 05
return data.ToArray();

好,到这里基本上搞定了。其实很多逻辑都在代码中,说简单的就是某个ID设备,从哪个地址开始,读几个设备的值,这里需要注意是short 不要用32位的int去替换,结果会不一样的。仔细看看估计大家都能明白,也没有什么神秘的东西。

哦,对了还ValueHelper代码

using System;
using System.Collections.Generic;
using System.Text;

namespace Modbus
public class ValueHelper

#region 大小端判断

public static bool LittleEndian = false;

static ValueHelper()

unsafe

int tester = 1;
LittleEndian = (*(byte*)(&tester)) == (byte)1;


#endregion

#region Factory
public static ValueHelper _Instance = null;
internal static ValueHelper Instance

get

if (_Instance == null)

_Instance = LittleEndian ? new LittleEndianValueHelper() : new ValueHelper();
//_Instance = new ValueHelper();

return _Instance;


#endregion

protected ValueHelper()




public virtual Byte[] GetBytes(short value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(int value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(float value)

return BitConverter.GetBytes(value);


public virtual Byte[] GetBytes(double value)

return BitConverter.GetBytes(value);


public virtual short GetShort(byte[] data)

return BitConverter.ToInt16(data, 0);


public virtual int GetInt(byte[] data)

return BitConverter.ToInt32(data, 0);


public virtual float GetFloat(byte[] data)

return BitConverter.ToSingle(data, 0);


public virtual double GetDouble(byte[] data)

return BitConverter.ToDouble(data, 0);



internal class LittleEndianValueHelper : ValueHelper

public override Byte[] GetBytes(short value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] GetBytes(int value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] GetBytes(float value)

return this.Reverse(BitConverter.GetBytes(value));


public override Byte[] Ge
参考技术C 前面连续写了关于SOCKET编程的东西,似乎有点高大上,为了学习而学习。因此这里我们来整点实际应用的东西。C#如何读取Modbus数据,Modbus很多人可能一点都不知道,也正常,隔行如隔山嘛。Modbus在自动化行业就不一样,属于路人皆知的东西,很多设备、程序都与Modbus息息相关。 参考技术D 前面连续写了关于SOCKET编程的东西,似乎有点高大上,为了学习而学习。因此这里我们来整点实际应用的东西。C#如何读取Modbus数据,Modbus很多人可能一点都不知道,也正常,隔行如隔山嘛。Modbus在自动化行业就不一样,属于路人皆知的东西,很多设备、程序都与Modbus息息相关。

为啥我在 C# 中收到“无法读取属性 HRESULT:0x80650002”?

【中文标题】为啥我在 C# 中收到“无法读取属性 HRESULT:0x80650002”?【英文标题】:Why am I getting "Attribute cannot be read HRESULT : 0x80650002" in C#?为什么我在 C# 中收到“无法读取属性 HRESULT:0x80650002”? 【发布时间】:2015-09-19 20:36:33 【问题描述】:

我收到异常 Attribute cannot be read HRESULT : 0x80650002 from the following line:-

GattReadResult readResult = 等待 tempMeasurementCharacterisitic[0].ReadValueAsync();

如何解决这个问题。我正在为 HealthThermometer 做这个。特征 UUID 0x2A1C

【问题讨论】:

反对票有什么解释吗? 【参考方案1】:

知道了。问题是我试图读取的特征不允许被这个特定的服务读取。如果您看到蓝牙 helath 温度计服务的规范,您会看到无法读取特征 uuid 0x2A1C。但是,您可以使用指示方法。所以不要阅读使用characteritic.valuechanged += value_change_event_handler;

【讨论】:

以上是关于modbusslave数据无法读取c#的主要内容,如果未能解决你的问题,请参考以下文章

modbus汇总

ModbusPoll及ModbusSlave安装及使用指南

modbus主机从机虚拟串口调试软件

modbus主机从机虚拟串口调试软件

modbus主机从机虚拟串口调试软件

Modbus Slave缓冲区溢出漏洞CVE-2022-1068分析与复现