@@ -1,4 +1,5 @@ | |||
using ModbusDemo.VIew; | |||
using ModbusDemo.Device; | |||
using ModbusDemo.VIew; | |||
using ModbusDemo.VIewModel; | |||
using Prism.DryIoc; | |||
using Prism.Ioc; | |||
@@ -39,6 +40,10 @@ namespace ModbusDemo | |||
containerRegistry.RegisterForNavigation<SettingsUC, SettingsUCViewModel>(); | |||
//将窗口注册为全局唯一的单例 | |||
containerRegistry.RegisterSingleton<SerialPort>(); | |||
//将读线圈注册 | |||
containerRegistry.Register<ModbusRTU>(); | |||
} | |||
/// <summary> | |||
/// 程序打开默认是设置界面,来设置串口的各个东西 | |||
@@ -0,0 +1,13 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace ModbusDemo.Device | |||
{ | |||
interface IModbusRTU | |||
{ | |||
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints); | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
using ModbusDemo.Uitls; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO.Ports; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
namespace ModbusDemo.Device | |||
{ | |||
public class ModbusRTU : IModbusRTU | |||
{ | |||
private SerialPort _serialPort; | |||
public ModbusRTU(SerialPort serialPort) | |||
{ | |||
_serialPort = serialPort; | |||
} | |||
/// <summary> | |||
/// 用来发送读写线圈的指令 | |||
/// </summary> | |||
/// <param name="slaveAddress"></param> | |||
/// <param name="startAddress"></param> | |||
/// <param name="numberOfPoints"></param> | |||
/// <returns></returns> | |||
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | |||
{ | |||
List<byte> sendByteList = new List<byte>(); | |||
//设置从站地址 | |||
sendByteList.Add(slaveAddress); | |||
//设置功能码 | |||
sendByteList.Add(0x01); | |||
//设置起始地址的高位和低位 | |||
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(); | |||
if (_serialPort.IsOpen) | |||
{ | |||
// 清空接收缓冲区(避免残留数据干扰) | |||
_serialPort.DiscardInBuffer(); | |||
_serialPort.Write(sendByte, 0, sendByte.Length); | |||
Thread.Sleep(300); | |||
byte[] response = new byte[_serialPort.BytesToRead]; | |||
_serialPort.Read(response, 0, _serialPort.BytesToRead); | |||
return Parseresponse(response,numberOfPoints); | |||
} | |||
return null; | |||
} | |||
public bool[] Parseresponse(byte[] response, ushort numberOfPoints) | |||
{ | |||
//判断发送回来的数据是否正确 | |||
CheckData.CheckResponse(response); | |||
if (!CRCUitl.ValidateCRC(response)) | |||
{ | |||
MessageBox.Show("0x14:CRC校验错误"); | |||
return null; | |||
} | |||
List<byte> responseList = new List<byte>(response); | |||
//移除前两位 | |||
responseList.RemoveRange(0, 3); | |||
//移除后两位校验码 | |||
responseList.RemoveRange(responseList.Count - 2, 2); | |||
responseList.Reverse(); | |||
//数组反转之后,转为二进制字符 | |||
List<string> StringList = responseList.Select(m => Convert.ToString(m, 2)).ToList(); | |||
var result = ""; | |||
foreach (var item in StringList) | |||
{ | |||
result += item.PadLeft(8, '0'); | |||
} | |||
//先将字符串转为数组,再反转,再转为字节数组 | |||
char[] chars = result.ToArray().Reverse<char>().ToArray(); | |||
bool[] f = new bool[numberOfPoints]; | |||
for (int i = 0; i < numberOfPoints; i++) | |||
{ | |||
if (chars[i] == '1') | |||
{ | |||
f[i] = true; | |||
} | |||
else | |||
{ | |||
f[i] = false; | |||
} | |||
} | |||
return f; | |||
} | |||
} | |||
} |
@@ -77,5 +77,30 @@ namespace ModbusDemo.Uitls | |||
} | |||
return res; | |||
} | |||
/// <summary> | |||
/// 用来计算CRC是否合理 | |||
/// </summary> | |||
/// <param name="fullFrame"></param> | |||
/// <returns></returns> | |||
public static bool ValidateCRC(byte[] fullFrame) | |||
{ | |||
// 报文必须至少包含2个字节的CRC校验码 | |||
if (fullFrame.Length < 2) | |||
return false; | |||
// 计算数据部分的长度(排除最后2个CRC字节) | |||
int dataLength = fullFrame.Length - 2; | |||
// 提取报文中包含的CRC校验码(传输顺序:低字节在前,高字节在后) | |||
byte receivedCrcLo = fullFrame[fullFrame.Length - 2]; | |||
byte receivedCrcHi = fullFrame[fullFrame.Length - 1]; | |||
// 计算数据部分的CRC(返回顺序:高字节在前,低字节在后) | |||
byte[] calculatedCrc = CalculateCRC(fullFrame, dataLength); | |||
// 比较计算出的CRC和接收到的CRC | |||
return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) && | |||
(calculatedCrc[calculatedCrc.Length - 2] == receivedCrcLo); | |||
} | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
namespace ModbusDemo.Uitls | |||
{ | |||
public static class CheckData | |||
{ | |||
public static void CheckResponse(byte[] response) | |||
{ | |||
// 检查数组长度是否足够 | |||
if (response.Length > 1) | |||
{ | |||
byte secondByte = response[1]; // 获取第二个字节(索引为1) | |||
// 使用掩码0xF0获取高4位,并右移4位 | |||
int highNibble = (secondByte & 0xF0) >> 4; | |||
// 判断高4位是否等于8 | |||
if (highNibble == 8) | |||
{ | |||
MessageBox.Show($"数据错误:错误码为:0x{response[2]:X2}"); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,12 +1,77 @@ | |||
<UserControl x:Class="ModbusDemo.VIew.CoilUC" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:ModbusDemo.VIew" | |||
mc:Ignorable="d" | |||
d:DesignHeight="450" d:DesignWidth="800"> | |||
<UserControl | |||
x:Class="ModbusDemo.VIew.CoilUC" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:ModbusDemo.VIew" | |||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" | |||
xmlns:viewmodel="clr-namespace:ModbusDemo.VIewModel" | |||
d:DataContext="{d:DesignInstance Type=viewmodel:CoilUCViewModel, | |||
IsDesignTimeCreatable=True}" | |||
d:Background="White" | |||
d:DesignHeight="450" | |||
d:DesignWidth="800" | |||
mc:Ignorable="d"> | |||
<Grid> | |||
<TextBlock Text="Coil" FontSize="60"></TextBlock> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="60"> | |||
</RowDefinition> | |||
<RowDefinition> | |||
</RowDefinition> | |||
<RowDefinition> | |||
</RowDefinition> | |||
<RowDefinition> | |||
</RowDefinition> | |||
</Grid.RowDefinitions> | |||
<TextBlock | |||
HorizontalAlignment="Center" | |||
VerticalAlignment="Center" | |||
Text="{Binding SerialPortInfo}" | |||
FontWeight="Black" | |||
FontSize="16"> | |||
</TextBlock> | |||
<Grid Grid.Row="1"> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
</Grid.ColumnDefinitions> | |||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center"> | |||
<TextBlock Text="从站地址" Margin="20"></TextBlock> | |||
<TextBox Text="{Binding SlaveAddress}" Margin="0,-10,0,0"></TextBox> | |||
</StackPanel> | |||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Grid.Column="1"> | |||
<TextBlock Text="起始地址" Margin="20"></TextBlock> | |||
<TextBox Text="{Binding StartAddress}" Margin="0,-10,0,0"></TextBox> | |||
</StackPanel> | |||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Grid.Column="2"> | |||
<TextBlock Text="读取位数" Margin="20"></TextBlock> | |||
<TextBox Text="{Binding NumberOfPoints}" Margin="0,-10,0,0"></TextBox> | |||
</StackPanel> | |||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Grid.Column="3"> | |||
<TextBlock Text="读取结果" Margin="20"></TextBlock> | |||
<TextBox Text="{Binding ReadResult}" Margin="0,-10,0,0"></TextBox> | |||
</StackPanel> | |||
<Button | |||
Grid.Column="4" | |||
Width="80" | |||
Height="30" | |||
Command="{Binding ReadCoilCmm}" | |||
materialDesign:ButtonAssist.CornerRadius="15" | |||
Content="读取" | |||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | |||
</Button> | |||
</Grid> | |||
</Grid> | |||
</UserControl> |
@@ -43,10 +43,7 @@ | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
<ColumnDefinition> | |||
</ColumnDefinition> | |||
</Grid.ColumnDefinitions> | |||
<!-- 这是设置串口的选择 --> | |||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> | |||
@@ -127,7 +124,9 @@ | |||
</ComboBoxItem> | |||
</ComboBox> | |||
</StackPanel> | |||
</Grid> | |||
<!-- 两个按钮的定义 --> | |||
<Grid Grid.Row="2"> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition> | |||
@@ -1,17 +1,137 @@ | |||
using Prism.Mvvm; | |||
using ModbusDemo.Device; | |||
using Prism.Commands; | |||
using Prism.Mvvm; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO.Ports; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
namespace ModbusDemo.VIewModel | |||
{ | |||
/// <summary> | |||
/// 这是CoilUC的VM用来支持数据绑定 | |||
/// </summary> | |||
public class CoilUCViewModel:BindableBase | |||
public class CoilUCViewModel : BindableBase | |||
{ | |||
//获取读线圈的类 | |||
private IModbusRTU _modbusRTU; | |||
//定义读线圈的命令 | |||
public DelegateCommand ReadCoilCmm { get; set; } | |||
//获取当前使用的串口 | |||
private SerialPort _serialPort; | |||
//显示当前链接串口的信息 | |||
public string SerialPortInfo | |||
{ | |||
get | |||
{ | |||
return "当前链接的状态:" + "串口号:" + _serialPort.PortName + ",波特率:" + _serialPort.BaudRate | |||
+ ",数据位:" + _serialPort.DataBits + ",校验位:" + _serialPort.Parity + ",停止位:" + _serialPort.StopBits; | |||
} | |||
} | |||
//读取的从站id | |||
private string _slaveAddress; | |||
public string SlaveAddress | |||
{ | |||
get { return _slaveAddress; } | |||
set | |||
{ | |||
_slaveAddress = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
private string _startAddress; | |||
//读取的起始地址 | |||
public string StartAddress | |||
{ | |||
get { return _startAddress; } | |||
set | |||
{ | |||
_startAddress = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
private string _numberOfPoints; | |||
//读取的位数 | |||
public string NumberOfPoints | |||
{ | |||
get { return _numberOfPoints; } | |||
set | |||
{ | |||
_numberOfPoints = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
private string _readResult; | |||
//读取的结果 | |||
public string ReadResult | |||
{ | |||
get { return _readResult; } | |||
set | |||
{ | |||
_readResult = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
public CoilUCViewModel() | |||
{ | |||
} | |||
public CoilUCViewModel(SerialPort serialPort, ModbusRTU modbusRTU) | |||
{ | |||
_serialPort = serialPort; | |||
ReadCoilCmm = new DelegateCommand(ReadCoil); | |||
_modbusRTU = modbusRTU; | |||
} | |||
private void ReadCoil() | |||
{ | |||
try | |||
{ | |||
bool[] result = _modbusRTU.ReadCoil(byte.Parse(SlaveAddress), | |||
ushort.Parse(StartAddress), | |||
ushort.Parse(NumberOfPoints)); | |||
if(result != null) | |||
{ | |||
string temp = ""; | |||
foreach(var item in result) | |||
{ | |||
temp += item; | |||
temp += " "; | |||
} | |||
ReadResult = temp; | |||
} | |||
} | |||
catch (Exception) | |||
{ | |||
MessageBox.Show("参数配置错误"); | |||
} | |||
} | |||
} | |||
} |
@@ -45,10 +45,17 @@ namespace ModbusDemo.VIewModel | |||
NavigationCmm = new DelegateCommand<MenusInfo>(Navigation); | |||
} | |||
/// <summary> | |||
/// 视图导航 | |||
/// </summary> | |||
/// <param name="info"></param> | |||
private void Navigation(MenusInfo info) | |||
{ | |||
_regionManager.Regions["ModbusRegion"].RequestNavigate(info.ViewName); | |||
if(info != null) | |||
{ | |||
_regionManager.Regions["ModbusRegion"].RequestNavigate(info.ViewName); | |||
} | |||
} | |||
/// <summary> | |||
/// 初始化左下拉框 | |||
@@ -7,6 +7,7 @@ using System.IO.Ports; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
namespace ModbusDemo.VIewModel | |||
{ | |||
@@ -115,42 +116,25 @@ namespace ModbusDemo.VIewModel | |||
//todo,做数据的判空处理 | |||
if (!_serialPort.IsOpen) | |||
{ | |||
_serialPort.PortName = GetComboBoxItemValue(this.PortName); | |||
_serialPort.BaudRate = int.Parse(GetComboBoxItemValue(this.BaudRate)); | |||
_serialPort.Parity = this.Parity; | |||
_serialPort.StopBits = this.StopBits; | |||
_serialPort.DataBits = int.Parse(GetComboBoxItemValue(this.DataBits)); | |||
_serialPort.Open(); | |||
try | |||
{ | |||
_serialPort.PortName = GetComboBoxItemValue(this.PortName); | |||
_serialPort.BaudRate = int.Parse(GetComboBoxItemValue(this.BaudRate)); | |||
_serialPort.Parity = this.Parity; | |||
_serialPort.StopBits = this.StopBits; | |||
_serialPort.DataBits = int.Parse(GetComboBoxItemValue(this.DataBits)); | |||
_serialPort.Open(); | |||
MessageBox.Show("串口链接成功"); | |||
} | |||
catch (Exception) | |||
{ | |||
MessageBox.Show("串口已经链接,请先断开链接在尝试"); | |||
} | |||
} | |||
List<byte> bytes = new List<byte>(); | |||
bytes.Add(0x01); | |||
bytes.Add(0x05); | |||
bytes.Add(BitConverter.GetBytes(301)[1]); | |||
bytes.Add(BitConverter.GetBytes(301)[0]); | |||
bytes.Add(0xFF); | |||
bytes.Add(0x00); | |||
byte[] bytes1 = CRCUitl.CalculateCRC(bytes.ToArray(), bytes.Count); | |||
bytes.Add(bytes1[0]); | |||
bytes.Add(bytes1[1]); | |||
byte[] bytes2 = bytes.ToArray(); | |||
System.Threading.Thread.Sleep(200); // 等待200毫秒 | |||
byte[] response = new byte[_serialPort.BytesToRead]; | |||
//int bytesRead = serialPort.Read(response, 0, response.Length); | |||
//string hexResponse = BitConverter.ToString(response, 0, bytesRead); | |||
_serialPort.Write(bytes2, 0, bytes2.Length); | |||
} | |||
/// <summary> | |||
/// 处理下拉框的选择信息 | |||