- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
上一篇已经实现了ModbusTcp服务器和8个主要的功能码,只是还没有实现错误处理功能。 但是在测试客户端时却发现了上一篇的一个错误,那就是写数据成功,服务器不需要响应。 接下来要做的就是实现ModbusTcp客户端。有了清晰的协议,代码循规蹈矩的写就行了.
原始数据 其中只读寄存器和线圈都有可分辨的值 。
交互 改变线圈和寄存器的值 。
//WebModbus.cs
// 读 读写线圈
public async Task<bool[]> Request_01(ushort startIndex, ushort length){}
// 读 只读线圈
public async Task<bool[]> Request_02(ushort startIndex, ushort length){}
// 读 读写寄存器
public async Task<ushort[]> Request_03(ushort startIndex, ushort length){}
// 读 只读寄存器
public async Task<ushort[]> Request_04(ushort startIndex, ushort length){}
// 写 读写一个线圈
public async Task<ADUMessage> Request_05(ushort startIndex, bool coil){}
// 写 读写一个寄存器
public async Task<ADUMessage> Request_06(ushort startIndex, ushort register){}
// 写 读写多个线圈
public async Task<ADUMessage> Request_0f(ushort startIndex, bool[] coils){}
// 写 读写多个寄存器
public async Task<ADUMessage> Request_10(ushort startIndex, ushort[] registers){}
PrintBytes(ADUMessage.Serialze(request), "请求");
if (Client != null)
{
await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));
byte[] bytes = new byte[1024];
int msgLength = await Client.Client.ReceiveAsync(new ArraySegment<byte>(bytes));
PrintBytes(bytes.Take(msgLength).ToArray(), "响应");
}
public bool[] BytesToBools(byte[] bytes,ushort dataNumber){}
public byte[] BoolToBytes(bool[] bools){}
我们还需要一个界面区使用这个协议,所以还需要一个测试类。 命令行程序的话,就是使用while循环了,在循环中接收指令 。
private static async Task StartClient(string[] args)
{
//其他代码...
while (true)
{
Console.WriteLine("请输入指令");
string? msg = Console.ReadLine();
while (msg == null)
{
//功能码 数据
msg = Console.ReadLine();
}
try
{
string[] data = msg.Split(' ');
ushort funCode = ushort.Parse(data[0],NumberStyles.HexNumber);
ushort startIndex;
ushort length;
switch (funCode)
{
//读 读写线圈
case 0x01:
startIndex = ushort.Parse(data[1]);
length= ushort.Parse(data[2]);
var rs_01 = await webModbusClient.Request_01(startIndex, length);
PrintBools(rs_01);
break;
//读 只读线圈
case 0x02:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_02 = await webModbusClient.Request_02(startIndex, length);
PrintBools(rs_02);
break;
//读 读写寄存器
case 0x03:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_03 = await webModbusClient.Request_03(startIndex, length);
for (global::System.Int32 i = 0; i < length; i++)
{
Console.Write(rs_03[i]+" ");
}
Console.WriteLine();
break;
//读 只读寄存器
case 0x04:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_04 = await webModbusClient.Request_04(startIndex, length);
for (global::System.Int32 i = 0; i < length; i++)
{
Console.Write(rs_04[i] + " ");
}
Console.WriteLine();
break;
//写 读写一个线圈
case 0x05:
startIndex = ushort.Parse(data[1]);
var coil = bool.Parse(data[2]);
var rs_05 = await webModbusClient.Request_05(startIndex, coil);
break;
//写 读写一个寄存器
case 0x06:
startIndex = ushort.Parse(data[1]);
var register = ushort.Parse(data[2]);
var rs_06 = await webModbusClient.Request_06(startIndex, register);
break;
//写 读写多个线圈
case 0x0f:
startIndex = ushort.Parse(data[1]);
bool[] coils = new bool[data.Length - 2];
for (global::System.Int32 i = 2; i < data.Length; i++)
{
coils[i - 2] = bool.Parse(data[i]);
}
var rs_0f = await webModbusClient.Request_0f(startIndex, coils);
break;
//写 读写多个寄存器
case 0x10:
startIndex = ushort.Parse(data[1]);
ushort[] registers = new ushort[data.Length - 2];
for (global::System.Int32 i = 2; i < data.Length; i++)
{
registers[i - 2] = ushort.Parse(data[i]);
}
var rs_10 = await webModbusClient.Request_10(startIndex, registers);
break;
default:
//return Response_01(request);
break;
}
}
catch (Exception e)
{
}
}
}
/// <summary>
/// 数据仓库,144KB
/// </summary>
public class DataStore
{
/// <summary>
/// 读写16位寄存器,64KB
/// </summary>
public ushort[] HoldingRegisters;
/// <summary>
/// 只读16位寄存器,64KB
/// </summary>
public ushort[] InputRegisters;
/// <summary>
/// 读写1位线圈,8KB
/// </summary>
public bool[] CoilDiscretes;
/// <summary>
/// 只读1位线圈,8KB
/// </summary>
public bool[] CoilInputs;
public DataStore()
{
HoldingRegisters = new ushort[65536];
InputRegisters = new ushort[65536];
CoilDiscretes = new bool[65536];
CoilInputs = new bool[65536];
}
/// <summary>
/// 读 读写16位寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public ushort[] ReadHoldingRegisters(ushort startIndex, ushort length)
{
return HoldingRegisters.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();
}
/// <summary>
/// 读 只读16位寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public ushort[] ReadInputRegisters(ushort startIndex, ushort length)
{
return InputRegisters.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();
}
/// <summary>
/// 读 读写1位线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public bool[] ReadCoilDiscretes(ushort startIndex, ushort length)
{
return CoilDiscretes.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();
}
/// <summary>
/// 读 只读1位线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public bool[] ReadCoilInputs(ushort startIndex, ushort length)
{
return CoilInputs.Take(new Range(new Index(startIndex), new Index(startIndex + length))).ToArray();
}
/// <summary>
/// 写 读写16位寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="data"></param>
public void WriteHoldingRegisters(ushort startIndex, ushort[] data)
{
for (int i = 0; i < data.Length; i++)
{
if (startIndex+i < 65536)
{
HoldingRegisters[startIndex + i] = data[i];
}
}
}
/// <summary>
/// 写 读写1位线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="data"></param>
public void WriteCoilDiscretes(ushort startIndex, bool[] data)
{
for (int i = 0; i < data.Length; i++)
{
if (startIndex + i < 65536)
{
CoilDiscretes[startIndex + i] = data[i];
}
}
}
}
/// <summary>
/// Modbus报文
/// </summary>
public class ADUMessage
{
/// <summary>
/// 事务标识符
/// </summary>
public ushort Transaction { get; set; }
/// <summary>
/// 协议标识符
/// </summary>
public ushort Protocol { get; set; }
/// <summary>
/// 报文长度
/// </summary>
public ushort Length { get; set; }
/// <summary>
/// 单元标识符
/// </summary>
public byte Unit { get; set; }
/// <summary>
/// 功能码
/// </summary>
public byte FunctionCode { get; set; }
/// <summary>
/// 数据
/// </summary>
public byte[] Data { get; set; }
public static ADUMessage Deserialize(byte[] buffer)
{
//BinaryReader读取方式是小端(右边是高字节),而modbus是大端传输(左边是高字节)
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(buffer));
ADUMessage adu = new ADUMessage()
{
Transaction = reader.ReadUInt16(),
Protocol = reader.ReadUInt16(),
Length = reader.ReadUInt16(),
Unit = reader.ReadByte(),
FunctionCode = reader.ReadByte(),
Data = reader.ReadBytes(buffer.Length - 8)
};
return adu;
}
public static byte[] Serialze(ADUMessage message)
{
using (MemoryStream ms=new MemoryStream())
{
BinaryWriter writer = new BigEndianBinaryWriter(ms);
writer.Write(message.Transaction);
writer.Write(message.Protocol);
writer.Write(message.Length);
writer.Write(message.Unit);
writer.Write(message.FunctionCode);
writer.Write(message.Data);
return ms.ToArray();
}
}
}
/// <summary>
/// Modbus服务器
/// </summary>
public class WebModbusServer
{
public DataStore store = new DataStore();
public ADUMessage HandleRequest(byte[] buffer)
{
ADUMessage request = ADUMessage.Deserialize(buffer);
switch (request.FunctionCode)
{
//读 读写线圈
case 0x01:
return Response_01(request);
//读 只读线圈
case 0x02:
return Response_02(request);
//读 读写寄存器
case 0x03:
return Response_03(request);
//读 只读寄存器
case 0x04:
return Response_04(request);
//写 读写一个线圈
case 0x05:
return Response_05(request);
//写 读写一个寄存器
case 0x06:
return Response_06(request);
//写 读写多个线圈
case 0x0f:
return Response_0f(request);
//写 读写多个寄存器
case 0x10:
return Response_10(request);
default:
return Response_01(request);
}
}
public byte[] CoilToBytes(bool[] bools)
{
int byteCount = (bools.Length + 7) / 8; // 计算所需的字节数
byte[] bytes = new byte[byteCount];
for (int i = 0; i < bools.Length; i++)
{
int byteIndex = i / 8; // 计算当前布尔值应该存储在哪个字节中
int bitIndex = i % 8; // 计算当前布尔值应该存储在字节的哪个位上
if (bools[i])
{
// 设置对应位为 1
bytes[byteIndex] |= (byte)(1 << bitIndex);
}
else
{
// 对应位保持为 0,无需额外操作
}
}
return bytes;
}
/// <summary>
/// 读 读写线圈
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_01(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
BinaryWriter writer;
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
bool[] data = store.ReadCoilDiscretes(StartAddress, DataNumber);
byte[] coilBytes = CoilToBytes(data);
byte[] dataBytes = new byte[coilBytes.Length + 1];
writer = new BinaryWriter(new MemoryStream(dataBytes));
writer.Write((byte)coilBytes.Length);
writer.Write(coilBytes);
ADUMessage response = new ADUMessage()
{
Transaction = request.Transaction,
Protocol = request.Protocol,
Length = (ushort)(dataBytes.Length + 2),
Unit = request.Unit,
FunctionCode = request.FunctionCode,
Data = dataBytes,
};
return response;
}
/// <summary>
/// 读 只读线圈
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_02(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
BinaryWriter writer;
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
bool[] data = store.ReadCoilInputs(StartAddress, DataNumber);
byte[] coilBytes = CoilToBytes(data);
byte[] dataBytes = new byte[coilBytes.Length + 1];
writer = new BinaryWriter(new MemoryStream(dataBytes));
writer.Write((byte)coilBytes.Length);
writer.Write(coilBytes);
ADUMessage response = new ADUMessage()
{
Transaction = request.Transaction,
Protocol = request.Protocol,
Length = (ushort)(dataBytes.Length + 2),
Unit = request.Unit,
FunctionCode = request.FunctionCode,
Data = dataBytes,
};
return response;
}
/// <summary>
/// 读 读写寄存器
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_03(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
BinaryWriter writer;
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
ushort[] data = store.ReadHoldingRegisters(StartAddress, DataNumber);
byte[] dataBytes = new byte[data.Length * 2 + 1];
writer = new BigEndianBinaryWriter(new MemoryStream(dataBytes));
writer.Write((byte)(data.Length * 2));
foreach (ushort value in data)
{
writer.Write(value);
}
Array.Resize(ref dataBytes, dataBytes.Length + 1);
ADUMessage response = new ADUMessage()
{
Transaction = request.Transaction,
Protocol = request.Protocol,
Length = (ushort)(dataBytes.Length + 2),
Unit = request.Unit,
FunctionCode = request.FunctionCode,
Data = dataBytes,
};
return response;
}
/// <summary>
/// 读 只读寄存器
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_04(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
BinaryWriter writer;
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
ushort[] data = store.ReadInputRegisters(StartAddress, DataNumber);
byte[] dataBytes = new byte[data.Length * 2 + 1];
writer = new BigEndianBinaryWriter(new MemoryStream(dataBytes));
writer.Write((byte)(data.Length * 2));
foreach (ushort value in data)
{
writer.Write(value);
}
Array.Resize(ref dataBytes, dataBytes.Length + 1);
ADUMessage response = new ADUMessage()
{
Transaction = request.Transaction,
Protocol = request.Protocol,
Length = (ushort)(dataBytes.Length + 2),
Unit = request.Unit,
FunctionCode = request.FunctionCode,
Data = dataBytes,
};
return response;
}
/// <summary>
/// 写 读写一个线圈
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_05(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
ushort StartAddress, coli;
StartAddress = reader.ReadUInt16();
coli = reader.ReadUInt16();
store.WriteCoilDiscretes(StartAddress, new bool[] { coli ==0xff00?true:false});
return request;
}
/// <summary>
/// 写 读写一个寄存器
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_06(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
ushort StartAddress, register;
StartAddress = reader.ReadUInt16();
register = reader.ReadUInt16();
store.WriteHoldingRegisters(StartAddress, new ushort[] { register });
return request;
}
/// <summary>
/// 写 读写多个线圈
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_0f(ADUMessage request)
{
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
byte byteNumber = reader.ReadByte();
//线圈是小端传输
byte[] bytes = reader.ReadBytes(byteNumber);
bool[] data=new bool[DataNumber];
byte index = 0;
foreach (var item in bytes)
{
//1000 0000
byte rr = (byte)0x01;
for (int i = 0; i < 8; i++)
{
if (index< DataNumber)
{
var result = rr & item;
if (result > 0)
{
data[index] = true;
}
else
{
data[index] = false;
}
//0100 0000
rr <<= 1;
index++;
}
else
{
break;
}
}
}
store.WriteCoilDiscretes(StartAddress, data);
return request;
}
/// <summary>
/// 写 读写多个寄存器
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private ADUMessage Response_10(ADUMessage request)
{
//寄存器是大端传输
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(request.Data));
ushort StartAddress, DataNumber;
StartAddress = reader.ReadUInt16();
DataNumber = reader.ReadUInt16();
byte byteNumber = reader.ReadByte();
ushort[] data = new ushort[byteNumber / 2];
for (int i = 0; i < data.Length; i++)
{
data[i] = reader.ReadUInt16();
}
store.WriteHoldingRegisters(StartAddress, data);
return request;
}
}
/// <summary>
/// Modbus客户端
/// </summary>
public class WebModbusClient
{
public ushort Transaction { get; set; }
public TcpClient Client { get; }
public WebSocket WebSocket { get; set; }
public ADUMessage request { get; set; }
public ADUMessage response { get; set; }
public WebModbusClient(TcpClient client)
{
Transaction = 0x00;
Client = client;
}
public WebModbusClient(WebSocket webSocket)
{
Transaction = 0x00;
WebSocket = webSocket;
}
private ADUMessage CreateMsg()
{
ADUMessage message = new ADUMessage();
message.Transaction = Transaction;
Transaction++;
message.Protocol = 0x00;
message.Unit = 0x00;
this.request = message;
return message;
}
public void PrintBytes(byte[] bytes, string prefix = "")
{
Console.Write(prefix);
for (int i = 0; i < bytes.Length; i++)
{
if (i < 2)
{
Console.ForegroundColor = ConsoleColor.Red;
}
else if (i < 4)
{
Console.ForegroundColor = ConsoleColor.Green;
}
else if (i < 6)
{
Console.ForegroundColor = ConsoleColor.Blue;
}
else if (i < 7)
{
Console.ForegroundColor = ConsoleColor.Yellow;
}
else if (i < 8)
{
Console.ForegroundColor = ConsoleColor.DarkCyan;
}
else
{
Console.ForegroundColor = ConsoleColor.White;
}
Console.Write(bytes[i].ToString("X2") + " ");
}
Console.WriteLine();
}
public bool[] BytesToBools(byte[] bytes,ushort dataNumber)
{
int index = 0;
bool[] bools = new bool[dataNumber];
foreach (var item in bytes)
{
//1000 0000
byte rr = (byte)0x01;
for (int i = 0; i < 8; i++)
{
if (index < dataNumber)
{
var result = rr & item;
if (result > 0)
{
bools[index] = true;
}
else
{
bools[index] = false;
}
//0100 0000
rr <<= 1;
index++;
}
else
{
break;
}
}
}
return bools;
}
private async Task<ADUMessage> SendWithResponse(ADUMessage request)
{
PrintBytes(ADUMessage.Serialze(request), "请求");
if (Client != null)
{
await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));
byte[] bytes = new byte[1024];
int msgLength = await Client.Client.ReceiveAsync(new ArraySegment<byte>(bytes));
this.response = ADUMessage.Deserialize(bytes.Take(msgLength).ToArray());
PrintBytes(bytes.Take(msgLength).ToArray(), "响应");
return response;
}
else if(WebSocket != null)
{
await WebSocket.SendAsync(new ArraySegment<byte>(ADUMessage.Serialze(request)),WebSocketMessageType.Binary,true,CancellationToken.None);
byte[] bytes = new byte[1024];
var result = await WebSocket.ReceiveAsync(new ArraySegment<byte>(bytes),CancellationToken.None);
this.response = ADUMessage.Deserialize(bytes.Take(result.Count).ToArray());
PrintBytes(bytes.Take(result.Count).ToArray(), "响应");
return response;
}
else
{
throw new Exception("没有传入连接");
}
}
private async Task SendNoResponse(ADUMessage request)
{
PrintBytes(ADUMessage.Serialze(request), "请求");
if (Client != null)
{
await Client.Client.SendAsync(new Memory<byte>(ADUMessage.Serialze(request)));
}
else if (WebSocket != null)
{
await WebSocket.SendAsync(new ArraySegment<byte>(ADUMessage.Serialze(request)), WebSocketMessageType.Binary, true, CancellationToken.None);
}
else
{
throw new Exception("没有传入连接");
}
}
public byte[] BoolToBytes(bool[] bools)
{
int byteCount = (bools.Length + 7) / 8; // 计算所需的字节数
byte[] bytes = new byte[byteCount];
for (int i = 0; i < bools.Length; i++)
{
int byteIndex = i / 8; // 计算当前布尔值应该存储在哪个字节中
int bitIndex = i % 8; // 计算当前布尔值应该存储在字节的哪个位上
if (bools[i])
{
// 设置对应位为 1
bytes[byteIndex] |= (byte)(1 << bitIndex);
}
else
{
// 对应位保持为 0,无需额外操作
}
}
return bytes;
}
/// <summary>
/// 读 读写线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public async Task<bool[]> Request_01(ushort startIndex, ushort length)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode= 0x01;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
writer.Write(length);
var response = await SendWithResponse(request);
BinaryReader reader = new BinaryReader(new MemoryStream(response.Data));
byte byteLength=reader.ReadByte();
byte[] bytes = reader.ReadBytes(byteLength);
bool[] bools= BytesToBools(bytes,length);
return bools;
}
/// <summary>
/// 读 只读线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public async Task<bool[]> Request_02(ushort startIndex, ushort length)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode = 0x02;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
writer.Write(length);
var response = await SendWithResponse(request);
BinaryReader reader = new BinaryReader(new MemoryStream(response.Data));
byte byteLength = reader.ReadByte();
byte[] bytes = reader.ReadBytes(byteLength);
bool[] bools = BytesToBools(bytes, length);
return bools;
}
/// <summary>
/// 读 读写寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public async Task<ushort[]> Request_03(ushort startIndex, ushort length)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode = 0x03;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
writer.Write(length);
var response = await SendWithResponse(request);
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(response.Data));
byte byteLength = reader.ReadByte();
ushort[] registers = new ushort[length];
for (int i = 0; i < length; i++)
{
registers[i] = reader.ReadUInt16();
}
return registers;
}
/// <summary>
/// 读 只读寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public async Task<ushort[]> Request_04(ushort startIndex, ushort length)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode = 0x04;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
writer.Write(length);
var response = await SendWithResponse(request);
BinaryReader reader = new BigEndianBinaryReader(new MemoryStream(response.Data));
byte byteLength = reader.ReadByte();
ushort[] registers = new ushort[length];
for (int i = 0; i < registers.Length; i++)
{
registers[i] = reader.ReadUInt16();
}
return registers;
}
/// <summary>
/// 写 读写一个线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="coil"></param>
/// <returns></returns>
public async Task<ADUMessage> Request_05(ushort startIndex, bool coil)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode = 0x05;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
if (coil)
{
writer.Write((ushort)0xff00);
}
else
{
writer.Write((ushort)0x0000);
}
await SendNoResponse(request);
return request;
}
/// <summary>
/// 写 读写一个寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="register"></param>
/// <returns></returns>
public async Task<ADUMessage> Request_06(ushort startIndex, ushort register)
{
var request = CreateMsg();
request.Length = 0x06;
request.FunctionCode = 0x06;
request.Data = new byte[4];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write(startIndex);
writer.Write(register);
await SendNoResponse(request);
return request;
}
/// <summary>
/// 写 读写多个线圈
/// </summary>
/// <param name="startIndex"></param>
/// <param name="coils"></param>
/// <returns></returns>
public async Task<ADUMessage> Request_0f(ushort startIndex, bool[] coils)
{
var request = CreateMsg();
request.FunctionCode = 0x0f;
request.Data = new byte[4+1+(coils.Length+7)/8];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write((ushort)startIndex);
var coilBytes = BoolToBytes(coils);
request.Length = (ushort)(7 + coilBytes.Length);
writer.Write((ushort)coils.Length);
writer.Write((byte)coilBytes.Length);
writer.Write(coilBytes);
await SendNoResponse(request);
return request;
}
/// <summary>
/// 写 读写多个寄存器
/// </summary>
/// <param name="startIndex"></param>
/// <param name="registers"></param>
/// <returns></returns>
public async Task<ADUMessage> Request_10(ushort startIndex, ushort[] registers)
{
var request = CreateMsg();
request.Length = (ushort)(7+ registers.Length * 2);
request.FunctionCode = 0x10;
request.Data = new byte[4+1+registers.Length*2];
BinaryWriter writer = new BigEndianBinaryWriter(new MemoryStream(request.Data));
writer.Write((ushort)startIndex);
writer.Write((ushort)registers.Length);
writer.Write((byte)(registers.Length * 2));
for (int i = 0; i < registers.Length; i++)
{
writer.Write(registers[i]);
}
await SendNoResponse(request);
return request;
}
}
internal class Program
{
static WebModbusServer webModbusServer;
static void Main(string[] args)
{
webModbusServer = new WebModbusServer();
//服务器
if (args.Length == 1)
{
//webModbusServer.store.WriteCoilDiscretes(0, new bool[] { true, true });
//webModbusServer.store.CoilInputs[0] = true;
//webModbusServer.store.CoilInputs[1] = true;
StartServer(args[0]);
}
//客户端
else
{
Task.Run(async () =>
{
await StartClient(args);
}).Wait();
}
}
private static void StartServer(string args)
{
int serverPort = Convert.ToInt32(args);
var server = new TcpListener(IPAddress.Parse("127.0.0.1"), serverPort);
Console.WriteLine($"TCP服务器 127.0.0.1:{serverPort}");
server.Start();
int cnt = 0;
Task.Run(async () =>
{
List<TcpClient> clients = new List<TcpClient>();
while (true)
{
TcpClient client = await server.AcceptTcpClientAsync();
clients.Add(client);
cnt++;
var ep = client.Client.RemoteEndPoint as IPEndPoint;
Console.WriteLine($"TCP客户端_{cnt} {ep.Address}:{ep.Port}");
//给这个客户端开一个聊天线程
//操作系统将会根据游客端口对应表将控制权交给对应游客线程
//StartChat(client);
StartModbus(client);
}
}).Wait();
}
private static async Task StartClient(string[] args)
{
int clientPort = Convert.ToInt32(args[0]);
int serverPort = Convert.ToInt32(args[1]);
var client = new TcpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), clientPort));
Console.WriteLine($"TCP客户端 127.0.0.1:{clientPort}");
await client.ConnectAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), serverPort));
Console.WriteLine($"连接到 127.0.0.1:{serverPort}");
WebModbusClient webModbusClient = new WebModbusClient(client);
Console.WriteLine("【功能码】 【地址】 【数量|数据】");
while (true)
{
Console.WriteLine("请输入指令");
string? msg = Console.ReadLine();
while (msg == null)
{
//功能码 数据
msg = Console.ReadLine();
}
try
{
string[] data = msg.Split(' ');
ushort funCode = ushort.Parse(data[0],NumberStyles.HexNumber);
ushort startIndex;
ushort length;
switch (funCode)
{
//读 读写线圈
case 0x01:
startIndex = ushort.Parse(data[1]);
length= ushort.Parse(data[2]);
var rs_01 = await webModbusClient.Request_01(startIndex, length);
PrintBools(rs_01);
break;
//读 只读线圈
case 0x02:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_02 = await webModbusClient.Request_02(startIndex, length);
PrintBools(rs_02);
break;
//读 读写寄存器
case 0x03:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_03 = await webModbusClient.Request_03(startIndex, length);
for (global::System.Int32 i = 0; i < length; i++)
{
Console.Write(rs_03[i]+" ");
}
Console.WriteLine();
break;
//读 只读寄存器
case 0x04:
startIndex = ushort.Parse(data[1]);
length = ushort.Parse(data[2]);
var rs_04 = await webModbusClient.Request_04(startIndex, length);
for (global::System.Int32 i = 0; i < length; i++)
{
Console.Write(rs_04[i] + " ");
}
Console.WriteLine();
break;
//写 读写一个线圈
case 0x05:
startIndex = ushort.Parse(data[1]);
var coil = bool.Parse(data[2]);
var rs_05 = await webModbusClient.Request_05(startIndex, coil);
break;
//写 读写一个寄存器
case 0x06:
startIndex = ushort.Parse(data[1]);
var register = ushort.Parse(data[2]);
var rs_06 = await webModbusClient.Request_06(startIndex, register);
break;
//写 读写多个线圈
case 0x0f:
startIndex = ushort.Parse(data[1]);
bool[] coils = new bool[data.Length - 2];
for (global::System.Int32 i = 2; i < data.Length; i++)
{
coils[i - 2] = bool.Parse(data[i]);
}
var rs_0f = await webModbusClient.Request_0f(startIndex, coils);
break;
//写 读写多个寄存器
case 0x10:
startIndex = ushort.Parse(data[1]);
ushort[] registers = new ushort[data.Length - 2];
for (global::System.Int32 i = 2; i < data.Length; i++)
{
registers[i - 2] = ushort.Parse(data[i]);
}
var rs_10 = await webModbusClient.Request_10(startIndex, registers);
break;
default:
//return Response_01(request);
break;
}
}
catch (Exception e)
{
}
}
}
public static async Task StartModbus(TcpClient client)
{
var buffer = new byte[1024 * 4];
while (client.Connected)
{
int msgLength = await client.Client.ReceiveAsync(new ArraySegment<byte>(buffer));
//关闭连接时会接收到一次空消息,不知道为什么
if (msgLength>0)
{
PrintBytes(buffer.Take(msgLength).ToArray(), "请求 ");
ADUMessage response = webModbusServer.HandleRequest(buffer.Take(msgLength).ToArray());
await client.Client.SendAsync(ADUMessage.Serialze(response));
PrintBytes(ADUMessage.Serialze(response), "响应 ");
}
}
}
public static void PrintBytes(byte[] bytes,string prefix="")
{
Console.Write(prefix);
for (int i = 0; i < bytes.Length; i++)
{
if (i < 2)
{
Console.ForegroundColor = ConsoleColor.Red;
}
else if(i<4)
{
Console.ForegroundColor = ConsoleColor.Green;
}
else if(i<6)
{
Console.ForegroundColor= ConsoleColor.Blue;
}
else if (i < 7)
{
Console.ForegroundColor = ConsoleColor.Yellow;
}
else if (i<8)
{
Console.ForegroundColor = ConsoleColor.DarkCyan;
}
else
{
Console.ForegroundColor = ConsoleColor.White;
}
Console.Write(bytes[i].ToString("X2") + " ");
}
Console.WriteLine();
}
public static void PrintBools(bool[] bools)
{
for (int i = 0; i < bools.Length; i++)
{
Console.Write(bools[i] + " ");
}
Console.WriteLine();
}
}
public class BigEndianBinaryReader : BinaryReader
{
public BigEndianBinaryReader(Stream input) : base(input)
{
}
public override short ReadInt16()
{
var data = base.ReadBytes(2);
Array.Reverse(data);
return BitConverter.ToInt16(data, 0);
}
public override ushort ReadUInt16()
{
var data = base.ReadBytes(2);
Array.Reverse(data);
return BitConverter.ToUInt16(data, 0);
}
public override int ReadInt32()
{
var data = base.ReadBytes(4);
Array.Reverse(data);
return BitConverter.ToInt32(data, 0);
}
public override uint ReadUInt32()
{
var data = base.ReadBytes(4);
Array.Reverse(data);
return BitConverter.ToUInt32(data, 0);
}
public override long ReadInt64()
{
var data = base.ReadBytes(8);
Array.Reverse(data);
return BitConverter.ToInt64(data, 0);
}
public override float ReadSingle()
{
var data = base.ReadBytes(4);
Array.Reverse(data);
return BitConverter.ToSingle(data, 0);
}
public override double ReadDouble()
{
var data = base.ReadBytes(8);
Array.Reverse(data);
return BitConverter.ToDouble(data, 0);
}
// 可以继续添加其他方法来支持更多数据类型的大端读取
}
public class BigEndianBinaryWriter : BinaryWriter
{
public BigEndianBinaryWriter(Stream input) : base(input)
{
}
public override void Write(ushort value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
public override void Write(short value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
public override void Write(uint value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
public override void Write(int value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
public override void Write(ulong value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
public override void Write(long value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
base.Write(bytes);
}
// 可以继续添加其他方法来支持更多数据类型的大端写入
}
程序所需命令行参数的一种方式是在项目文件种指定,这在调试时比较方便 。
<PropertyGroup>
<StartArguments>5234</StartArguments>
</PropertyGroup>
可以注意到ModbusTcp消息的解析和Tcp没有什么关系。因此,验证了服务器和客户端的正确性之后,就可以把Tcp连接改为WebSocket连接了.
最后此篇关于基于WebSocket的modbus通信(二)-客户端的文章就讲到这里了,如果你想了解更多关于基于WebSocket的modbus通信(二)-客户端的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
大多数语言都支持双向进程通信。例如,在 Python 中,我可以(草率地)执行以下操作: >>> from subprocess import * >>> p = Popen('nslookup',
致力于使用 C++ 在 arduino 和 PC (Win 7) 之间进行通信。使用 WriteFile 和 ReadFile 创建通信或简单地发送或接收数据没有问题。但是当我想以某种方式“协调”沟通
我们正在开发一个基于微服务的应用程序。它们将使用 Helm Package Manager 部署到 kubernetes,并且它们都存储了自己的存储库和 helm chart。以下是我们微服务的名称。
我正在开发一个大型 MVVM 应用程序。我为此使用了 MVVM 轻量级工具包。该应用程序就像一个带有后退和前进按钮的网络浏览器。主视图是一个用户控件。我在主视图用户控件中放置了后退和前进按钮。主视图又
我在 java 和 freepascal(lazarus) 应用程序之间的通信有问题。我使用套接字。它们正确连接。一切都很顺利,直到我想从一个应用程序向另一个应用程序发送一些东西。在java而不是“a
我已经使用客户端套接字和服务器套接字使用C#编写了群聊。 当我使用VS 2017在自己的PC中运行程序(服务器和客户端)时,客户端和服务器之间的通信工作正常。 当我在笔记本电脑中运行客户端程序,并在自
Kubernetes 中两个不同 Pod 之间的通信是如何发生的? 就我而言,我有两个 Pod:前端和后端,它们都有不同的容器。 我希望我的前端 pod 与后端 pod 通信,但我不想使用后端 pod
我正在尝试在浏览器中嵌入的 flash 实例与在 C# WinForms 应用程序中运行的 flash 实例之间进行通信...我收到一个编译错误,内容为: 1119 Access of possibl
鉴于网络上缺乏信息,请问一个问题:我要在 Android 中创建一个应用程序,使用一个数据库应用程序 rails 。为此,我需要一个手动 session 。所以如果有人准备好了示例/教程显示通信 an
我正在编写一个应用程序,它将通过 MySQL 数据库对用户进行身份验证。我已经用 Java (android) 编写了它,但现在正在移植到 Windows 手机。 PHP 文件使用 $get 然后回显
是否可以通过互联网在两个不同设备上的两个不同应用程序之间建立通信。我想从设备 A 上的应用程序点击一个设备 B 上的应用程序,然后从设备 B 上的应用程序获取数据到设备 A 上的应用程序。如果可能,如
这是脚本: 它被放置在其他网站上。 com 并显示一个 iframe。如果有人点击 iframe 中的某个内容,脚本应该将一个 div 写入 othersite 。 com. 所以我的问题是如何做到
你好我是 php 的新手,我用 c++ 编写了整个代码并想在 php 中使用这段代码。所以我为我的代码制作了 dll 以使用它。但是我不能在 php 中使用这个 dll,可以谁能给我完整的代码来使用
我确定之前已经有人问过(并回答过)此类问题,所以如果是这样,请将我链接到之前的讨论... 在 C++ 中,假设我有一个 ClassA 类型的对象,其中包含一个 ClassB 类型的私有(private
我正在尝试使用 ATmega32 进行串行通信。首先,我使用 RS232,使用 USB-to-RS232 建立使用串行终端的接收和传输(在我的例子中是 tera 术语)。无论我从串行终端 Atmega
我找不到适用于 Ruby 的 SSL 实现。 我的部分项目需要服务器和客户端之间的安全通信链接,我希望为此使用 SSL 以创建安全 session 。 谢谢 最佳答案 如果你使用 Ruby 1.9.x
我正在尝试在客户端/服务器之间进行 SSL 通信。 到目前为止,我已经从 keystore 创建了 java.security.cert.X509Certificate。接下来我应该怎么做才能使这次沟
我在与 Windows 上的 USB 设备 通信时遇到问题。我不能使用 libusb 或 WinUSB,因为我有一个特定的驱动程序(Silabs USB 到 UART,这是一个 USB 到串口的桥接器
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我发现 xcom 实际上是将数据写入数据库并从其他任务中提取数据。我的数据集很大,将其腌制并写入数据库会导致一些不必要的延迟。有没有办法在不使用 xcom 的情况下在同一 Airflow Dag 中的
我是一名优秀的程序员,十分优秀!