@@ -1,40 +1,47 @@ | |||||
<prism:PrismApplication x:Class="ModbusDemo.App" | |||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||||
xmlns:local="clr-namespace:ModbusDemo" | |||||
xmlns:prism="http://prismlibrary.com/" | |||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" | |||||
> | |||||
<prism:PrismApplication | |||||
x:Class="ModbusDemo.App" | |||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||||
xmlns:local="clr-namespace:ModbusDemo" | |||||
xmlns:prism="http://prismlibrary.com/" | |||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"> | |||||
<Application.Resources> | <Application.Resources> | ||||
<ResourceDictionary> | <ResourceDictionary> | ||||
<ResourceDictionary.MergedDictionaries> | <ResourceDictionary.MergedDictionaries> | ||||
<!-- 使用捆绑的主题,设置基础主题为浅色,主色为深紫色,次色为酸橙色 --> | |||||
<!-- 使用捆绑的主题,设置基础主题为浅色,主色为深紫色,次色为酸橙色 --> | |||||
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="LightBlue" SecondaryColor="Lime" /> | <materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="LightBlue" SecondaryColor="Lime" /> | ||||
<!-- 引用 Material Design 2 的默认样式 --> | |||||
<!-- 引用 Material Design 2 的默认样式 --> | |||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" /> | <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" /> | ||||
</ResourceDictionary.MergedDictionaries> | </ResourceDictionary.MergedDictionaries> | ||||
<Style x:Key="LeftMenuStyle" TargetType="ListBoxItem"> | <Style x:Key="LeftMenuStyle" TargetType="ListBoxItem"> | ||||
<Setter Property="MinHeight" Value="40"></Setter> | |||||
<Setter Property="MinHeight" Value="40"> | |||||
</Setter> | |||||
<Setter Property="Template"> | <Setter Property="Template"> | ||||
<Setter.Value> | <Setter.Value> | ||||
<ControlTemplate TargetType="ListBoxItem"> | <ControlTemplate TargetType="ListBoxItem"> | ||||
<Grid> | <Grid> | ||||
<Border x:Name="border"></Border> | |||||
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"></ContentPresenter> | |||||
<Border x:Name="border"> | |||||
</Border> | |||||
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"> | |||||
</ContentPresenter> | |||||
</Grid> | </Grid> | ||||
<!--触发器--> | |||||
<!-- 触发器 --> | |||||
<ControlTemplate.Triggers> | <ControlTemplate.Triggers> | ||||
<Trigger Property="IsSelected" Value="True"> | <Trigger Property="IsSelected" Value="True"> | ||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"></Setter> | |||||
<Setter TargetName="border" Property="Opacity" Value="0.2"></Setter> | |||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"> | |||||
</Setter> | |||||
<Setter TargetName="border" Property="Opacity" Value="0.2"> | |||||
</Setter> | |||||
</Trigger> | </Trigger> | ||||
<Trigger Property="IsMouseOver" Value="True"> | <Trigger Property="IsMouseOver" Value="True"> | ||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"></Setter> | |||||
<Setter TargetName="border" Property="Opacity" Value="0.2"></Setter> | |||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"> | |||||
</Setter> | |||||
<Setter TargetName="border" Property="Opacity" Value="0.2"> | |||||
</Setter> | |||||
</Trigger> | </Trigger> | ||||
</ControlTemplate.Triggers> | </ControlTemplate.Triggers> | ||||
</ControlTemplate> | </ControlTemplate> | ||||
@@ -9,5 +9,9 @@ namespace ModbusDemo.Device | |||||
interface IModbusRTU | interface IModbusRTU | ||||
{ | { | ||||
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); | |||||
} | } | ||||
} | } |
@@ -26,7 +26,11 @@ namespace ModbusDemo.Device | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | ||||
{ | { | ||||
if (!_serialPort.IsOpen) | |||||
{ | |||||
MessageBox.Show("串口没有链接,请先链接"); | |||||
return null; | |||||
} | |||||
List<byte> sendByteList = new List<byte>(); | List<byte> sendByteList = new List<byte>(); | ||||
//设置从站地址 | //设置从站地址 | ||||
sendByteList.Add(slaveAddress); | sendByteList.Add(slaveAddress); | ||||
@@ -89,20 +93,84 @@ namespace ModbusDemo.Device | |||||
} | } | ||||
//先将字符串转为数组,再反转,再转为字节数组 | //先将字符串转为数组,再反转,再转为字节数组 | ||||
char[] chars = result.ToArray().Reverse<char>().ToArray(); | char[] chars = result.ToArray().Reverse<char>().ToArray(); | ||||
bool[] f = new bool[numberOfPoints]; | |||||
bool[] ultimately = new bool[numberOfPoints]; | |||||
for (int i = 0; i < numberOfPoints; i++) | for (int i = 0; i < numberOfPoints; i++) | ||||
{ | { | ||||
if (chars[i] == '1') | if (chars[i] == '1') | ||||
{ | { | ||||
f[i] = true; | |||||
ultimately[i] = true; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
f[i] = false; | |||||
ultimately[i] = false; | |||||
} | } | ||||
} | } | ||||
return f; | |||||
return ultimately; | |||||
} | } | ||||
public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data) | |||||
{ | |||||
List<byte> sendByteList = new List<byte>(); | |||||
//设置从站地址 | |||||
sendByteList.Add(slaveAddress); | |||||
//设置功能码 | |||||
sendByteList.Add(0x0F); | |||||
//设置起始地址的高位和低位 | |||||
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]); | |||||
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]); | |||||
//设置写入几个线圈 | |||||
sendByteList.Add(BitConverter.GetBytes(data.Length)[1]); | |||||
sendByteList.Add(BitConverter.GetBytes(data.Length)[0]); | |||||
// 计算字节数 | |||||
int byteCount = (data.Length + 7) / 8; | |||||
sendByteList.Add((byte)byteCount); | |||||
// 初始化字节数组 | |||||
byte[] coilBytes = new byte[byteCount]; | |||||
// 把bool数组转换为字节 | |||||
for (int i = 0; i < data.Length; i++) | |||||
{ | |||||
int byteIndex = i / 8; | |||||
int bitIndex = i % 8; | |||||
if (data[i]) | |||||
{ | |||||
coilBytes[byteIndex] |= (byte)(1 << bitIndex); | |||||
} | |||||
} | |||||
sendByteList.AddRange(coilBytes); | |||||
//获取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); | |||||
//判断发送回来的数据是否正确 | |||||
CheckData.CheckResponse(response); | |||||
if (!CRCUitl.ValidateCRC(response)) | |||||
{ | |||||
MessageBox.Show("0x14:CRC校验错误"); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -31,6 +31,7 @@ | |||||
FontWeight="Black" | FontWeight="Black" | ||||
FontSize="16"> | FontSize="16"> | ||||
</TextBlock> | </TextBlock> | ||||
<!-- 定义读取的数据 --> | |||||
<Grid Grid.Row="1"> | <Grid Grid.Row="1"> | ||||
<Grid.ColumnDefinitions> | <Grid.ColumnDefinitions> | ||||
<ColumnDefinition> | <ColumnDefinition> | ||||
@@ -44,34 +45,89 @@ | |||||
<ColumnDefinition> | <ColumnDefinition> | ||||
</ColumnDefinition> | </ColumnDefinition> | ||||
</Grid.ColumnDefinitions> | </Grid.ColumnDefinitions> | ||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center"> | |||||
<TextBlock Text="从站地址" Margin="20"></TextBlock> | |||||
<TextBox Text="{Binding SlaveAddress}" Margin="0,-10,0,0"></TextBox> | |||||
<StackPanel HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="从站地址"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding SlaveAddress}"> | |||||
</TextBox> | |||||
</StackPanel> | </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 Grid.Column="1" HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="起始地址"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding StartAddress}"> | |||||
</TextBox> | |||||
</StackPanel> | </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 Grid.Column="2" HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="读取位数"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding NumberOfPoints}"> | |||||
</TextBox> | |||||
</StackPanel> | </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 Grid.Column="3" HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="读取结果"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding ReadResult}"> | |||||
</TextBox> | |||||
</StackPanel> | </StackPanel> | ||||
<Button | <Button | ||||
Grid.Column="4" | Grid.Column="4" | ||||
Width="80" | Width="80" | ||||
Height="30" | Height="30" | ||||
Command="{Binding ReadCoilCmm}" | |||||
materialDesign:ButtonAssist.CornerRadius="15" | materialDesign:ButtonAssist.CornerRadius="15" | ||||
Command="{Binding ReadCoilCmm}" | |||||
Content="读取" | Content="读取" | ||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | Style="{StaticResource MaterialDesignRaisedDarkButton}"> | ||||
</Button> | </Button> | ||||
</Grid> | </Grid> | ||||
<Grid Grid.Row="2"> | |||||
<Grid.ColumnDefinitions> | |||||
<ColumnDefinition> | |||||
</ColumnDefinition> | |||||
<ColumnDefinition> | |||||
</ColumnDefinition> | |||||
<ColumnDefinition> | |||||
</ColumnDefinition> | |||||
<ColumnDefinition> | |||||
</ColumnDefinition> | |||||
<ColumnDefinition> | |||||
</ColumnDefinition> | |||||
</Grid.ColumnDefinitions> | |||||
<StackPanel HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="从站地址"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding WriteSlaveAddress}"> | |||||
</TextBox> | |||||
</StackPanel> | |||||
<StackPanel Grid.Column="1" HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="起始地址"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding WriteStartAddress}"> | |||||
</TextBox> | |||||
</StackPanel> | |||||
<StackPanel Grid.Column="2" HorizontalAlignment="Center" Orientation="Vertical"> | |||||
<TextBlock Margin="20" Text="写入数据"> | |||||
</TextBlock> | |||||
<TextBox Margin="0,-10,0,0" Text="{Binding WriteData}"> | |||||
</TextBox> | |||||
</StackPanel> | |||||
<TextBlock Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center" Text="1代表Ture,0代表flase"> | |||||
</TextBlock> | |||||
<Button | |||||
Grid.Column="4" | |||||
Width="80" | |||||
Height="30" | |||||
materialDesign:ButtonAssist.CornerRadius="15" | |||||
Command="{Binding WriteCoilCmm}" | |||||
Content="写入" | |||||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | |||||
</Button> | |||||
</Grid> | |||||
</Grid> | </Grid> | ||||
</UserControl> | </UserControl> |
@@ -20,6 +20,8 @@ namespace ModbusDemo.VIewModel | |||||
private IModbusRTU _modbusRTU; | private IModbusRTU _modbusRTU; | ||||
//定义读线圈的命令 | //定义读线圈的命令 | ||||
public DelegateCommand ReadCoilCmm { get; set; } | public DelegateCommand ReadCoilCmm { get; set; } | ||||
//定义写线圈操作 | |||||
public DelegateCommand WriteCoilCmm { get; set; } | |||||
//获取当前使用的串口 | //获取当前使用的串口 | ||||
private SerialPort _serialPort; | private SerialPort _serialPort; | ||||
//显示当前链接串口的信息 | //显示当前链接串口的信息 | ||||
@@ -32,7 +34,7 @@ namespace ModbusDemo.VIewModel | |||||
} | } | ||||
} | } | ||||
#region 读取定义 | |||||
//读取的从站id | //读取的从站id | ||||
private string _slaveAddress; | private string _slaveAddress; | ||||
@@ -46,10 +48,10 @@ namespace ModbusDemo.VIewModel | |||||
RaisePropertyChanged(); | RaisePropertyChanged(); | ||||
} | } | ||||
} | } | ||||
//读取的起始地址 | |||||
private string _startAddress; | private string _startAddress; | ||||
//读取的起始地址 | |||||
public string StartAddress | public string StartAddress | ||||
{ | { | ||||
get { return _startAddress; } | get { return _startAddress; } | ||||
@@ -59,10 +61,10 @@ namespace ModbusDemo.VIewModel | |||||
RaisePropertyChanged(); | RaisePropertyChanged(); | ||||
} | } | ||||
} | } | ||||
//读取的位数 | |||||
private string _numberOfPoints; | private string _numberOfPoints; | ||||
//读取的位数 | |||||
public string NumberOfPoints | public string NumberOfPoints | ||||
{ | { | ||||
get { return _numberOfPoints; } | get { return _numberOfPoints; } | ||||
@@ -85,9 +87,51 @@ namespace ModbusDemo.VIewModel | |||||
RaisePropertyChanged(); | RaisePropertyChanged(); | ||||
} | } | ||||
} | } | ||||
#endregion | |||||
#region 写入定义 | |||||
//写入的从站id | |||||
private string _writeslaveAddress; | |||||
public string WriteSlaveAddress | |||||
{ | |||||
get { return _writeslaveAddress; } | |||||
set | |||||
{ | |||||
_writeslaveAddress = value; | |||||
RaisePropertyChanged(); | |||||
} | |||||
} | |||||
//写入的起始地址 | |||||
private string _writestartAddress; | |||||
public string WriteStartAddress | |||||
{ | |||||
get { return _writestartAddress; } | |||||
set | |||||
{ | |||||
_writestartAddress = value; | |||||
RaisePropertyChanged(); | |||||
} | |||||
} | |||||
//写入的数据 | |||||
private string _writeData; | |||||
public string WriteData | |||||
{ | |||||
get { return _writeData; } | |||||
set | |||||
{ | |||||
_writeData = value; | |||||
RaisePropertyChanged(); | |||||
} | |||||
} | |||||
#endregion | |||||
public CoilUCViewModel() | public CoilUCViewModel() | ||||
{ | { | ||||
@@ -98,30 +142,31 @@ namespace ModbusDemo.VIewModel | |||||
_serialPort = serialPort; | _serialPort = serialPort; | ||||
ReadCoilCmm = new DelegateCommand(ReadCoil); | ReadCoilCmm = new DelegateCommand(ReadCoil); | ||||
_modbusRTU = modbusRTU; | _modbusRTU = modbusRTU; | ||||
WriteCoilCmm = new DelegateCommand(WriteCoil); | |||||
} | } | ||||
private void ReadCoil() | |||||
{ | |||||
/// <summary> | |||||
/// 线圈读取操作 | |||||
/// </summary> | |||||
private void ReadCoil() | |||||
{ | |||||
try | try | ||||
{ | { | ||||
bool[] result = _modbusRTU.ReadCoil(byte.Parse(SlaveAddress), | bool[] result = _modbusRTU.ReadCoil(byte.Parse(SlaveAddress), | ||||
ushort.Parse(StartAddress), | ushort.Parse(StartAddress), | ||||
ushort.Parse(NumberOfPoints)); | ushort.Parse(NumberOfPoints)); | ||||
if(result != null) | |||||
if (result != null) | |||||
{ | { | ||||
string temp = ""; | string temp = ""; | ||||
foreach(var item in result) | |||||
foreach (var item in result) | |||||
{ | { | ||||
temp += item; | temp += item; | ||||
temp += " "; | temp += " "; | ||||
} | } | ||||
ReadResult = temp; | ReadResult = temp; | ||||
} | } | ||||
} | } | ||||
catch (Exception) | catch (Exception) | ||||
{ | { | ||||
@@ -129,9 +174,26 @@ namespace ModbusDemo.VIewModel | |||||
MessageBox.Show("参数配置错误"); | MessageBox.Show("参数配置错误"); | ||||
} | } | ||||
} | |||||
private void WriteCoil() | |||||
{ | |||||
//去除字符串中的空格 | |||||
WriteData = WriteData.Replace(" ", ""); | |||||
//转换为布尔数组 | |||||
bool[] data = WriteData.Select(m => m == '1').ToArray(); | |||||
try | |||||
{ | |||||
_modbusRTU.WriteCoil(byte.Parse(WriteSlaveAddress), | |||||
ushort.Parse(WriteStartAddress), | |||||
data); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
MessageBox.Show("信息配置错误"); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |