Browse Source

添加功能码,并添加可以选择读写耗时时间

dev-2
永攀 张 1 week ago
parent
commit
85f81b9817
10 changed files with 270 additions and 93 deletions
  1. +95
    -46
      ModbusDemo/Device/ModbusRTU.cs
  2. +52
    -0
      ModbusDemo/ErrorCode/ErrorCode.cs
  3. +2
    -1
      ModbusDemo/Extension/EnumBindingSourceExtension.cs
  4. +5
    -0
      ModbusDemo/ModbusDemo.csproj
  5. +10
    -0
      ModbusDemo/Model/ErrorCode.cs
  6. +5
    -3
      ModbusDemo/Uitls/CheckData.cs
  7. +28
    -2
      ModbusDemo/VIew/SettingsUC.xaml
  8. +42
    -29
      ModbusDemo/VIewModel/AttachUCViewModel.cs
  9. +25
    -11
      ModbusDemo/VIewModel/SettingsUCViewModel.cs
  10. +6
    -1
      ModbusTest/UnitTest1.cs

+ 95
- 46
ModbusDemo/Device/ModbusRTU.cs View File

@@ -25,7 +25,7 @@ namespace ModbusDemo.Device
{
_serialPort = serialPort;
_modbusDbContext = modbusDbContext;
_portAdapter = new SerialPortAdapter(serialPort);
//_portAdapter = new SerialPortAdapter(serialPort);
}
/// <summary>
/// 用来发送读写线圈的指令
@@ -63,11 +63,20 @@ namespace ModbusDemo.Device
int attempt = 0;
byte[] response = null;
List<byte> responseData = new List<byte>();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
@@ -80,11 +89,12 @@ namespace ModbusDemo.Device
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Task.Delay(20);
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response[0] == 0x00)
if (response.Length == 0)
{
attempt++;
continue;
}
resultValue = ParseCoilresponse(sendByte, response, numberOfPoints);
@@ -93,6 +103,10 @@ namespace ModbusDemo.Device
catch (Exception)
{


}
finally
{
attempt++;
}

@@ -112,7 +126,7 @@ namespace ModbusDemo.Device
catch (Exception)
{

MessageBox.Show("0x15:数据库插入错误");
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}

return resultValue;
@@ -132,17 +146,17 @@ 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);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读线圈";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{

MessageBox.Show("0x15:数据库插入错误");
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}


@@ -222,11 +236,19 @@ namespace ModbusDemo.Device
int attempt = 0;
byte[] response = null;
List<byte> responseData = new List<byte>();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
@@ -239,10 +261,10 @@ namespace ModbusDemo.Device
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Task.Delay(20);
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response[0] != 0x00)
if (response.Length != 0)
{
break;
}
@@ -250,6 +272,10 @@ namespace ModbusDemo.Device
catch (Exception)
{


}
finally
{
attempt++;
}

@@ -260,16 +286,16 @@ 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);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "写线圈";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show("0x15:数据库插入错误");
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());

}
//判断发送回来的数据是否正确
@@ -334,12 +360,18 @@ namespace ModbusDemo.Device
int attempt = 0;
List<byte> responseData = new List<byte>();
byte[] response = null;
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{

_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
@@ -352,10 +384,10 @@ namespace ModbusDemo.Device
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Task.Delay(20);
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response[0] == 0x00)
if (response.Length == 0)
{
continue;
}
@@ -365,26 +397,31 @@ namespace ModbusDemo.Device
catch (Exception)
{


}
finally
{
attempt++;
}

}
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);


try
{
ModbusLog m = new ModbusLog();
m.OperationType = "读寄存器";
m.ResponseData = "";
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读寄存器";
modbusLog.ResponseData = "";
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{

MessageBox.Show("0x15:数据库插入错误");
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}

return resultValue;
@@ -406,16 +443,16 @@ 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);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读寄存器";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show("0x15:数据库插入错误");
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());

}

@@ -493,11 +530,19 @@ namespace ModbusDemo.Device
int attempt = 0;
byte[] response = null;
List<byte> responseData = new List<byte>();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
@@ -510,15 +555,19 @@ namespace ModbusDemo.Device
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Task.Delay(20);
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response[0] != 0x00)
if (response.Length != 0)
{
break;
}
}
catch (Exception)
{

}
finally
{
attempt++;
}
@@ -528,11 +577,11 @@ 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);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "写寄存器";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)


+ 52
- 0
ModbusDemo/ErrorCode/ErrorCode.cs View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ModbusDemo.ErrorCode
{
public class ErrorCode
{

// 静态列表:存储所有错误码
public static readonly List<ErrorCode> AllErrors = new List<ErrorCode>();

// 错误码值
public byte Code { get; }

// 错误描述
public string Description { get; }

// 私有构造函数,只能通过静态方法创建错误码
private ErrorCode(byte code, string description)
{
Code = code;
Description = description;

// 自动添加到错误码列表
AllErrors.Add(this);
}

// 重写 ToString 方法
public override string ToString() => $"Error 0x{Code:X2}: {Description}";

// 隐式转换为 byte
public static implicit operator byte(ErrorCode error) => error.Code;

// 根据错误码值查找错误码
public static ErrorCode FromByte(byte code) => AllErrors.FirstOrDefault(e => e.Code == code);

// 定义所有错误码
public static readonly ErrorCode FunctionCodeError = new ErrorCode(0x01, "功能码错误");
public static readonly ErrorCode DomainStartAddressError = new ErrorCode(0x02, "域数据首地址错误");
public static readonly ErrorCode DataContentError = new ErrorCode(0x03, "数据内容错误");
public static readonly ErrorCode DataTypeError = new ErrorCode(0x10, "数据类型错误");
public static readonly ErrorCode DomainDataCountError = new ErrorCode(0x11, "域数据个数错误");
public static readonly ErrorCode DataCountMismatch = new ErrorCode(0x12, "数据个数与内部区域数据个数不符");
public static readonly ErrorCode WriteOperationDenied = new ErrorCode(0x13, "不能进行写操作");
public static readonly ErrorCode CrcCheckError = new ErrorCode(0x14, "CRC校验错误");
public static readonly ErrorCode DatabaseException = new ErrorCode(0x15, "数据库异常");
}

}

+ 2
- 1
ModbusDemo/Extension/EnumBindingSourceExtension.cs View File

@@ -4,7 +4,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;

using System;
using System.Reflection;
namespace ModbusDemo.Extension
{
/// <summary>


+ 5
- 0
ModbusDemo/ModbusDemo.csproj View File

@@ -9,6 +9,11 @@
<ApplicationIcon>ModbusRTU_64x64.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Compile Remove="ErrorCode\ErrorCodeExtensions.cs" />
<Compile Remove="ErrorCode\ErrorInfoAttribute.cs" />
</ItemGroup>

<ItemGroup>
<Content Include="ModbusRTU_64x64.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>


+ 10
- 0
ModbusDemo/Model/ErrorCode.cs View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ModbusDemo.Model
{
}

+ 5
- 3
ModbusDemo/Uitls/CheckData.cs View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using ModbusDemo.ErrorCode;
namespace ModbusDemo.Uitls
{
public static class CheckData
@@ -32,7 +32,9 @@ namespace ModbusDemo.Uitls
// 判断高4位是否等于8
if (highNibble == 8)
{
MessageBox.Show($"数据错误:错误码为:0x{response[2]:X2}");
var error = ErrorCode.ErrorCode.FromByte(response[2]);
MessageBox.Show(error.ToString());
return false;
}
}
@@ -40,7 +42,7 @@ namespace ModbusDemo.Uitls

if (!CRCUitl.ValidateCRC(response))
{
MessageBox.Show("0x14:CRC校验错误");
MessageBox.Show(ErrorCode.ErrorCode.CrcCheckError.ToString());
return false;
}



+ 28
- 2
ModbusDemo/VIew/SettingsUC.xaml View File

@@ -31,7 +31,6 @@
<RowDefinition>
</RowDefinition>
</Grid.RowDefinitions>

<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition>
@@ -44,7 +43,8 @@
</ColumnDefinition>
<ColumnDefinition>
</ColumnDefinition>

<ColumnDefinition>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- 这是设置串口的选择 -->
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
@@ -131,6 +131,32 @@
</ComboBox>
</StackPanel>

<!-- 超时时间 -->
<StackPanel Grid.Column="5" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Margin="10,0,0,5" Text="超时时间">
</TextBlock>
<ComboBox
Width="80"
Height="30"
Margin="10,0,0,5"
HorizontalAlignment="Left"
materialDesign:HintAssist.Hint="200"
SelectedItem="{Binding TimeOut}">
<ComboBoxItem Content="20">
</ComboBoxItem>
<ComboBoxItem Content="50">
</ComboBoxItem>
<ComboBoxItem Content="100">
</ComboBoxItem>
<ComboBoxItem Content="200">
</ComboBoxItem>
<ComboBoxItem Content="300">
</ComboBoxItem>
<ComboBoxItem Content="500">
</ComboBoxItem>
</ComboBox>
</StackPanel>

</Grid>
<!-- 两个按钮的定义 -->
<Grid Grid.Row="2">


+ 42
- 29
ModbusDemo/VIewModel/AttachUCViewModel.cs View File

@@ -50,7 +50,7 @@ namespace ModbusDemo.VIewModel
}
public AttachUCViewModel(SerialPort serialPort)
{
ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister);
ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister2);
_serialPort = serialPort;
}
/// <summary>
@@ -124,18 +124,18 @@ namespace ModbusDemo.VIewModel
ushort currentChunkSize = 100;

