浏览代码

重构Modbus和功能修改

dev-2
永攀 张 1周前
父节点
当前提交
b797ed4caf
共有 15 个文件被更改,包括 439 次插入387 次删除
  1. +1
    -1
      ModbusDemo/App.xaml.cs
  2. +4
    -4
      ModbusDemo/Device/IModbusRTU.cs
  3. +94
    -55
      ModbusDemo/Device/ModbusRTU.cs
  4. +8
    -1
      ModbusDemo/Extension/EnumBindingSourceExtension.cs
  5. +2
    -0
      ModbusDemo/ModbusDemo.csproj
  6. +6
    -1
      ModbusDemo/Model/ModbusDbContext.cs
  7. +82
    -0
      ModbusDemo/Uitls/CheckInputUitl.cs
  8. +27
    -0
      ModbusDemo/Uitls/DatabaseHelper.cs
  9. +30
    -3
      ModbusDemo/VIew/AttachUC.xaml
  10. +1
    -1
      ModbusDemo/VIew/CoilUC.xaml
  11. +4
    -220
      ModbusDemo/VIewModel/AttachUCViewModel.cs
  12. +60
    -45
      ModbusDemo/VIewModel/CoilUCViewModel.cs
  13. +61
    -45
      ModbusDemo/VIewModel/RegisterUCViewModel.cs
  14. +3
    -3
      ModbusDemo/VIewModel/SettingsUCViewModel.cs
  15. +56
    -8
      ModbusTest/ModbusRTUTest.cs

+ 1
- 1
ModbusDemo/App.xaml.cs 查看文件

@@ -50,7 +50,7 @@ namespace ModbusDemo
//将读线圈注册
containerRegistry.Register<ModbusRTU>();
//附加功能如何读取单数寄存器;
//containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>();
containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>();


// 1. 加载配置文件


+ 4
- 4
ModbusDemo/Device/IModbusRTU.cs 查看文件

@@ -6,17 +6,17 @@ using System.Threading.Tasks;

namespace ModbusDemo.Device
{
public interface IModbusRTU
public interface IModbusRTU:IDisposable
{
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);

}
}

+ 94
- 55
ModbusDemo/Device/ModbusRTU.cs 查看文件

@@ -5,16 +5,19 @@ using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace ModbusDemo.Device
{
public class ModbusRTU : IModbusRTU
{
// 标记是否已释放资源
private bool _disposed = false;
//串口
private SerialPort _serialPort;
//用于操作数据库
@@ -46,7 +49,7 @@ namespace ModbusDemo.Device
return resultValue;
}
//构建发送报文
byte[] sendMessage = ConstructMessage<ushort>(0x01, slaveAddress, startAddress, numberOfPoints,ProcessReadData);
byte[] sendMessage = ConstructMessage<ushort>(0x01, slaveAddress, startAddress, numberOfPoints, ProcessReadData);
//发送报文
byte[] responseMessage = SendMessage(sendMessage);
//判断发送过来的字节是否正确
@@ -72,7 +75,7 @@ namespace ModbusDemo.Device
.ToArray();
}
//将数据插入到数据库
InsertDatabase(sendMessage, responseMessage,"读线圈");
InsertDatabase(sendMessage, responseMessage, "读线圈");
return resultValue;
}

@@ -82,12 +85,12 @@ namespace ModbusDemo.Device
/// <param name="slaveAddress">从站地址</param>
/// <param name="startAddress">起始地址</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)
{
MessageBox.Show("串口没有链接,请先链接");
return;
return false;
}
//构建报文
byte[] sendMessage = ConstructMessage<bool[]>(0x0F, slaveAddress, startAddress, data, ProcessCoilData);
@@ -95,13 +98,13 @@ namespace ModbusDemo.Device
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;
if (!_serialPort.IsOpen)
@@ -115,7 +118,7 @@ namespace ModbusDemo.Device
byte[] responseMessage = SendMessage(sendMessage);

