@@ -50,7 +50,7 @@ namespace ModbusDemo | |||||
//将读线圈注册 | //将读线圈注册 | ||||
containerRegistry.Register<ModbusRTU>(); | containerRegistry.Register<ModbusRTU>(); | ||||
//附加功能如何读取单数寄存器; | //附加功能如何读取单数寄存器; | ||||
//containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>(); | |||||
containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>(); | |||||
// 1. 加载配置文件 | // 1. 加载配置文件 | ||||
@@ -6,17 +6,17 @@ using System.Threading.Tasks; | |||||
namespace ModbusDemo.Device | namespace ModbusDemo.Device | ||||
{ | { | ||||
public interface IModbusRTU | |||||
public interface IModbusRTU:IDisposable | |||||
{ | { | ||||
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints); | public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints); | ||||
public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data); | |||||
public bool WriteCoil(byte slaveAddress, ushort startAddress, bool[] data); | |||||
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints); | |||||
public ushort[] ReadRegister(byte slaveAddress, ushort startAddress, ushort numberOfPoints); | |||||
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data); | |||||
public bool WriteRegister(byte slaveAddress, ushort startAddress, ushort[] data); | |||||
} | } | ||||
} | } |
@@ -5,16 +5,19 @@ using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO.Ports; | using System.IO.Ports; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Runtime.InteropServices; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using System.Windows; | using System.Windows; | ||||
using static System.Runtime.InteropServices.JavaScript.JSType; | |||||
namespace ModbusDemo.Device | namespace ModbusDemo.Device | ||||
{ | { | ||||
public class ModbusRTU : IModbusRTU | public class ModbusRTU : IModbusRTU | ||||
{ | { | ||||
// 标记是否已释放资源 | |||||
private bool _disposed = false; | |||||
//串口 | //串口 | ||||
private SerialPort _serialPort; | private SerialPort _serialPort; | ||||
//用于操作数据库 | //用于操作数据库 | ||||
@@ -46,7 +49,7 @@ namespace ModbusDemo.Device | |||||
return resultValue; | return resultValue; | ||||
} | } | ||||
//构建发送报文 | //构建发送报文 | ||||
byte[] sendMessage = ConstructMessage<ushort>(0x01, slaveAddress, startAddress, numberOfPoints,ProcessReadData); | |||||
byte[] sendMessage = ConstructMessage<ushort>(0x01, slaveAddress, startAddress, numberOfPoints, ProcessReadData); | |||||
//发送报文 | //发送报文 | ||||
byte[] responseMessage = SendMessage(sendMessage); | byte[] responseMessage = SendMessage(sendMessage); | ||||
//判断发送过来的字节是否正确 | //判断发送过来的字节是否正确 | ||||
@@ -72,7 +75,7 @@ namespace ModbusDemo.Device | |||||
.ToArray(); | .ToArray(); | ||||
} | } | ||||
//将数据插入到数据库 | //将数据插入到数据库 | ||||
InsertDatabase(sendMessage, responseMessage,"读线圈"); | |||||
InsertDatabase(sendMessage, responseMessage, "读线圈"); | |||||
return resultValue; | return resultValue; | ||||
} | } | ||||
@@ -82,12 +85,12 @@ namespace ModbusDemo.Device | |||||
/// <param name="slaveAddress">从站地址</param> | /// <param name="slaveAddress">从站地址</param> | ||||
/// <param name="startAddress">起始地址</param> | /// <param name="startAddress">起始地址</param> | ||||
/// <param name="data">写入的数据</param> | /// <param name="data">写入的数据</param> | ||||
public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data) | |||||
public bool WriteCoil(byte slaveAddress, ushort startAddress, bool[] data) | |||||
{ | { | ||||
if (!_serialPort.IsOpen) | if (!_serialPort.IsOpen) | ||||
{ | { | ||||
MessageBox.Show("串口没有链接,请先链接"); | MessageBox.Show("串口没有链接,请先链接"); | ||||
return; | |||||
return false; | |||||
} | } | ||||
//构建报文 | //构建报文 | ||||
byte[] sendMessage = ConstructMessage<bool[]>(0x0F, slaveAddress, startAddress, data, ProcessCoilData); | byte[] sendMessage = ConstructMessage<bool[]>(0x0F, slaveAddress, startAddress, data, ProcessCoilData); | ||||
@@ -95,13 +98,13 @@ namespace ModbusDemo.Device | |||||
byte[] responseMessage = SendMessage(sendMessage); | byte[] responseMessage = SendMessage(sendMessage); | ||||
//将数据插入到数据库 | //将数据插入到数据库 | ||||
InsertDatabase(sendMessage, responseMessage,"写线圈"); | |||||
InsertDatabase(sendMessage, responseMessage, "写线圈"); | |||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
CheckResponse(responseMessage); | |||||
return CheckResponse(responseMessage); | |||||
} | } | ||||
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | |||||
public ushort[] ReadRegister(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | |||||
{ | { | ||||
ushort[] resultValue = null; | ushort[] resultValue = null; | ||||
if (!_serialPort.IsOpen) | if (!_serialPort.IsOpen) | ||||
@@ -115,7 +118,7 @@ namespace ModbusDemo.Device | |||||
byte[] responseMessage = SendMessage(sendMessage); | byte[] responseMessage = SendMessage(sendMessage); | ||||
//将数据插入到数据库 | //将数据插入到数据库 | ||||
InsertDatabase(sendMessage, responseMessage,"读寄存器"); | |||||
InsertDatabase(sendMessage, responseMessage, "读寄存器"); | |||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
if (CheckResponse(responseMessage)) | if (CheckResponse(responseMessage)) | ||||
{ | { | ||||
@@ -139,12 +142,12 @@ namespace ModbusDemo.Device | |||||
/// <param name="slaveAddress"></param> | /// <param name="slaveAddress"></param> | ||||
/// <param name="startAddress"></param> | /// <param name="startAddress"></param> | ||||
/// <param name="data"></param> | /// <param name="data"></param> | ||||
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) | |||||
public bool WriteRegister(byte slaveAddress, ushort startAddress, ushort[] data) | |||||
{ | { | ||||
if (!_serialPort.IsOpen) | if (!_serialPort.IsOpen) | ||||
{ | { | ||||
MessageBox.Show("串口没有链接,请先链接"); | MessageBox.Show("串口没有链接,请先链接"); | ||||
return; | |||||
return false; | |||||
} | } | ||||
//构建报文 | //构建报文 | ||||
byte[] sendMessage = ConstructMessage<ushort[]>(0x10, slaveAddress, startAddress, data, ProcessRegisterData); | byte[] sendMessage = ConstructMessage<ushort[]>(0x10, slaveAddress, startAddress, data, ProcessRegisterData); | ||||
@@ -154,9 +157,9 @@ namespace ModbusDemo.Device | |||||
//将数据插入到数据库 | //将数据插入到数据库 | ||||
InsertDatabase(sendMessage, responseMessage, "写寄存器"); | InsertDatabase(sendMessage, responseMessage, "写寄存器"); | ||||
//判断发送回来的数据是否正确 | //判断发送回来的数据是否正确 | ||||
CheckResponse(responseMessage); | |||||
return CheckResponse(responseMessage); | |||||
} | } | ||||
@@ -190,7 +193,7 @@ namespace ModbusDemo.Device | |||||
//获取CRC校验码 | //获取CRC校验码 | ||||
byte[] getCRC = sendByteList.ToArray(); | byte[] getCRC = sendByteList.ToArray(); | ||||
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length); | |||||
byte[] resultCRC = CalculateCRC(getCRC, getCRC.Length); | |||||
sendByteList.Add(resultCRC[0]); | sendByteList.Add(resultCRC[0]); | ||||
sendByteList.Add(resultCRC[1]); | sendByteList.Add(resultCRC[1]); | ||||
@@ -259,13 +262,8 @@ namespace ModbusDemo.Device | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// 读寄存器操作 | |||||
/// </summary> | |||||
/// <param name="slaveAddress"></param> | |||||
/// <param name="startAddress"></param> | |||||
/// <param name="numberOfPoints"></param> | |||||
/// <returns></returns> | |||||
// 自定义锁 | |||||
private static readonly object _Lock = new object(); | |||||
/// <summary> | /// <summary> | ||||
/// 将构建好的报文发送出去 | /// 将构建好的报文发送出去 | ||||
@@ -274,6 +272,7 @@ namespace ModbusDemo.Device | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] SendMessage(byte[] sendByte) | public byte[] SendMessage(byte[] sendByte) | ||||
{ | { | ||||
int maxRetries = 3; // 最大重试次数 | int maxRetries = 3; // 最大重试次数 | ||||
int attempt = 0; | int attempt = 0; | ||||
byte[] response = null; | byte[] response = null; | ||||
@@ -281,48 +280,49 @@ namespace ModbusDemo.Device | |||||
while (attempt < maxRetries) | while (attempt < maxRetries) | ||||
{ | { | ||||
if (_serialPort.IsOpen) | |||||
lock (_Lock) | |||||
{ | { | ||||
// 每次重试前清空缓冲区 | |||||
responseData.Clear(); | |||||
// 清除输入缓冲区残留数据 | |||||
_serialPort.DiscardInBuffer(); | |||||
} | |||||
try | |||||
{ | |||||
_serialPort.Write(sendByte, 0, sendByte.Length); | |||||
//使用时间去轮询查询数据 | |||||
DateTime startTime = DateTime.Now; | |||||
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) | |||||
if (_serialPort.IsOpen) | |||||
{ | |||||
// 每次重试前清空缓冲区 | |||||
responseData.Clear(); | |||||
// 清除输入缓冲区残留数据 | |||||
_serialPort.DiscardInBuffer(); | |||||
} | |||||
try | |||||
{ | { | ||||
int bytesToRead = _serialPort.BytesToRead; | |||||
if (bytesToRead > 0) | |||||
_serialPort.Write(sendByte, 0, sendByte.Length); | |||||
//使用时间去轮询查询数据 | |||||
DateTime startTime = DateTime.Now; | |||||
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) | |||||
{ | { | ||||
byte[] buffer = new byte[bytesToRead]; | |||||
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); | |||||
responseData.AddRange(buffer.Take(bytesRead)); | |||||
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).Wait(); | |||||
} | } | ||||
//延迟20毫秒,节约cpu资源 | |||||
Thread.Sleep(20); | |||||
response = responseData.ToArray(); | |||||
if (response.Length == 0) | |||||
{ | |||||
attempt++; | |||||
continue; | |||||
} | |||||
return response; | |||||
} | } | ||||
response = responseData.ToArray(); | |||||
if (response.Length == 0) | |||||
catch (Exception) | |||||
{ | { | ||||
attempt++; | |||||
continue; | |||||
} | } | ||||
return response; | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
finally | |||||
{ | |||||
attempt++; | |||||
} | |||||
} | } | ||||
finally | |||||
{ | |||||
attempt++; | |||||
} | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
@@ -517,5 +517,44 @@ namespace ModbusDemo.Device | |||||
return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) && | return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) && | ||||
(calculatedCrc[calculatedCrc.Length - 2] == receivedCrcLo); | (calculatedCrc[calculatedCrc.Length - 2] == receivedCrcLo); | ||||
} | } | ||||
/// <summary> | |||||
/// 释放资源 | |||||
/// </summary> | |||||
public void Dispose() | |||||
{ | |||||
Dispose(true); | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
/// <summary> | |||||
/// 真实调用的方法 | |||||
/// </summary> | |||||
/// <param name="disposing"></param> | |||||
protected virtual void Dispose(bool disposing) | |||||
{ | |||||
if (_disposed) | |||||
return; // 避免重复释放 | |||||
// 释放托管资源(仅在手动调用Dispose时执行) | |||||
if (disposing) | |||||
{ | |||||
//_serialPort是单例的,不要手动释放 | |||||
// 释放数据库上下文是瞬态的需要手动释放 | |||||
if (_modbusDbContext != null) | |||||
{ | |||||
_modbusDbContext.Dispose(); | |||||
_modbusDbContext = null; | |||||
} | |||||
} | |||||
_disposed = true; // 标记为已释放 | |||||
} | |||||
} | } | ||||
} | } |
@@ -13,6 +13,7 @@ namespace ModbusDemo.Extension | |||||
/// </summary> | /// </summary> | ||||
class EnumBindingSourceExtension : MarkupExtension | class EnumBindingSourceExtension : MarkupExtension | ||||
{ | { | ||||
//枚举字段 | |||||
private Type _enumType; | private Type _enumType; | ||||
public Type EnumType | public Type EnumType | ||||
@@ -40,7 +41,13 @@ namespace ModbusDemo.Extension | |||||
{ | { | ||||
EnumType = enumType; | EnumType = enumType; | ||||
} | } | ||||
/// <summary> | |||||
/// 将枚举转换为数组,返回出去 | |||||
/// 可以绑定可空枚举 | |||||
/// </summary> | |||||
/// <param name="serviceProvider">xmal上下文</param> | |||||
/// <returns></returns> | |||||
/// <exception cref="InvalidOperationException"></exception> | |||||
public override object ProvideValue(IServiceProvider serviceProvider) | public override object ProvideValue(IServiceProvider serviceProvider) | ||||
{ | { | ||||
if (_enumType == null) | if (_enumType == null) | ||||
@@ -14,6 +14,8 @@ | |||||
<Compile Remove="Device\SerialPortAdapter.cs" /> | <Compile Remove="Device\SerialPortAdapter.cs" /> | ||||
<Compile Remove="ErrorCode\ErrorCodeExtensions.cs" /> | <Compile Remove="ErrorCode\ErrorCodeExtensions.cs" /> | ||||
<Compile Remove="ErrorCode\ErrorInfoAttribute.cs" /> | <Compile Remove="ErrorCode\ErrorInfoAttribute.cs" /> | ||||
<Compile Remove="Uitls\CheckData.cs" /> | |||||
<Compile Remove="Uitls\CRCUitl.cs" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -12,7 +12,12 @@ namespace ModbusDemo.Model | |||||
/// </summary> | /// </summary> | ||||
public class ModbusDbContext:DbContext | public class ModbusDbContext:DbContext | ||||
{ | { | ||||
public ModbusDbContext() | |||||
{ | |||||
} | |||||
public ModbusDbContext(DbContextOptions<ModbusDbContext> options) :base(options) | public ModbusDbContext(DbContextOptions<ModbusDbContext> options) :base(options) | ||||
{ | { | ||||
@@ -0,0 +1,82 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using System.Windows; | |||||
namespace ModbusDemo.Uitls | |||||
{ | |||||
public class CheckInputUitl | |||||
{ | |||||
/// <summary> | |||||
/// 检测输入的数据是否合理 | |||||
/// </summary> | |||||
/// <param name="slaveAddressStr"></param> | |||||
/// <param name="startAddressStr"></param> | |||||
/// <param name="numberOfPointsStr"></param> | |||||
/// <returns></returns> | |||||
public static (bool success, byte slaveAddress, ushort startAddress, ushort numberOfPoints) ValidateReadParameters( | |||||
string slaveAddressStr, string startAddressStr, string numberOfPointsStr) | |||||
{ | |||||
if (!byte.TryParse(slaveAddressStr, out byte slaveAddress)) | |||||
{ | |||||
ShowMessage("SlaveAddress 格式无效"); | |||||
return (false, 0, 0, 0); | |||||
} | |||||
if (!ushort.TryParse(startAddressStr, out ushort startAddress)) | |||||
{ | |||||
ShowMessage("StartAddress 格式无效"); | |||||
return (false, 0, 0, 0); | |||||
} | |||||
if (!ushort.TryParse(numberOfPointsStr, out ushort numberOfPoints)) | |||||
{ | |||||
ShowMessage("NumberOfPoints 格式无效"); | |||||
return (false, 0, 0, 0); | |||||
} | |||||
return (true, slaveAddress, startAddress, numberOfPoints); | |||||
} | |||||
/// <summary> | |||||
/// 检测写的数据是否合理 | |||||
/// </summary> | |||||
/// <param name="slaveAddressStr"></param> | |||||
/// <param name="startAddressStr"></param> | |||||
/// <param name="WriteData"></param> | |||||
/// <returns></returns> | |||||
public static (bool success, byte slaveAddress, ushort startAddress) ValidateWriteParameters( | |||||
string slaveAddressStr, string startAddressStr, string WriteData) | |||||
{ | |||||
if (!byte.TryParse(slaveAddressStr, out byte slaveAddress)) | |||||
{ | |||||
ShowMessage("SlaveAddress 格式无效"); | |||||
return (false, 0, 0); | |||||
} | |||||
if (!ushort.TryParse(startAddressStr, out ushort startAddress)) | |||||
{ | |||||
ShowMessage("StartAddress 格式无效"); | |||||
return (false, 0, 0); | |||||
} | |||||
if (string.IsNullOrEmpty(WriteData)) | |||||
{ | |||||
ShowMessage("WriteData 格式无效"); | |||||
return (false, 0, 0); | |||||
} | |||||
return (true, slaveAddress, startAddress); | |||||
} | |||||
private static void ShowMessage(string message, string title = "warn", | |||||
MessageBoxImage image = MessageBoxImage.Warning) | |||||
{ | |||||
MessageBox.Show(message, title, MessageBoxButton.OK, image); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
using ModbusDemo.Model; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace ModbusDemo.Uitls | |||||
{ | |||||
public class DatabaseHelper | |||||
{ | |||||
//private readonly ModbusDbContext _modbusDbContext; | |||||
//public DatabaseHelper(ModbusDbContext modbusDbContext) | |||||
//{ | |||||
// _modbusDbContext = modbusDbContext; | |||||
//} | |||||
//public static List<ModbusLog> GetOperateCoil() | |||||
//{ | |||||
// return _modbusDbContext.ModbusLog | |||||
// .Where(log => log.OperationType == "读线圈" || log.OperationType == "写线圈") | |||||
// .OrderByDescending(log => log.Time) // 按时间倒序,最新的在前 | |||||
// .Take(30) // 只取前30条记录 | |||||
// .ToList(); // 执行查询 | |||||
//} | |||||
} | |||||
} |
@@ -12,15 +12,22 @@ | |||||
d:DesignHeight="450" | d:DesignHeight="450" | ||||
d:DesignWidth="800" | d:DesignWidth="800" | ||||
mc:Ignorable="d"> | mc:Ignorable="d"> | ||||
<UserControl.Resources> | |||||
<Style TargetType="TextBox"> | |||||
<Setter Property="Margin" Value="0,5" /> | |||||
<Setter Property="FontSize" Value="14" /> | |||||
</Style> | |||||
</UserControl.Resources> | |||||
<Grid> | <Grid> | ||||
<Grid.RowDefinitions> | <Grid.RowDefinitions> | ||||
<RowDefinition Height="80"> | <RowDefinition Height="80"> | ||||
</RowDefinition> | </RowDefinition> | ||||
<RowDefinition> | <RowDefinition> | ||||
</RowDefinition> | </RowDefinition> | ||||
<RowDefinition Height="80"> | |||||
</RowDefinition> | |||||
<RowDefinition> | <RowDefinition> | ||||
</RowDefinition> | </RowDefinition> | ||||
</Grid.RowDefinitions> | </Grid.RowDefinitions> | ||||
<Button | <Button | ||||
@@ -33,8 +40,28 @@ | |||||
</Button> | </Button> | ||||
<TextBox Grid.Row="1" Margin="5" Text="{Binding ReadResult}" Style="{StaticResource MaterialDesignOutlinedTextBox}"> | <TextBox Grid.Row="1" Margin="5" Text="{Binding ReadResult}" Style="{StaticResource MaterialDesignOutlinedTextBox}"> | ||||
</TextBox> | </TextBox> | ||||
<TextBlock Grid.Row="2" Text="{Binding Time}" FontSize="16"> | |||||
</TextBlock> | |||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center"> | |||||
<TextBlock Margin="10,30,10,0" Text="写入位数:"> | |||||
</TextBlock> | |||||
<TextBox MinWidth="80" Height="20"> | |||||
</TextBox> | |||||
<TextBlock Margin="10,30,10,0" Text="写入数据:"> | |||||
</TextBlock> | |||||
<TextBox MinWidth="80" Height="20" Margin="0 0 20 0"> | |||||
</TextBox> | |||||
<Button | |||||
Grid.Row="2" | |||||
Width="80" | |||||
Height="40" | |||||
materialDesign:ButtonAssist.CornerRadius="20" | |||||
Command="{Binding WriteMoreRegisterCmm}" | |||||
Content="读取" | |||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | |||||
</Button> | |||||
</StackPanel> | |||||
<TextBox Grid.Row="3" Margin="5" Text="{Binding ReadResult}" Style="{StaticResource MaterialDesignOutlinedTextBox}"> | |||||
</TextBox> | |||||
</Grid> | </Grid> | ||||
</UserControl> | </UserControl> |
@@ -143,7 +143,7 @@ | |||||
Margin="0,-50,0,0" | Margin="0,-50,0,0" | ||||
HorizontalAlignment="Center" | HorizontalAlignment="Center" | ||||
VerticalAlignment="Center" | VerticalAlignment="Center" | ||||
Text="1代表Ture,0代表flase"> | |||||
Text="1为true,0为flase"> | |||||
</TextBlock> | </TextBlock> | ||||
@@ -17,7 +17,7 @@ namespace ModbusDemo.VIewModel | |||||
{ | { | ||||
private SerialPort _serialPort; | private SerialPort _serialPort; | ||||
public DelegateCommand ReadOddRegisterCmm { get; set; } | |||||
public DelegateCommand WriteMoreRegisterCmm { get; set; } | |||||
//读取的结果 | //读取的结果 | ||||
private string _readResult; | private string _readResult; | ||||
@@ -30,18 +30,7 @@ namespace ModbusDemo.VIewModel | |||||
RaisePropertyChanged(); | RaisePropertyChanged(); | ||||
} | } | ||||
} | } | ||||
//显示操作的时间 | |||||
private int _time; | |||||
public int Time | |||||
{ | |||||
get { return _time; } | |||||
set | |||||
{ | |||||
_time = value; | |||||
RaisePropertyChanged(); | |||||
} | |||||
} | |||||
public AttachUCViewModel() | public AttachUCViewModel() | ||||
@@ -50,219 +39,14 @@ namespace ModbusDemo.VIewModel | |||||
} | } | ||||
public AttachUCViewModel(SerialPort serialPort) | public AttachUCViewModel(SerialPort serialPort) | ||||
{ | { | ||||
//ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); | |||||
WriteMoreRegisterCmm = new DelegateCommand(WriteMoreRegister); | |||||
_serialPort = serialPort; | _serialPort = serialPort; | ||||
} | } | ||||
/// <summary> | |||||
/// 读1000个寄存器 | |||||
/// </summary> | |||||
//private void ReadOddRegister() | |||||
//{ | |||||
// // 创建 Modbus RTU 主站 | |||||
// IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(_serialPort); | |||||
// 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(() => | |||||
// { | |||||
// try | |||||
// { | |||||
// ushort[] registers = master.ReadHoldingRegisters(slaveId, startAddress, currentChunkSize); | |||||
// Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); | |||||
// } | |||||
// catch (Exception ex) | |||||
// { | |||||
// MessageBox.Show($"线程 {chunkIndex} 读取寄存器时出错: {ex.Message}"); | |||||
// } | |||||
// }); | |||||
// } | |||||
// // 等待所有任务完成 | |||||
// 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(); | |||||
//} | |||||
/// <summary> | |||||
/// 自己的类 | |||||
/// </summary> | |||||
private void ReadOddRegister2() | |||||
private void WriteMoreRegister() | |||||
{ | { | ||||
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; | |||||
ushort[] registers = ReadRegisters(slaveId, startAddress, currentChunkSize); | |||||
Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); | |||||
} | |||||
// 等待所有任务完成 | |||||
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) | |||||
{ | |||||
ushort[] resultValue = null; | |||||
if (!_serialPort.IsOpen) | |||||
{ | |||||
MessageBox.Show("串口没有链接,请先链接"); | |||||
return resultValue; | |||||
} | |||||
var sendByteList = new List<byte>(8) | |||||
{ | |||||
slaveAddress, // 从站地址 | |||||
0x03, // 功能码:读保持寄存器 | |||||
}; | |||||
// 添加起始地址(高字节在前) | |||||
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); | |||||
// 添加读取点数(高字节在前) | |||||
sendByteList.AddRange(BitConverter.GetBytes(numberOfPoints).Reverse()); | |||||
//获取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; | |||||
while (attempt < maxRetries) | |||||
{ | |||||
// 每次重试前清空缓冲区 | |||||
responseData.Clear(); | |||||
// 清除输入缓冲区残留数据 | |||||
_serialPort.DiscardInBuffer(); | |||||
try | |||||
{ | |||||
_serialPort.Write(sendByte, 0, sendByte.Length); | |||||
//使用时间去轮询查询数据 | |||||
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资源 | |||||
Thread.Sleep(20); | |||||
} | |||||
response = responseData.ToArray(); | |||||
if (response.Length == 0) | |||||
{ | |||||
continue; | |||||
} | |||||
resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints); | |||||
return resultValue; | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
} | |||||
finally | |||||
{ | |||||
attempt++; | |||||
} | |||||
} | |||||
return resultValue; | |||||
} | |||||
/// <summary> | |||||
/// 解析读取到的操作 | |||||
/// </summary> | |||||
/// <param name="sendByte"></param> | |||||
/// <param name="response"></param> | |||||
/// <param name="numberOfPoints"></param> | |||||
/// <returns></returns> | |||||
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; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -18,9 +18,10 @@ namespace ModbusDemo.VIewModel | |||||
/// <summary> | /// <summary> | ||||
/// 这是CoilUC的VM用来支持数据绑定 | /// 这是CoilUC的VM用来支持数据绑定 | ||||
/// </summary> | /// </summary> | ||||
public class CoilUCViewModel : BindableBase | |||||
public class CoilUCViewModel : BindableBase,IDisposable | |||||
{ | { | ||||
// 标记是否已释放资源 | |||||
private bool _disposed = false; | |||||
//事件聚合器 | //事件聚合器 | ||||
private readonly IEventAggregator _eventAggregator; | private readonly IEventAggregator _eventAggregator; | ||||
//定义数据库操作 | //定义数据库操作 | ||||
@@ -205,33 +206,18 @@ namespace ModbusDemo.VIewModel | |||||
/// </summary> | /// </summary> | ||||
private void ReadCoil() | private void ReadCoil() | ||||
{ | { | ||||
//将按钮设置为不可点击状态,并解析用户输入的信息 | |||||
IsEnable = false; | |||||
if (!byte.TryParse(SlaveAddress, out byte slaveAddressValue)) | |||||
{ | |||||
MessageBox.Show("SlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(StartAddress, out ushort startAddressValue)) | |||||
//检测当前输入的信息是否正确 | |||||
var validation = CheckInputUitl.ValidateReadParameters(SlaveAddress, StartAddress, NumberOfPoints); | |||||
if (!validation.success) | |||||
{ | { | ||||
MessageBox.Show("StartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(NumberOfPoints, out ushort numberOfPointsValue)) | |||||
{ | |||||
MessageBox.Show("NumberOfPoints 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | return; | ||||
} | } | ||||
//使用子线程,防止ui线程卡顿 | //使用子线程,防止ui线程卡顿 | ||||
Task.Run(() => | Task.Run(() => | ||||
{ | { | ||||
bool[] result = _modbusRTU.ReadCoil(slaveAddressValue, startAddressValue, numberOfPointsValue); | |||||
IsEnable = false; | |||||
bool[] result = _modbusRTU.ReadCoil(validation.slaveAddress, validation.startAddress, validation.numberOfPoints); | |||||
if (result == null) | if (result == null) | ||||
{ | { | ||||
@@ -240,7 +226,6 @@ namespace ModbusDemo.VIewModel | |||||
IsEnable = true; | IsEnable = true; | ||||
return; | return; | ||||
} | } | ||||
string temp = ""; | string temp = ""; | ||||
int count = 0; | int count = 0; | ||||
foreach (var item in result) | foreach (var item in result) | ||||
@@ -266,24 +251,10 @@ namespace ModbusDemo.VIewModel | |||||
private void WriteCoil() | private void WriteCoil() | ||||
{ | { | ||||
//将按钮设置为不可点击状态,并解析用户输入的信息 | |||||
IsEnable = false; | |||||
if (!byte.TryParse(WriteSlaveAddress, out byte writeSlaveAddressValue)) | |||||
{ | |||||
MessageBox.Show("WriteSlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(WriteStartAddress, out ushort WriteStartAddressValue)) | |||||
{ | |||||
MessageBox.Show("WriteStartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (string.IsNullOrEmpty(WriteData)) | |||||
//检测当前输入的信息是否正确 | |||||
var validation = CheckInputUitl.ValidateWriteParameters(WriteSlaveAddress, WriteStartAddress,WriteData); | |||||
if (!validation.success) | |||||
{ | { | ||||
MessageBox.Show("WriteData 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | return; | ||||
} | } | ||||
//去除字符串中的空格 | //去除字符串中的空格 | ||||
@@ -294,19 +265,20 @@ namespace ModbusDemo.VIewModel | |||||
//使用子线程执行,避免ui线程卡顿 | //使用子线程执行,避免ui线程卡顿 | ||||
Task.Run(() => | Task.Run(() => | ||||
{ | { | ||||
_modbusRTU.WriteCoil(writeSlaveAddressValue, WriteStartAddressValue, data); | |||||
IsEnable = false; | |||||
bool issucceed = _modbusRTU.WriteCoil(validation.slaveAddress, validation.startAddress, data); | |||||
ModbusLogList = GetOperateCoil(); | ModbusLogList = GetOperateCoil(); | ||||
ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); | |||||
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] !='8') | |||||
if (issucceed) | |||||
{ | { | ||||
MessageBox.Show("写入成功"); | MessageBox.Show("写入成功"); | ||||
IsEnable = true; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | ||||
IsEnable = true; | |||||
} | } | ||||
IsEnable = true; | |||||
}); | }); | ||||
} | } | ||||
@@ -323,5 +295,48 @@ namespace ModbusDemo.VIewModel | |||||
.Take(30) // 只取前20条记录 | .Take(30) // 只取前20条记录 | ||||
.ToList(); // 执行查询 | .ToList(); // 执行查询 | ||||
} | } | ||||
/// <summary> | |||||
/// 释放资源 | |||||
/// </summary> | |||||
public void Dispose() | |||||
{ | |||||
Dispose(true); | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
/// <summary> | |||||
/// 真实调用的方法 | |||||
/// </summary> | |||||
/// <param name="disposing"></param> | |||||
protected virtual void Dispose(bool disposing) | |||||
{ | |||||
if (_disposed) | |||||
return; // 避免重复释放 | |||||
// 释放托管资源(仅在手动调用Dispose时执行) | |||||
if (disposing) | |||||
{ | |||||
//_serialPort是单例的,不要手动释放 | |||||
// 释放数据库上下文是瞬态的需要手动释放 | |||||
if (_modbusDbContext != null) | |||||
{ | |||||
_modbusDbContext.Dispose(); | |||||
_modbusDbContext = null; | |||||
} | |||||
if (_modbusRTU != null) | |||||
{ | |||||
_modbusRTU.Dispose(); | |||||
_modbusRTU = null; | |||||
} | |||||
} | |||||
_disposed = true; // 标记为已释放 | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,6 +1,7 @@ | |||||
using ModbusDemo.Device; | using ModbusDemo.Device; | ||||
using ModbusDemo.Event; | using ModbusDemo.Event; | ||||
using ModbusDemo.Model; | using ModbusDemo.Model; | ||||
using ModbusDemo.Uitls; | |||||
using Prism.Commands; | using Prism.Commands; | ||||
using Prism.Events; | using Prism.Events; | ||||
using Prism.Mvvm; | using Prism.Mvvm; | ||||
@@ -14,8 +15,10 @@ using System.Windows; | |||||
namespace ModbusDemo.VIewModel | namespace ModbusDemo.VIewModel | ||||
{ | { | ||||
class RegisterUCViewModel : BindableBase | |||||
class RegisterUCViewModel : BindableBase,IDisposable | |||||
{ | { | ||||
// 标记是否已释放资源 | |||||
private bool _disposed = false; | |||||
//事件聚合器 | //事件聚合器 | ||||
private readonly IEventAggregator _eventAggregator; | private readonly IEventAggregator _eventAggregator; | ||||
//定义数据库操作类 | //定义数据库操作类 | ||||
@@ -200,32 +203,16 @@ namespace ModbusDemo.VIewModel | |||||
/// </summary> | /// </summary> | ||||
private void ReadRegister() | private void ReadRegister() | ||||
{ | { | ||||
//将按钮设置为不可点击状态,并解析用户输入的信息 | |||||
IsEnable = false; | |||||
if (!byte.TryParse(SlaveAddress, out byte slaveAddressValue)) | |||||
//检测当前输入的信息是否正确 | |||||
var validation = CheckInputUitl.ValidateReadParameters(SlaveAddress, StartAddress, NumberOfPoints); | |||||
if (!validation.success) | |||||
{ | { | ||||
MessageBox.Show("SlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(StartAddress, out ushort startAddressValue)) | |||||
{ | |||||
MessageBox.Show("StartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(NumberOfPoints, out ushort numberOfPointsValue)) | |||||
{ | |||||
MessageBox.Show("NumberOfPoints 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | return; | ||||
} | } | ||||
//使用子线程进行读取,防止ui线程卡顿 | //使用子线程进行读取,防止ui线程卡顿 | ||||
Task.Run(() => | Task.Run(() => | ||||
{ | |||||
ushort[] result = _modbusRTU.ReadRegisters(slaveAddressValue, startAddressValue, numberOfPointsValue); | |||||
{ IsEnable = false; | |||||
ushort[] result = _modbusRTU.ReadRegister(validation.slaveAddress, validation.startAddress, validation.numberOfPoints); | |||||
if (result == null) | if (result == null) | ||||
{ | { | ||||
MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | ||||
@@ -257,31 +244,17 @@ namespace ModbusDemo.VIewModel | |||||
private void WriteRegister() | private void WriteRegister() | ||||
{ | { | ||||
//将按钮设置为不可点击状态,并解析用户输入的信息 | |||||
IsEnable = false; | |||||
if (!byte.TryParse(WriteSlaveAddress, out byte writeSlaveAddressValue)) | |||||
{ | |||||
MessageBox.Show("WriteSlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (!ushort.TryParse(WriteStartAddress, out ushort WriteStartAddressValue)) | |||||
//检测当前输入的信息是否正确 | |||||
var validation = CheckInputUitl.ValidateWriteParameters(WriteSlaveAddress, WriteStartAddress, WriteData); | |||||
if (!validation.success) | |||||
{ | { | ||||
MessageBox.Show("WriteStartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | |||||
} | |||||
if (string.IsNullOrEmpty(WriteData)) | |||||
{ | |||||
MessageBox.Show("WriteData 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||||
IsEnable = true; | |||||
return; | return; | ||||
} | } | ||||
string[] parts = WriteData.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); | string[] parts = WriteData.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); | ||||
// 3. 创建结果数组 | |||||
// 创建结果数组 | |||||
ushort[] data = new ushort[parts.Length]; | ushort[] data = new ushort[parts.Length]; | ||||
// 4. 遍历并转换每个部分 | |||||
// 遍历并转换每个部分 | |||||
for (int i = 0; i < parts.Length; i++) | for (int i = 0; i < parts.Length; i++) | ||||
{ | { | ||||
// 尝试解析为ushort | // 尝试解析为ushort | ||||
@@ -293,7 +266,7 @@ namespace ModbusDemo.VIewModel | |||||
{ | { | ||||
MessageBox.Show($"无法将 '{parts[i]}' 转换为 ushort 类型"); | MessageBox.Show($"无法将 '{parts[i]}' 转换为 ushort 类型"); | ||||
IsEnable = true; | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
@@ -301,10 +274,11 @@ namespace ModbusDemo.VIewModel | |||||
//使用子线程来去执行操作,防止ui线程卡顿 | //使用子线程来去执行操作,防止ui线程卡顿 | ||||
Task.Run(() => | Task.Run(() => | ||||
{ | { | ||||
_modbusRTU.WriteRegisters(writeSlaveAddressValue, WriteStartAddressValue, data); | |||||
IsEnable = false; | |||||
bool issucceed = _modbusRTU.WriteRegister(validation.slaveAddress, validation.startAddress, data); | |||||
ModbusLogList = GetOperateRegister(); | ModbusLogList = GetOperateRegister(); | ||||
ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); | |||||
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] != '8') | |||||
if (issucceed) | |||||
{ | { | ||||
MessageBox.Show("写入成功"); | MessageBox.Show("写入成功"); | ||||
IsEnable = true; | IsEnable = true; | ||||
@@ -332,5 +306,47 @@ namespace ModbusDemo.VIewModel | |||||
.Take(30) // 只取前20条记录 | .Take(30) // 只取前20条记录 | ||||
.ToList(); // 执行查询 | .ToList(); // 执行查询 | ||||
} | } | ||||
/// <summary> | |||||
/// 释放资源 | |||||
/// </summary> | |||||
public void Dispose() | |||||
{ | |||||
Dispose(true); | |||||
GC.SuppressFinalize(this); | |||||
} | |||||
/// <summary> | |||||
/// 真实调用的方法 | |||||
/// </summary> | |||||
/// <param name="disposing"></param> | |||||
protected virtual void Dispose(bool disposing) | |||||
{ | |||||
if (_disposed) | |||||
return; // 避免重复释放 | |||||
// 释放托管资源(仅在手动调用Dispose时执行) | |||||
if (disposing) | |||||
{ | |||||
//_serialPort是单例的,不要手动释放 | |||||
// 释放数据库上下文是瞬态的需要手动释放 | |||||
if (_modbusDbContext != null) | |||||
{ | |||||
_modbusDbContext.Dispose(); | |||||
_modbusDbContext = null; | |||||
} | |||||
if (_modbusRTU != null) | |||||
{ | |||||
_modbusRTU.Dispose(); | |||||
_modbusRTU = null; | |||||
} | |||||
} | |||||
_disposed = true; // 标记为已释放 | |||||
} | |||||
} | } | ||||
} | } |
@@ -129,17 +129,17 @@ namespace ModbusDemo.VIewModel | |||||
_regionManager = regionManager; | _regionManager = regionManager; | ||||
_eventAggregator = eventAggregator; | _eventAggregator = eventAggregator; | ||||
_modbusRTU = modbusRTU; | _modbusRTU = modbusRTU; | ||||
_checkTimer = new Timer(1000); // 每秒检查一次 | |||||
_checkTimer = new Timer(5000); // 每秒检查一次 | |||||
_checkTimer.Elapsed += CheckPortStatus; | _checkTimer.Elapsed += CheckPortStatus; | ||||
_checkTimer.AutoReset = true; | _checkTimer.AutoReset = true; | ||||
} | } | ||||
private void CheckPortStatus(object sender, ElapsedEventArgs e) | private void CheckPortStatus(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
bool isConnected = SerialPort.GetPortNames().Contains(PortName); | |||||
//bool isConnected = SerialPort.GetPortNames().Contains(PortName); | |||||
byte[] bytes = new byte[] { 0x01, 0x01, 0x01, 0x2C, 0x00, 0x01, 0x3D, 0xFF }; | byte[] bytes = new byte[] { 0x01, 0x01, 0x01, 0x2C, 0x00, 0x01, 0x3D, 0xFF }; | ||||
byte[] response = _modbusRTU.SendMessage(bytes); | byte[] response = _modbusRTU.SendMessage(bytes); | ||||
if (!isConnected || response ==null) | |||||
if (response ==null) | |||||
{ | { | ||||
_checkTimer.Stop(); | _checkTimer.Stop(); | ||||
_serialPort.Close(); | _serialPort.Close(); | ||||
@@ -14,7 +14,7 @@ namespace ModbusTest | |||||
{ | { | ||||
public class ModbusRTUTest | public class ModbusRTUTest | ||||
{ | { | ||||
//设计缺陷,应该使用适配器模式重写SerialPort | |||||
private SerialPort _serialport; | private SerialPort _serialport; | ||||
private Mock<ModbusDbContext> _mockDbContext; | private Mock<ModbusDbContext> _mockDbContext; | ||||
private ModbusRTU _modbusRtu; | private ModbusRTU _modbusRtu; | ||||
@@ -38,6 +38,7 @@ namespace ModbusTest | |||||
{ | { | ||||
// 确保在测试结束后释放资源 | // 确保在测试结束后释放资源 | ||||
_serialport.Dispose(); | _serialport.Dispose(); | ||||
_modbusRtu.Dispose(); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 测试线圈读取 | /// 测试线圈读取 | ||||
@@ -100,7 +101,7 @@ namespace ModbusTest | |||||
{ | { | ||||
ushort[] data = new ushort[5]; | ushort[] data = new ushort[5]; | ||||
Array.Fill(data, (ushort)0); | Array.Fill(data, (ushort)0); | ||||
var expected = _modbusRtu.ReadRegisters(1, 300, 5); | |||||
var expected = _modbusRtu.ReadRegister(1, 300, 5); | |||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
@@ -112,7 +113,7 @@ 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.ReadRegister(1, 305, 5); | |||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
@@ -126,8 +127,8 @@ namespace ModbusTest | |||||
ushort[] data = new ushort[5]; | ushort[] data = new ushort[5]; | ||||
Array.Fill(data, (ushort)1); | Array.Fill(data, (ushort)1); | ||||
_modbusRtu.WriteRegisters(1, 310, data); | |||||
var expected = _modbusRtu.ReadRegisters(1, 310, 5); | |||||
_modbusRtu.WriteRegister(1, 310, data); | |||||
var expected = _modbusRtu.ReadRegister(1, 310, 5); | |||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
@@ -138,11 +139,58 @@ namespace ModbusTest | |||||
ushort[] data = new ushort[5]; | ushort[] data = new ushort[5]; | ||||
Array.Fill(data, (ushort)2); | Array.Fill(data, (ushort)2); | ||||
_modbusRtu.WriteRegisters(1, 315, data); | |||||
var expected = _modbusRtu.ReadRegisters(1, 315, 5); | |||||
_modbusRtu.WriteRegister(1, 315, data); | |||||
var expected = _modbusRtu.ReadRegister(1, 315, 5); | |||||
CollectionAssert.AreEqual(data, expected); | CollectionAssert.AreEqual(data, expected); | ||||
} | } | ||||
[Test] | |||||
public void CRC1() | |||||
{ | |||||
byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; | |||||
int length = data.Length; | |||||
byte[] crc = ModbusRTU.CalculateCRC(data, length); | |||||
// Assert | |||||
Assert.That(crc[0], Is.EqualTo(0x77)); | |||||
Assert.That(crc[1], Is.EqualTo(0xF3)); | |||||
} | |||||
[Test] | |||||
public void CRC2() | |||||
{ | |||||
byte[] data = { 0x49, 0x66, 0x51, 0x6C, 0x7F }; | |||||
int length = data.Length; | |||||
byte[] crc = ModbusRTU.CalculateCRC(data, length); | |||||
// Assert | |||||
Assert.That(crc[0], Is.EqualTo(0xDA)); | |||||
Assert.That(crc[1], Is.EqualTo(0x77)); | |||||
} | |||||
[Test] | |||||
public void CRC3() | |||||
{ | |||||
byte[] data = { 0x01, 0x90, 0x03, 0x0C, 0x01 }; | |||||
bool isTrue = ModbusRTU.ValidateCRC(data); | |||||
// Assert | |||||
Assert.IsTrue(isTrue); | |||||
} | |||||
[Test] | |||||
public void CRC4() | |||||
{ | |||||
byte[] data = { 0x01, 0x90, 0x03, 0x0C, 0x00 }; | |||||
bool isFalse = ModbusRTU.ValidateCRC(data); | |||||
// Assert | |||||
Assert.IsFalse(isFalse); | |||||
} | |||||
} | |||||
///// <summary> | ///// <summary> | ||||
///// 解析线圈的返回值 | ///// 解析线圈的返回值 | ||||
///// </summary> | ///// </summary> | ||||
@@ -197,4 +245,4 @@ namespace ModbusTest | |||||
//} | //} | ||||
} | } | ||||
} | |||||