Browse Source

线圈的读取和解析

dev1
永攀 张 2 weeks ago
parent
commit
1173bdd8fe
10 changed files with 412 additions and 52 deletions
  1. +6
    -1
      ModbusDemo/App.xaml.cs
  2. +13
    -0
      ModbusDemo/Device/IModbusRTU.cs
  3. +108
    -0
      ModbusDemo/Device/ModbusRTU.cs
  4. +25
    -0
      ModbusDemo/Uitls/CRCUitl.cs
  5. +34
    -0
      ModbusDemo/Uitls/CheckData.cs
  6. +74
    -9
      ModbusDemo/VIew/CoilUC.xaml
  7. +3
    -4
      ModbusDemo/VIew/SettingsUC.xaml
  8. +122
    -2
      ModbusDemo/VIewModel/CoilUCViewModel.cs
  9. +9
    -2
      ModbusDemo/VIewModel/MainWindowViewModel.cs
  10. +18
    -34
      ModbusDemo/VIewModel/SettingsUCViewModel.cs

+ 6
- 1
ModbusDemo/App.xaml.cs View File

@@ -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>
/// 程序打开默认是设置界面,来设置串口的各个东西


+ 13
- 0
ModbusDemo/Device/IModbusRTU.cs View File

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

+ 108
- 0
ModbusDemo/Device/ModbusRTU.cs View File

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

}
}

+ 25
- 0
ModbusDemo/Uitls/CRCUitl.cs View File

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

+ 34
- 0
ModbusDemo/Uitls/CheckData.cs View File

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

+ 74
- 9
ModbusDemo/VIew/CoilUC.xaml View File

@@ -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>

+ 3
- 4
ModbusDemo/VIew/SettingsUC.xaml View File

@@ -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>


+ 122
- 2
ModbusDemo/VIewModel/CoilUCViewModel.cs View File

@@ -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("参数配置错误");
}



}
}
}

+ 9
- 2
ModbusDemo/VIewModel/MainWindowViewModel.cs View File

@@ -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>
/// 初始化左下拉框


+ 18
- 34
ModbusDemo/VIewModel/SettingsUCViewModel.cs View File

@@ -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>
/// 处理下拉框的选择信息


Loading…
Cancel
Save