//将数据插入到数据库
InsertDatabase(sendMessage, responseMessage,"读寄存器");
InsertDatabase(sendMessage, responseMessage, "读寄存器");
//判断发送回来的数据是否正确
if (CheckResponse(responseMessage))
{
@@ -139,12 +142,12 @@ namespace ModbusDemo.Device
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></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)
{
MessageBox.Show("串口没有链接,请先链接");
return;
return false;
}
//构建报文
byte[] sendMessage = ConstructMessage<ushort[]>(0x10, slaveAddress, startAddress, data, ProcessRegisterData);
@@ -154,9 +157,9 @@ namespace ModbusDemo.Device
//将数据插入到数据库
InsertDatabase(sendMessage, responseMessage, "写寄存器");

//判断发送回来的数据是否正确
CheckResponse(responseMessage);
return CheckResponse(responseMessage);


}
@@ -190,7 +193,7 @@ namespace ModbusDemo.Device

//获取CRC校验码
byte[] getCRC = sendByteList.ToArray();
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
byte[] resultCRC = CalculateCRC(getCRC, getCRC.Length);
sendByteList.Add(resultCRC[0]);
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>
/// 将构建好的报文发送出去
@@ -274,6 +272,7 @@ namespace ModbusDemo.Device
/// <returns></returns>
public byte[] SendMessage(byte[] sendByte)
{

int maxRetries = 3; // 最大重试次数
int attempt = 0;
byte[] response = null;
@@ -281,48 +280,49 @@ namespace ModbusDemo.Device

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;
}
@@ -517,5 +517,44 @@ namespace ModbusDemo.Device
return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) &&
(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; // 标记为已释放
}

}
}

+ 8
- 1
ModbusDemo/Extension/EnumBindingSourceExtension.cs 查看文件

@@ -13,6 +13,7 @@ namespace ModbusDemo.Extension
/// </summary>
class EnumBindingSourceExtension : MarkupExtension
{
//枚举字段
private Type _enumType;

public Type EnumType
@@ -40,7 +41,13 @@ namespace ModbusDemo.Extension
{
EnumType = enumType;
}

/// <summary>
/// 将枚举转换为数组,返回出去
/// 可以绑定可空枚举
/// </summary>
/// <param name="serviceProvider">xmal上下文</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_enumType == null)


+ 2
- 0
ModbusDemo/ModbusDemo.csproj 查看文件

@@ -14,6 +14,8 @@
<Compile Remove="Device\SerialPortAdapter.cs" />
<Compile Remove="ErrorCode\ErrorCodeExtensions.cs" />
<Compile Remove="ErrorCode\ErrorInfoAttribute.cs" />
<Compile Remove="Uitls\CheckData.cs" />
<Compile Remove="Uitls\CRCUitl.cs" />
</ItemGroup>

<ItemGroup>


+ 6
- 1
ModbusDemo/Model/ModbusDbContext.cs 查看文件

@@ -12,7 +12,12 @@ namespace ModbusDemo.Model
/// </summary>
public class ModbusDbContext:DbContext
{

public ModbusDbContext()
{
}

public ModbusDbContext(DbContextOptions<ModbusDbContext> options) :base(options)
{


+ 82
- 0
ModbusDemo/Uitls/CheckInputUitl.cs 查看文件

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

+ 27
- 0
ModbusDemo/Uitls/DatabaseHelper.cs 查看文件

@@ -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(); // 执行查询
//}
}
}

+ 30
- 3
ModbusDemo/VIew/AttachUC.xaml 查看文件

@@ -12,15 +12,22 @@
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="0,5" />
<Setter Property="FontSize" Value="14" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80">
</RowDefinition>
<RowDefinition>
</RowDefinition>
<RowDefinition Height="80">
</RowDefinition>
<RowDefinition>
</RowDefinition>

</Grid.RowDefinitions>

<Button
@@ -33,8 +40,28 @@
</Button>
<TextBox Grid.Row="1" Margin="5" Text="{Binding ReadResult}" Style="{StaticResource MaterialDesignOutlinedTextBox}">
</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>
</UserControl>

+ 1
- 1
ModbusDemo/VIew/CoilUC.xaml 查看文件

@@ -143,7 +143,7 @@
Margin="0,-50,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="1代表Ture,0代表flase">
Text="1为true,0为flase">
</TextBlock>




+ 4
- 220
ModbusDemo/VIewModel/AttachUCViewModel.cs 查看文件

@@ -17,7 +17,7 @@ namespace ModbusDemo.VIewModel
{

private SerialPort _serialPort;
public DelegateCommand ReadOddRegisterCmm { get; set; }
public DelegateCommand WriteMoreRegisterCmm { get; set; }
//读取的结果
private string _readResult;

@@ -30,18 +30,7 @@ namespace ModbusDemo.VIewModel
RaisePropertyChanged();
}
}
//显示操作的时间
private int _time;