int chunkIndex = i;
tasks[i] = Task.Run(async () =>
{

ushort[] registers = await ReadRegisters(slaveId, startAddress, currentChunkSize);
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;
@@ -151,27 +151,25 @@ namespace ModbusDemo.VIewModel
ReadResult = result.ToString();
}


public async Task<ushort[]> ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{

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]);
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);
@@ -183,14 +181,15 @@ namespace ModbusDemo.VIewModel
int attempt = 0;
List<byte> responseData = new List<byte>();
byte[] response = null;
_serialPort.Write(sendByte, 0, sendByte.Length);
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)
@@ -203,33 +202,42 @@ namespace ModbusDemo.VIewModel
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
await Task.Delay(20);
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response[0] == 0x00)
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)
{


//判断发送回来的数据是否正确
@@ -249,7 +257,12 @@ namespace ModbusDemo.VIewModel
{
return null;
}




}

}

}

+ 25
- 11
ModbusDemo/VIewModel/SettingsUCViewModel.cs View File

@@ -14,7 +14,7 @@ using System.Windows;

namespace ModbusDemo.VIewModel
{
class SettingsUCViewModel : BindableBase
{
//事件聚合器
@@ -22,9 +22,9 @@ namespace ModbusDemo.VIewModel
//控制页面跳转
private readonly IRegionManager _regionManager;
//断开连接的命令
public DelegateCommand DisConnectionCmm { get; set; }
public DelegateCommand DisConnectionCmm { get; set; }
//链接窗口的命令
public DelegateCommand ConnectionCmm { get; set; }
public DelegateCommand ConnectionCmm { get; set; }

//获取串口
private SerialPort _serialPort;
@@ -93,9 +93,23 @@ namespace ModbusDemo.VIewModel
}
}

//设置读取和写入时间
private string _timeOut = "200";

public string TimeOut
{
get { return _timeOut; }
set
{
_timeOut = value;
RaisePropertyChanged();
}
}


public SettingsUCViewModel()
{
}
/// <summary>
/// 从容器中获取创建的窗口
@@ -115,7 +129,7 @@ namespace ModbusDemo.VIewModel
if (_serialPort.IsOpen)
{
_serialPort.Close();
}
MessageBox.Show("连接已经断开");
}
@@ -139,11 +153,11 @@ namespace ModbusDemo.VIewModel
}
_serialPort.StopBits = this.StopBits;
_serialPort.DataBits = int.Parse(GetComboBoxItemValue(this.DataBits));
//读取超时时间
_serialPort.ReadTimeout = 500;
_serialPort.ReadTimeout = int.Parse(GetComboBoxItemValue(this.TimeOut));
//写入超时时间
_serialPort.WriteTimeout = 500;
_serialPort.WriteTimeout = int.Parse(GetComboBoxItemValue(this.TimeOut));
try
{
_serialPort.Open();
@@ -156,14 +170,14 @@ namespace ModbusDemo.VIewModel
catch (Exception)
{

MessageBox.Show("串口已经链接或者断开,请重新检查在尝试","warning",MessageBoxButton.OK,MessageBoxImage.Warning);
MessageBox.Show("串口已经链接或者断开,请重新检查在尝试", "warning", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
MessageBox.Show("当前串口已经连接,请先断开,再连接");
}
}
/// <summary>
/// 处理下拉框的选择信息
@@ -177,7 +191,7 @@ namespace ModbusDemo.VIewModel
string cleanText = displayText.Replace("System.Windows.Controls.ComboBoxItem: ", "");
return cleanText;
}

}
}

+ 6
- 1
ModbusTest/UnitTest1.cs View File

@@ -1,3 +1,5 @@
//using ModbusDemo.Enum;

namespace ModbusTest
{
public class Tests
@@ -10,7 +12,10 @@ namespace ModbusTest
[Test]
public void Test1()
{
Assert.Pass();
byte[] byteStream = new byte[] { 0x01, 0x81, 0x01, 0x10, 0x00, 0x00, 0xAB, 0x17 };
byte errorCodeByte = byteStream[2]; // 0x01
//ErrorCode errorCode = (ErrorCode)errorCodeByte; // ת»»Îª ErrorCode.FunctionCodeError
//string message = errorCode.GetMessage();
}
}
}

Loading…
Cancel
Save