@@ -0,0 +1,45 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace ModbusDemo.Device | |||||
{ | |||||
public interface IStreamResource : IDisposable | |||||
{ | |||||
int InfiniteTimeout { get; } | |||||
/// <summary> | |||||
/// Gets or sets the number of milliseconds before a timeout occurs when a read operation does not finish. | |||||
/// </summary> | |||||
int ReadTimeout { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the number of milliseconds before a timeout occurs when a write operation does not finish. | |||||
/// </summary> | |||||
int WriteTimeout { get; set; } | |||||
/// <summary> | |||||
/// Purges the receive buffer. | |||||
/// </summary> | |||||
void DiscardInBuffer(); | |||||
/// <summary> | |||||
/// Reads a number of bytes from the input buffer and writes those bytes into a byte array at the specified offset. | |||||
/// </summary> | |||||
/// <param name="buffer">The byte array to write the input to.</param> | |||||
/// <param name="offset">The offset in the buffer array to begin writing.</param> | |||||
/// <param name="count">The number of bytes to read.</param> | |||||
/// <returns>The number of bytes read.</returns> | |||||
int Read(byte[] buffer, int offset, int count); | |||||
/// <summary> | |||||
/// Writes a specified number of bytes to the port from an output buffer, starting at the specified offset. | |||||
/// </summary> | |||||
/// <param name="buffer">The byte array that contains the data to write to the port.</param> | |||||
/// <param name="offset">The offset in the buffer array to begin writing.</param> | |||||
/// <param name="count">The number of bytes to write.</param> | |||||
void Write(byte[] buffer, int offset, int count); | |||||
} | |||||
} |
@@ -17,11 +17,14 @@ namespace ModbusDemo.Device | |||||
private SerialPort _serialPort; | private SerialPort _serialPort; | ||||
//用于操作数据库 | //用于操作数据库 | ||||
private ModbusDbContext _modbusDbContext; | private ModbusDbContext _modbusDbContext; | ||||
//TODO,修改 | |||||
private SerialPortAdapter _portAdapter; | |||||
public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext) | public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext) | ||||
{ | { | ||||
_serialPort = serialPort; | _serialPort = serialPort; | ||||
_modbusDbContext = modbusDbContext; | _modbusDbContext = modbusDbContext; | ||||
_portAdapter = new SerialPortAdapter(serialPort); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 用来发送读写线圈的指令 | /// 用来发送读写线圈的指令 | ||||
@@ -94,15 +97,24 @@ namespace ModbusDemo.Device | |||||
} | } | ||||
} | } | ||||
//将操作失败的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
try | |||||
{ | |||||
//将操作失败的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读线圈"; | |||||
m.ResponseData = ""; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读线圈"; | |||||
m.ResponseData = ""; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | |||||
return resultValue; | return resultValue; | ||||
} | } | ||||
@@ -115,15 +127,24 @@ namespace ModbusDemo.Device | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | ||||
{ | { | ||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读线圈"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
try | |||||
{ | |||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读线圈"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | |||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
@@ -255,21 +276,29 @@ namespace ModbusDemo.Device | |||||
} | } | ||||
} | } | ||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "写线圈"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
//判断发送回来的数据是否正确 | |||||
if (CheckData.CheckResponse(response)) | |||||
try | |||||
{ | { | ||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "写线圈"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | } | ||||
//判断发送回来的数据是否正确 | |||||
CheckData.CheckResponse(response); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -347,12 +376,21 @@ namespace ModbusDemo.Device | |||||
//将操作失败的指令,插入数据库 | //将操作失败的指令,插入数据库 | ||||
string RequestStr = ByteArrayToString(sendByte); | string RequestStr = ByteArrayToString(sendByte); | ||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读寄存器"; | |||||
m.ResponseData = ""; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
try | |||||
{ | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读寄存器"; | |||||
m.ResponseData = ""; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | |||||
return resultValue; | return resultValue; | ||||
@@ -367,15 +405,24 @@ namespace ModbusDemo.Device | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | ||||
{ | { | ||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读寄存器"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = responseStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
try | |||||
{ | |||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "读寄存器"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | |||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
if (CheckData.CheckResponse(response)) | if (CheckData.CheckResponse(response)) | ||||
@@ -479,16 +526,25 @@ namespace ModbusDemo.Device | |||||
attempt++; | attempt++; | ||||
} | } | ||||
} | } | ||||
try | |||||
{ | |||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "写寄存器"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
//将操作的指令,插入数据库 | |||||
string RequestStr = ByteArrayToString(sendByte); | |||||
string responseStr = ByteArrayToString(response); | |||||
ModbusLog m = new ModbusLog(); | |||||
m.OperationType = "写寄存器"; | |||||
m.ResponseData = responseStr; | |||||
m.RequestData = RequestStr; | |||||
_modbusDbContext.Add(m); | |||||
_modbusDbContext.SaveChanges(); | |||||
MessageBox.Show("0x15:数据库插入错误"); | |||||
} | |||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
CheckData.CheckResponse(response); | CheckData.CheckResponse(response); | ||||
@@ -506,7 +562,7 @@ namespace ModbusDemo.Device | |||||
if (byteArray == null || byteArray.Length == 0) | if (byteArray == null || byteArray.Length == 0) | ||||
return string.Empty; | return string.Empty; | ||||
System.Text.StringBuilder sb = new System.Text.StringBuilder(); | |||||
StringBuilder sb = new StringBuilder(); | |||||
// 处理第一个元素(无前导空格) | // 处理第一个元素(无前导空格) | ||||
sb.Append(byteArray[0].ToString("X2")); | sb.Append(byteArray[0].ToString("X2")); | ||||
@@ -0,0 +1,71 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.IO.Ports; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace ModbusDemo.Device | |||||
{ | |||||
public class SerialPortAdapter:IStreamResource | |||||
{ | |||||
private string NewLine = Environment.NewLine; | |||||
private SerialPort _serialPort; | |||||
public SerialPortAdapter(SerialPort serialPort) | |||||
{ | |||||
Debug.Assert(serialPort != null, "Argument serialPort cannot be null."); | |||||
_serialPort = serialPort; | |||||
_serialPort.NewLine = NewLine; | |||||
} | |||||
public int InfiniteTimeout | |||||
{ | |||||
get { return SerialPort.InfiniteTimeout; } | |||||
} | |||||
public int ReadTimeout | |||||
{ | |||||
get { return _serialPort.ReadTimeout; } | |||||
set { _serialPort.ReadTimeout = value; } | |||||
} | |||||
public int WriteTimeout | |||||
{ | |||||
get { return _serialPort.WriteTimeout; } | |||||
set { _serialPort.WriteTimeout = value; } | |||||
} | |||||
public void DiscardInBuffer() | |||||
{ | |||||
_serialPort.DiscardInBuffer(); | |||||
} | |||||
public int Read(byte[] buffer, int offset, int count) | |||||
{ | |||||
return _serialPort.Read(buffer, offset, count); | |||||
} | |||||
public void Write(byte[] buffer, int offset, int count) | |||||
{ | |||||
_serialPort.Write(buffer, offset, count); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
Dispose(true); | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
protected virtual void Dispose(bool disposing) | |||||
{ | |||||
if (disposing) | |||||
{ | |||||
_serialPort?.Dispose(); | |||||
_serialPort = null; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -6,8 +6,15 @@ | |||||
<Nullable>disable</Nullable> | <Nullable>disable</Nullable> | ||||
<ImplicitUsings>enable</ImplicitUsings> | <ImplicitUsings>enable</ImplicitUsings> | ||||
<UseWPF>true</UseWPF> | <UseWPF>true</UseWPF> | ||||
<ApplicationIcon>ModbusRTU_64x64.ico</ApplicationIcon> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<Content Include="ModbusRTU_64x64.ico"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</Content> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="MaterialDesignThemes" Version="5.2.1" /> | <PackageReference Include="MaterialDesignThemes" Version="5.2.1" /> | ||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.27" /> | <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.27" /> | ||||
@@ -24,4 +31,8 @@ | |||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<Folder Include="bin\Debug\net8.0-windows\" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -7,6 +7,9 @@ using System.Threading.Tasks; | |||||
namespace ModbusDemo.Model | namespace ModbusDemo.Model | ||||
{ | { | ||||
/// <summary> | |||||
/// 操作数据库的上下文 | |||||
/// </summary> | |||||
public class ModbusDbContext:DbContext | public class ModbusDbContext:DbContext | ||||
{ | { | ||||
protected ModbusDbContext() | protected ModbusDbContext() | ||||
@@ -7,6 +7,9 @@ using System.Threading.Tasks; | |||||
namespace ModbusDemo.Model | namespace ModbusDemo.Model | ||||
{ | { | ||||
/// <summary> | |||||
/// 表的映射 | |||||
/// </summary> | |||||
public class ModbusLog | public class ModbusLog | ||||
{ | { | ||||
//数据库主键 | //数据库主键 | ||||
@@ -17,6 +20,7 @@ namespace ModbusDemo.Model | |||||
//请求的字节信息 | //请求的字节信息 | ||||
public string RequestData { get; set; } | public string RequestData { get; set; } | ||||
//返回的字节信息 | //返回的字节信息 | ||||
public string ResponseData { get; set; } | public string ResponseData { get; set; } | ||||
//操作的时间 | //操作的时间 | ||||
@@ -9,7 +9,11 @@ namespace ModbusDemo.Uitls | |||||
{ | { | ||||
public static class CheckData | public static class CheckData | ||||
{ | { | ||||
/// <summary> | |||||
/// 检测返回的数据格式是否正确 | |||||
/// </summary> | |||||
/// <param name="response"></param> | |||||
/// <returns></returns> | |||||
public static bool CheckResponse(byte[] response) | public static bool CheckResponse(byte[] response) | ||||
{ | { | ||||
if (response.Length == 0) | if (response.Length == 0) | ||||
@@ -88,8 +88,8 @@ | |||||
Grid.Column="4" | Grid.Column="4" | ||||
Width="80" | Width="80" | ||||
Height="40" | Height="40" | ||||
IsEnabled="{Binding IsEnable}" | |||||
materialDesign:ButtonAssist.CornerRadius="20" | materialDesign:ButtonAssist.CornerRadius="20" | ||||
IsEnabled="{Binding IsEnable}" | |||||
Command="{Binding ReadCoilCmm}" | Command="{Binding ReadCoilCmm}" | ||||
Content="读取" | Content="读取" | ||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | Style="{StaticResource MaterialDesignRaisedDarkButton}"> | ||||
@@ -154,16 +154,16 @@ | |||||
<DataGrid ItemsSource="{Binding ModbusLogList}" AutoGenerateColumns="False" IsReadOnly="True" ColumnWidth="*"> | <DataGrid ItemsSource="{Binding ModbusLogList}" AutoGenerateColumns="False" IsReadOnly="True" ColumnWidth="*"> | ||||
<DataGrid.Columns> | <DataGrid.Columns> | ||||
<!-- 操作类型列 --> | <!-- 操作类型列 --> | ||||
<DataGridTextColumn Header="操作类型" Binding="{Binding OperationType}" Width="100" /> | |||||
<DataGridTextColumn Width="100" Header="操作类型" Binding="{Binding OperationType}" /> | |||||
<!-- 请求数据列 --> | <!-- 请求数据列 --> | ||||
<DataGridTextColumn Header="请求数据" Binding="{Binding RequestData}" /> | |||||
<DataGridTextColumn Header="请求数据" Binding="{Binding RequestData}" /> | |||||
<!-- 响应数据列 --> | <!-- 响应数据列 --> | ||||
<DataGridTextColumn Header="响应数据" Binding="{Binding ResponseData}" /> | <DataGridTextColumn Header="响应数据" Binding="{Binding ResponseData}" /> | ||||
<!-- 操作时间列 --> | <!-- 操作时间列 --> | ||||
<DataGridTextColumn Header="操作时间" Binding="{Binding Time, StringFormat='yyyy-MM-dd HH:mm:ss'}" Width="200"/> | |||||
<DataGridTextColumn Width="200" Header="操作时间" Binding="{Binding Time, StringFormat='yyyy-MM-dd HH:mm:ss'}" /> | |||||
</DataGrid.Columns> | </DataGrid.Columns> | ||||
</DataGrid> | </DataGrid> | ||||
</Grid> | </Grid> | ||||
@@ -88,8 +88,8 @@ | |||||
Grid.Column="4" | Grid.Column="4" | ||||
Width="80" | Width="80" | ||||
Height="40" | Height="40" | ||||
IsEnabled="{Binding IsEnable}" | |||||
materialDesign:ButtonAssist.CornerRadius="20" | materialDesign:ButtonAssist.CornerRadius="20" | ||||
IsEnabled="{Binding IsEnable}" | |||||
Command="{Binding ReadRegisterCmm}" | Command="{Binding ReadRegisterCmm}" | ||||
Content="读取" | Content="读取" | ||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | Style="{StaticResource MaterialDesignRaisedDarkButton}"> | ||||
@@ -142,8 +142,8 @@ | |||||
Grid.Column="4" | Grid.Column="4" | ||||
Width="80" | Width="80" | ||||
Height="40" | Height="40" | ||||
IsEnabled="{Binding IsEnable}" | |||||
materialDesign:ButtonAssist.CornerRadius="20" | materialDesign:ButtonAssist.CornerRadius="20" | ||||
IsEnabled="{Binding IsEnable}" | |||||
Command="{Binding WriteRegisterCmm}" | Command="{Binding WriteRegisterCmm}" | ||||
Content="写入" | Content="写入" | ||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | Style="{StaticResource MaterialDesignRaisedDarkButton}"> | ||||
@@ -1,4 +1,6 @@ | |||||
using Modbus.Device; | using Modbus.Device; | ||||
using ModbusDemo.Model; | |||||
using ModbusDemo.Uitls; | |||||
using Prism.Commands; | using Prism.Commands; | ||||
using Prism.Mvvm; | using Prism.Mvvm; | ||||
using System; | using System; | ||||
@@ -51,11 +53,12 @@ namespace ModbusDemo.VIewModel | |||||
ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); | ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); | ||||
_serialPort = serialPort; | _serialPort = serialPort; | ||||
} | } | ||||
/// <summary> | |||||
/// 读1000个寄存器 | |||||
/// </summary> | |||||
private void ReadOddRegister() | private void ReadOddRegister() | ||||
{ | { | ||||
// 创建 Modbus RTU 主站 | // 创建 Modbus RTU 主站 | ||||
IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(_serialPort); | IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(_serialPort); | ||||
byte slaveId = 1; | byte slaveId = 1; | ||||
ushort totalRegisters = 1000; | ushort totalRegisters = 1000; | ||||
@@ -80,7 +83,7 @@ namespace ModbusDemo.VIewModel | |||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
Console.WriteLine($"线程 {chunkIndex} 读取寄存器时出错: {ex.Message}"); | |||||
MessageBox.Show($"线程 {chunkIndex} 读取寄存器时出错: {ex.Message}"); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
@@ -102,6 +105,150 @@ namespace ModbusDemo.VIewModel | |||||
ReadResult = result.ToString(); | ReadResult = result.ToString(); | ||||
} | } | ||||
private void ReadOddRegister2() | |||||
{ | |||||
byte slaveId = 1; | |||||
ushort totalRegisters = 1000; | |||||
ushort chunkSize = 100; | |||||
int numChunks = (int)Math.Ceiling((double)totalRegisters / chunkSize); | |||||
ushort[] allRegisters = new ushort[totalRegisters]; | |||||
Task[] tasks = new Task[numChunks]; | |||||
DateTime startTime = DateTime.Now; | |||||
for (int i = 0; i < numChunks; i++) | |||||
{ | |||||
ushort startAddress = (ushort)(10300 + i * chunkSize); | |||||
ushort currentChunkSize = 100; | |||||
int chunkIndex = i; | |||||
tasks[i] = Task.Run(() => | |||||
{ | |||||
ushort[] registers = ReadRegisters(slaveId, startAddress, currentChunkSize); | |||||
Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); | |||||
}); | |||||
} | |||||
// 等待所有任务完成 | |||||
Task.WaitAll(tasks); | |||||
Time = (int)(DateTime.Now - startTime).TotalMilliseconds; | |||||
StringBuilder result = new StringBuilder(); | |||||
int count = 0; | |||||
for (int i = 1; i < totalRegisters; i += 2) | |||||
{ | |||||
result.Append(allRegisters[i].ToString() + " "); | |||||
count++; | |||||
if (count % 50 == 0) | |||||
{ | |||||
result.AppendLine(); | |||||
} | |||||
} | |||||
ReadResult = result.ToString(); | |||||
} | |||||
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | |||||
{ | |||||
object _lock = new object(); | |||||
ushort[] resultValue = null; | |||||
if (!_serialPort.IsOpen) | |||||
{ | |||||
MessageBox.Show("串口没有链接,请先链接"); | |||||
return resultValue; | |||||
} | |||||
List<byte> sendByteList = new List<byte>(); | |||||
//设置从站地址 | |||||
sendByteList.Add(slaveAddress); | |||||
//设置功能码 | |||||
sendByteList.Add(0x03); | |||||
//设置起始地址的高位和低位 | |||||
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]); | |||||
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]); | |||||
//设置读取几个线圈的高位和低位 | |||||
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]); | |||||
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]); | |||||
//获取CRC校验码 | |||||
byte[] getCRC = sendByteList.ToArray(); | |||||
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length); | |||||
sendByteList.Add(resultCRC[0]); | |||||
sendByteList.Add(resultCRC[1]); | |||||
byte[] sendByte = sendByteList.ToArray(); | |||||
int maxRetries = 3; // 最大重试次数 | |||||
int attempt = 0; | |||||
List<byte> responseData = new List<byte>(); | |||||
byte[] response = null; | |||||
_serialPort.Write(sendByte, 0, sendByte.Length); | |||||
while (attempt < maxRetries) | |||||
{ | |||||
try | |||||
{ | |||||
lock (_lock) | |||||
{ | |||||
//使用时间去轮询查询数据 | |||||
DateTime startTime = DateTime.Now; | |||||
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) | |||||
{ | |||||
int bytesToRead = _serialPort.BytesToRead; | |||||
if (bytesToRead > 0) | |||||
{ | |||||
byte[] buffer = new byte[bytesToRead]; | |||||
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); | |||||
responseData.AddRange(buffer.Take(bytesRead)); | |||||
} | |||||
//延迟20毫秒,节约cpu资源 | |||||
Task.Delay(20); | |||||
} | |||||
response = responseData.ToArray(); | |||||
if (response[0] == 0x00) | |||||
{ | |||||
continue; | |||||
} | |||||
resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints); | |||||
return resultValue; | |||||
} | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
attempt++; | |||||
} | |||||
} | |||||
return resultValue; | |||||
} | |||||
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | |||||
{ | |||||
//判断发送回来的数据是否正确 | |||||
if (CheckData.CheckResponse(response)) | |||||
{ | |||||
ushort[] registers = new ushort[numberOfPoints]; | |||||
for (int i = 0; i < numberOfPoints; i++) | |||||
{ | |||||
int pos = 3 + i * 2; | |||||
// 大端序转换 (高位在前) | |||||
registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]); | |||||
} | |||||
return registers; | |||||
} | |||||
else | |||||
{ | |||||
return null; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -50,6 +50,8 @@ namespace ModbusDemo.VIewModel | |||||
+ ",数据位:" + _serialPort.DataBits + ",校验位:" + _serialPort.Parity + ",停止位:" + _serialPort.StopBits; | + ",数据位:" + _serialPort.DataBits + ",校验位:" + _serialPort.Parity + ",停止位:" + _serialPort.StopBits; | ||||
} | } | ||||
} | } | ||||
#region 读取定义 | #region 读取定义 | ||||
@@ -66,7 +66,7 @@ namespace ModbusDemo.VIewModel | |||||
LeftMenusList.Add(new MenusInfo() { Icon = "AllInclusive", MenuName = "线圈操作", ViewName = "CoilUC" }); | LeftMenusList.Add(new MenusInfo() { Icon = "AllInclusive", MenuName = "线圈操作", ViewName = "CoilUC" }); | ||||
LeftMenusList.Add(new MenusInfo() { Icon = "BlurCircular", MenuName = "寄存器操作", ViewName = "RegisterUC" }); | LeftMenusList.Add(new MenusInfo() { Icon = "BlurCircular", MenuName = "寄存器操作", ViewName = "RegisterUC" }); | ||||
LeftMenusList.Add(new MenusInfo() { Icon = "Settings", MenuName = "串口设置", ViewName = "SettingsUC" }); | LeftMenusList.Add(new MenusInfo() { Icon = "Settings", MenuName = "串口设置", ViewName = "SettingsUC" }); | ||||
LeftMenusList.Add(new MenusInfo() { Icon = "Settings", MenuName = "附加功能", ViewName = "AttachUC" }); | |||||
LeftMenusList.Add(new MenusInfo() { Icon = "AddCircle", MenuName = "附加功能", ViewName = "AttachUC" }); | |||||
} | } | ||||
public void DefultNaigation() | public void DefultNaigation() | ||||
@@ -29,7 +29,7 @@ namespace ModbusTest | |||||
_serialport.StopBits = StopBits.One; | _serialport.StopBits = StopBits.One; | ||||
_serialport.ReadTimeout = 500; | _serialport.ReadTimeout = 500; | ||||
_serialport.WriteTimeout = 500; | _serialport.WriteTimeout = 500; | ||||
_serialport.Open(); | |||||
_serialport.Open(); | |||||
_mockDbContext = new Mock<ModbusDbContext>(); | _mockDbContext = new Mock<ModbusDbContext>(); | ||||
_modbusRtu = new ModbusRTU(_serialport, _mockDbContext.Object); | _modbusRtu = new ModbusRTU(_serialport, _mockDbContext.Object); | ||||
} | } | ||||
@@ -47,7 +47,6 @@ namespace ModbusTest | |||||
{ | { | ||||
var result = _modbusRtu.ReadCoil(1, 300, 5); | var result = _modbusRtu.ReadCoil(1, 300, 5); | ||||
// 创建包含5个true的预期数组 | |||||
var expected = new bool[5]; | var expected = new bool[5]; | ||||
Array.Fill(expected, false); | Array.Fill(expected, false); | ||||
CollectionAssert.AreEqual(expected, result); | CollectionAssert.AreEqual(expected, result); | ||||
@@ -57,21 +56,23 @@ namespace ModbusTest | |||||
public void ReadCoil2() | public void ReadCoil2() | ||||
{ | { | ||||
var result = _modbusRtu.ReadCoil(1, 305, 5); | var result = _modbusRtu.ReadCoil(1, 305, 5); | ||||
// 创建包含5个flase的预期数组 | |||||
var expected = new bool[5]; | var expected = new bool[5]; | ||||
Array.Fill(expected, true); | Array.Fill(expected, true); | ||||
CollectionAssert.AreEqual(expected, result); | CollectionAssert.AreEqual(expected, result); | ||||
} | } | ||||
/// <summary> | |||||
/// 测试写入线圈 | |||||
/// </summary> | |||||
[Test] | [Test] | ||||
public void WriteCoil1() | public void WriteCoil1() | ||||
{ | { | ||||
var data = new bool[5]; | var data = new bool[5]; | ||||
Array.Fill(data, true); | Array.Fill(data, true); | ||||
_modbusRtu.WriteCoil(1, 310, data); | _modbusRtu.WriteCoil(1, 310, data); | ||||
// 创建包含5个true的预期数组 | |||||
var expected = _modbusRtu.ReadCoil(1, 310, 5); | var expected = _modbusRtu.ReadCoil(1, 310, 5); | ||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
@@ -85,13 +86,15 @@ namespace ModbusTest | |||||
var data = new bool[5]; | var data = new bool[5]; | ||||
Array.Fill(data, false); | Array.Fill(data, false); | ||||
_modbusRtu.WriteCoil(1, 315, data); | _modbusRtu.WriteCoil(1, 315, data); | ||||
// 创建包含5个flase的预期数组 | |||||
var expected = _modbusRtu.ReadCoil(1, 315, 5); | var expected = _modbusRtu.ReadCoil(1, 315, 5); | ||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
/// <summary> | |||||
/// 测试读取寄存器 | |||||
/// </summary> | |||||
[Test] | [Test] | ||||
public void ReadRegister1() | public void ReadRegister1() | ||||
{ | { | ||||
@@ -108,13 +111,15 @@ namespace ModbusTest | |||||
{ | { | ||||
ushort[] data = new ushort[5]; | ushort[] data = new ushort[5]; | ||||
Array.Fill(data, (ushort)1); | Array.Fill(data, (ushort)1); | ||||
var expected = _modbusRtu.ReadRegisters(1, 305, 5); | var expected = _modbusRtu.ReadRegisters(1, 305, 5); | ||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
/// <summary> | |||||
/// 测试写入寄存器 | |||||
/// </summary> | |||||
[Test] | [Test] | ||||
public void WriteRegister1() | public void WriteRegister1() | ||||
{ | { | ||||
@@ -137,5 +142,59 @@ namespace ModbusTest | |||||
var expected = _modbusRtu.ReadRegisters(1, 315, 5); | var expected = _modbusRtu.ReadRegisters(1, 315, 5); | ||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
/// <summary> | |||||
/// 解析线圈的返回值 | |||||
/// </summary> | |||||
[Test] | |||||
public void ParseCoilresponse1() | |||||
{ | |||||
var data = new bool[5]; | |||||
Array.Fill(data, true); | |||||
byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x7f, 0x10, 0x68 }; | |||||
bool[] bools = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5); | |||||
CollectionAssert.AreEqual(data, bools); | |||||
} | |||||
[Test] | |||||
public void ParseCoilresponse2() | |||||
{ | |||||
var data = new bool[5]; | |||||
Array.Fill(data, false); | |||||
byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x00, 0x51, 0x88 }; | |||||
bool[] result = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5); | |||||
CollectionAssert.AreEqual(data, result); | |||||
} | |||||
/// <summary> | |||||
/// 解析寄存器的返回值 | |||||
/// </summary> | |||||
[Test] | |||||
public void ParseRegistersresponse1() | |||||
{ | |||||
ushort[] data = new ushort[5]; | |||||
Array.Fill(data, (ushort)1); | |||||
byte[] response = new byte[] {0x01, 0x03, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x00, | |||||
0x01, 0x00, 0x01, 0x00, 0x01, 0x94, 0x26 }; | |||||
ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] {},response, 5); | |||||
CollectionAssert.AreEqual(data, result); | |||||
} | |||||
[Test] | |||||
public void ParseRegistersresponse2() | |||||
{ | |||||
ushort[] data = new ushort[2]; | |||||
Array.Fill(data, (ushort)10); | |||||
byte[] response = new byte[] {0x01, 0x03, 0x04, 0x00,0x0a, 0x00, 0x0a, 0x5a, 0x36 }; | |||||
ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] { }, response, 2); | |||||
CollectionAssert.AreEqual(data, result); | |||||
} | |||||
} | } | ||||
} | } |