public int Time
{
get { return _time; }
set
{
_time = value;
RaisePropertyChanged();
}
}


public AttachUCViewModel()
@@ -50,219 +39,14 @@ namespace ModbusDemo.VIewModel
}
public AttachUCViewModel(SerialPort serialPort)
{
//ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister);
WriteMoreRegisterCmm = new DelegateCommand(WriteMoreRegister);
_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;
}




}

}

}

+ 60
- 45
ModbusDemo/VIewModel/CoilUCViewModel.cs 查看文件

@@ -18,9 +18,10 @@ namespace ModbusDemo.VIewModel
/// <summary>
/// 这是CoilUC的VM用来支持数据绑定
/// </summary>
public class CoilUCViewModel : BindableBase
public class CoilUCViewModel : BindableBase,IDisposable
{

// 标记是否已释放资源
private bool _disposed = false;
//事件聚合器
private readonly IEventAggregator _eventAggregator;
//定义数据库操作
@@ -205,33 +206,18 @@ namespace ModbusDemo.VIewModel
/// </summary>
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;
}

//使用子线程,防止ui线程卡顿
Task.Run(() =>
{
bool[] result = _modbusRTU.ReadCoil(slaveAddressValue, startAddressValue, numberOfPointsValue);
IsEnable = false;
bool[] result = _modbusRTU.ReadCoil(validation.slaveAddress, validation.startAddress, validation.numberOfPoints);

if (result == null)
{
@@ -240,7 +226,6 @@ namespace ModbusDemo.VIewModel
IsEnable = true;
return;
}

string temp = "";
int count = 0;
foreach (var item in result)
@@ -266,24 +251,10 @@ namespace ModbusDemo.VIewModel

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;
}
//去除字符串中的空格
@@ -294,19 +265,20 @@ namespace ModbusDemo.VIewModel
//使用子线程执行,避免ui线程卡顿
Task.Run(() =>
{
_modbusRTU.WriteCoil(writeSlaveAddressValue, WriteStartAddressValue, data);
IsEnable = false;
bool issucceed = _modbusRTU.WriteCoil(validation.slaveAddress, validation.startAddress, data);
ModbusLogList = GetOperateCoil();
ModbusLog modbusLog = ModbusLogList.FirstOrDefault();
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] !='8')
if (issucceed)
{
MessageBox.Show("写入成功");
IsEnable = true;
}
else
{
MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error);
IsEnable = true;
}
IsEnable = true;
});

}
@@ -323,5 +295,48 @@ namespace ModbusDemo.VIewModel
.Take(30) // 只取前20条记录
.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; // 标记为已释放
}
}
}

+ 61
- 45
ModbusDemo/VIewModel/RegisterUCViewModel.cs 查看文件

@@ -1,6 +1,7 @@
using ModbusDemo.Device;
using ModbusDemo.Event;
using ModbusDemo.Model;
using ModbusDemo.Uitls;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
@@ -14,8 +15,10 @@ using System.Windows;

namespace ModbusDemo.VIewModel
{
class RegisterUCViewModel : BindableBase
class RegisterUCViewModel : BindableBase,IDisposable
{
// 标记是否已释放资源
private bool _disposed = false;
//事件聚合器
private readonly IEventAggregator _eventAggregator;
//定义数据库操作类
@@ -200,32 +203,16 @@ namespace ModbusDemo.VIewModel
/// </summary>
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;
}
//使用子线程进行读取,防止ui线程卡顿
Task.Run(() =>
{
ushort[] result = _modbusRTU.ReadRegisters(slaveAddressValue, startAddressValue, numberOfPointsValue);
{ IsEnable = false;
ushort[] result = _modbusRTU.ReadRegister(validation.slaveAddress, validation.startAddress, validation.numberOfPoints);
if (result == null)
{
MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error);
@@ -257,31 +244,17 @@ namespace ModbusDemo.VIewModel

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;
}
string[] parts = WriteData.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
// 3. 创建结果数组
// 创建结果数组
ushort[] data = new ushort[parts.Length];

// 4. 遍历并转换每个部分
// 遍历并转换每个部分
for (int i = 0; i < parts.Length; i++)
{
// 尝试解析为ushort
@@ -293,7 +266,7 @@ namespace ModbusDemo.VIewModel
{

MessageBox.Show($"无法将 '{parts[i]}' 转换为 ushort 类型");
IsEnable = true;
return;
}
}
@@ -301,10 +274,11 @@ namespace ModbusDemo.VIewModel
//使用子线程来去执行操作,防止ui线程卡顿
Task.Run(() =>
{
_modbusRTU.WriteRegisters(writeSlaveAddressValue, WriteStartAddressValue, data);
IsEnable = false;
bool issucceed = _modbusRTU.WriteRegister(validation.slaveAddress, validation.startAddress, data);
ModbusLogList = GetOperateRegister();
ModbusLog modbusLog = ModbusLogList.FirstOrDefault();
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] != '8')
if (issucceed)
{
MessageBox.Show("写入成功");
IsEnable = true;
@@ -332,5 +306,47 @@ namespace ModbusDemo.VIewModel
.Take(30) // 只取前20条记录
.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; // 标记为已释放
}
}
}

+ 3
- 3
ModbusDemo/VIewModel/SettingsUCViewModel.cs 查看文件

@@ -129,17 +129,17 @@ namespace ModbusDemo.VIewModel
_regionManager = regionManager;
_eventAggregator = eventAggregator;
_modbusRTU = modbusRTU;
_checkTimer = new Timer(1000); // 每秒检查一次
_checkTimer = new Timer(5000); // 每秒检查一次
_checkTimer.Elapsed += CheckPortStatus;
_checkTimer.AutoReset = true;

}
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[] response = _modbusRTU.SendMessage(bytes);
if (!isConnected || response ==null)
if (response ==null)
{
_checkTimer.Stop();
_serialPort.Close();


+ 56
- 8
ModbusTest/ModbusRTUTest.cs 查看文件

@@ -14,7 +14,7 @@ namespace ModbusTest
{
public class ModbusRTUTest
{
//设计缺陷,应该使用适配器模式重写SerialPort
private SerialPort _serialport;
private Mock<ModbusDbContext> _mockDbContext;
private ModbusRTU _modbusRtu;
@@ -38,6 +38,7 @@ namespace ModbusTest
{
// 确保在测试结束后释放资源
_serialport.Dispose();
_modbusRtu.Dispose();
}
/// <summary>
/// 测试线圈读取
@@ -100,7 +101,7 @@ namespace ModbusTest
{
ushort[] data = new ushort[5];
Array.Fill(data, (ushort)0);
var expected = _modbusRtu.ReadRegisters(1, 300, 5);
var expected = _modbusRtu.ReadRegister(1, 300, 5);
CollectionAssert.AreEqual(data, expected);
}

@@ -112,7 +113,7 @@ namespace ModbusTest
ushort[] data = new ushort[5];
Array.Fill(data, (ushort)1);

var expected = _modbusRtu.ReadRegisters(1, 305, 5);
var expected = _modbusRtu.ReadRegister(1, 305, 5);
CollectionAssert.AreEqual(data, expected);
}

@@ -126,8 +127,8 @@ namespace ModbusTest
ushort[] data = new ushort[5];
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);
}

@@ -138,11 +139,58 @@ namespace ModbusTest
ushort[] data = new ushort[5];
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);
}


[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>
@@ -197,4 +245,4 @@ namespace ModbusTest
//}

}
}

正在加载...
取消
保存