增加以串口方式解析EF3板

This commit is contained in:
wio
2022-10-08 11:41:14 +08:00
parent 2f481baaa2
commit c01cb1400f
38 changed files with 55718 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
+211
View File
@@ -0,0 +1,211 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFSerialAssistant", "WPFSerialAssistant\WPFSerialAssistant.csproj", "{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,46 @@
<Window
x:Class="WPFSerialAssistant.About"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFSerialAssistant"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Name="AboutWindow"
Title="关于串口助手"
Width="350"
Height="200"
Icon="/icon/HexagonNew.ico"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*" />
<RowDefinition Height="150*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="5">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
EF3串口调试助手
</TextBlock>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
WPF Serial Assistant
</TextBlock>
</StackPanel>
<Grid Grid.Row="1">
<StackPanel
Grid.Column="0"
Width="300"
Margin="10">
<TextBlock Margin="5" TextWrapping="Wrap">本串口工具旨在配合EF3电路板完成光栅点缓存数据的解析,读取和保存工作</TextBlock>
<TextBlock Margin="5">版本编号:Verison 0.1</TextBlock>
<TextBlock Margin="5">更新日期:2022.10.08</TextBlock>
<!--<TextBlock Margin="5">软件作者:Christopher Lee</TextBlock>-->
<!--<TextBlock Margin="5">反馈邮箱:christopherlee1993@outlook.com</TextBlock>-->
<!--<TextBlock Margin="5">小小说明:开放源码,采用MIT协议。望共同学习进步。</TextBlock>-->
</StackPanel>
</Grid>
</Grid>
</Window>
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WPFSerialAssistant
{
/// <summary>
/// About.xaml 的交互逻辑
/// </summary>
public partial class About : Window
{
public About()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup>
</configuration>
@@ -0,0 +1,9 @@
<Application x:Class="WPFSerialAssistant.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFSerialAssistant"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
//using System.Threading.Tasks;
using System.Windows;
namespace WPFSerialAssistant
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}
+392
View File
@@ -0,0 +1,392 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace WPFSerialAssistant
{
public partial class MainWindow : Window
{
/// <summary>
/// 核心初始化
/// </summary>
private void InitCore()
{
// 加载配置信息
LoadConfig();
// 其他模块初始化
InitClockTimer();
InitAutoSendDataTimer();
InitSerialPort();
// 查找可以使用的端口号
FindPorts();
}
#region
/// <summary>
/// 更新时间信息
/// </summary>
private void UpdateTimeDate()
{
string timeDateString = "";
DateTime now = DateTime.Now;
timeDateString = string.Format("{0}年{1}月{2}日 {3}:{4}:{5}",
now.Year,
now.Month.ToString("00"),
now.Day.ToString("00"),
now.Hour.ToString("00"),
now.Minute.ToString("00"),
now.Second.ToString("00"));
timeDateTextBlock.Text = timeDateString;
}
/// <summary>
/// 警告信息提示(一直提示)
/// </summary>
/// <param name="message">提示信息</param>
private void Alert(string message)
{
// #FF68217A
statusBar.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x21, 0x2A));
statusInfoTextBlock.Text = message;
}
/// <summary>
/// 普通状态信息提示
/// </summary>
/// <param name="message">提示信息</param>
private void Information(string message)
{
if (serialPort.IsOpen)
{
// #FFCA5100
statusBar.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xCA, 0x51, 0x00));
}
else
{
// #FF007ACC
statusBar.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x00, 0x7A, 0xCC));
}
statusInfoTextBlock.Text = message;
}
#endregion
private void RecvDataBoxAppend(string textData)
{
if (showRecvDataCheckBox.IsChecked == true)
{
this.recvDataRichTextBox.AppendText(textData);
this.recvDataRichTextBox.ScrollToEnd();
}
}
private bool SendData()
{
string textToSend = sendDataTextBox.Text;
if (string.IsNullOrEmpty(textToSend))
{
Alert("要发送的内容不能为空!");
return false;
}
if (autoSendEnableCheckBox.IsChecked == true)
{
return SerialPortWrite(textToSend, false);
}
else
{
return SerialPortWrite(textToSend);
}
}
private void AutoSendData()
{
bool ret = SendData();
if (ret == false)
{
return;
}
// 启动自动发送定时器
StartAutoSendDataTimer(GetAutoSendDataInterval());
// 提示处于自动发送状态
progressBar.Visibility = Visibility.Visible;
Information("串口自动发送数据中...");
}
private int GetAutoSendDataInterval()
{
int interval = 1000;
if (int.TryParse(autoSendIntervalTextBox.Text.Trim(), out interval) == true)
{
string select = timeUnitComboBox.Text.Trim();
switch (select)
{
case "毫秒":
break;
case "秒钟":
interval *= 1000;
break;
case "分钟":
interval = interval * 60 * 1000;
break;
default:
break;
}
}
return interval;
}
#region
//
// 目前保存的配置信息如下:
// 1. 波特率
// 2. 奇偶校验位
// 3. 数据位
// 4. 停止位
// 5. 字节编码
// 6. 发送区文本内容
// 7. 自动发送时间间隔
// 8. 窗口状态:最大化|高度+宽度
// 9. 面板显示状态
// 10. 接收数据模式
// 11. 是否显示接收数据
// 12. 发送数据模式
// 13. 发送追加内容
//
/// <summary>
/// 保存配置信息
/// </summary>
private void SaveConfig()
{
// 配置对象实例
Configuration config = new Configuration();
// 保存波特率
AddBaudRate(config);
// 保存奇偶校验位
config.Add("parity", parityComboBox.SelectedIndex);
// 保存数据位
config.Add("dataBits", dataBitsComboBox.SelectedIndex);
// 保存停止位
config.Add("stopBits", stopBitsComboBox.SelectedIndex);
// 字节编码
config.Add("encoding", encodingComboBox.SelectedIndex);
// 保存发送区文本内容
config.Add("sendDataTextBoxText", sendDataTextBox.Text);
// 自动发送时间间隔
config.Add("autoSendDataInterval", autoSendIntervalTextBox.Text);
config.Add("timeUnit", timeUnitComboBox.SelectedIndex);
// 窗口状态信息
config.Add("maxmized", this.WindowState == WindowState.Maximized);
config.Add("windowWidth", this.Width);
config.Add("windowHeight", this.Height);
config.Add("windowLeft", this.Left);
config.Add("windowTop", this.Top);
// 面板显示状态
config.Add("serialPortConfigPanelVisible", serialPortConfigPanel.Visibility == Visibility.Visible);
config.Add("autoSendConfigPanelVisible", autoSendConfigPanel.Visibility == Visibility.Visible);
config.Add("serialCommunicationConfigPanelVisible", serialCommunicationConfigPanel.Visibility == Visibility.Visible);
// 保存接收模式
config.Add("receiveMode", receiveMode);
config.Add("showReceiveData", showReceiveData);
// 保存发送模式
config.Add("sendMode", sendMode);
// 保存发送追加
config.Add("appendContent", appendContent);
// 保存配置信息到磁盘中
Configuration.Save(config, @"Config\default.conf");
}
/// <summary>
/// 将波特率列表添加进去
/// </summary>
/// <param name="conf"></param>
private void AddBaudRate(Configuration conf)
{
conf.Add("baudRate", baudRateComboBox.Text);
}
/// <summary>
/// 加载配置信息
/// </summary>
private bool LoadConfig()
{
Configuration config = Configuration.Read(@"Config\default.conf");
if (config == null)
{
return false;
}
// 获取波特率
string baudRateStr = config.GetString("baudRate");
baudRateComboBox.Text = baudRateStr;
// 获取奇偶校验位
int parityIndex = config.GetInt("parity");
parityComboBox.SelectedIndex = parityIndex;
// 获取数据位
int dataBitsIndex = config.GetInt("dataBits");
dataBitsComboBox.SelectedIndex = dataBitsIndex;
// 获取停止位
int stopBitsIndex = config.GetInt("stopBits");
stopBitsComboBox.SelectedIndex = stopBitsIndex;
// 获取编码
int encodingIndex = config.GetInt("encoding");
encodingComboBox.SelectedIndex = encodingIndex;
// 获取发送区内容
string sendDataText = config.GetString("sendDataTextBoxText");
sendDataTextBox.Text = sendDataText;
// 获取自动发送数据时间间隔
string interval = config.GetString("autoSendDataInterval");
int timeUnitIndex = config.GetInt("timeUnit");
autoSendIntervalTextBox.Text = interval;
timeUnitComboBox.SelectedIndex = timeUnitIndex;
// 窗口状态
if (config.GetBool("maxmized"))
{
this.WindowState = WindowState.Maximized;
}
double width = config.GetDouble("windowWidth");
double height = config.GetDouble("windowHeight");
double top = config.GetDouble("windowTop");
double left = config.GetDouble("windowLeft");
this.Width = width;
this.Height = height;
this.Top = top;
this.Left = left;
// 面板显示状态
if (config.GetBool("serialPortConfigPanelVisible"))
{
serialSettingViewMenuItem.IsChecked = true;
serialPortConfigPanel.Visibility = Visibility.Visible;
}
else
{
serialSettingViewMenuItem.IsChecked = false;
serialPortConfigPanel.Visibility = Visibility.Collapsed;
}
if (config.GetBool("autoSendConfigPanelVisible"))
{
autoSendDataSettingViewMenuItem.IsChecked = true;
autoSendConfigPanel.Visibility = Visibility.Visible;
}
else
{
autoSendDataSettingViewMenuItem.IsChecked = false;
autoSendConfigPanel.Visibility = Visibility.Collapsed;
}
if (config.GetBool("serialCommunicationConfigPanelVisible"))
{
serialCommunicationSettingViewMenuItem.IsChecked = true;
serialCommunicationConfigPanel.Visibility = Visibility.Visible;
}
else
{
serialCommunicationSettingViewMenuItem.IsChecked = false;
serialCommunicationConfigPanel.Visibility = Visibility.Collapsed;
}
// 加载接收模式
receiveMode = (ReceiveMode)config.GetInt("receiveMode");
switch (receiveMode)
{
case ReceiveMode.Character:
recvCharacterRadioButton.IsChecked = true;
break;
case ReceiveMode.Hex:
recvHexRadioButton.IsChecked = true;
break;
case ReceiveMode.Decimal:
recvDecRadioButton.IsChecked = true;
break;
case ReceiveMode.Octal:
recvOctRadioButton.IsChecked = true;
break;
case ReceiveMode.Binary:
recvBinRadioButton.IsChecked = true;
break;
default:
break;
}
showReceiveData = config.GetBool("showReceiveData");
showRecvDataCheckBox.IsChecked = showReceiveData;
// 加载发送模式
sendMode = (SendMode)config.GetInt("sendMode");
switch (sendMode)
{
case SendMode.Character:
sendCharacterRadioButton.IsChecked = true;
break;
case SendMode.Hex:
sendHexRadioButton.IsChecked = true;
break;
default:
break;
}
//加载追加内容
appendContent = config.GetString("appendContent");
switch (appendContent)
{
case "":
appendNoneRadioButton.IsChecked = true;
break;
case "\r":
appendReturnRadioButton.IsChecked = true;
break;
case "\n":
appednNewLineRadioButton.IsChecked = true;
break;
case "\r\n":
appendReturnNewLineRadioButton.IsChecked = true;
break;
default:
break;
}
return true;
}
#endregion
}
}
@@ -0,0 +1,522 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
namespace WPFSerialAssistant
{
public enum ReceiveMode
{
Character, //字符显示
Hex, //十六进制
Decimal, //十进制
Octal, //八进制
Binary //二进制
}
public enum SendMode
{
Character, //字符
Hex //十六进制
}
public partial class MainWindow : Window
{
#region Global
// 接收并显示的方式
private ReceiveMode receiveMode = ReceiveMode.Character;
// 发送的方式
private SendMode sendMode = SendMode.Character;
#endregion
#region Event handler for menu items
private void saveSerialDataMenuItem_Click(object sender, RoutedEventArgs e)
{
}
private void saveConfigMenuItem_Click(object sender, RoutedEventArgs e)
{
SaveConfig();
// 状态栏显示保存成功
Information("配置信息保存成功。");
}
private void loadConfigMenuItem_Click(object sender, RoutedEventArgs e)
{
LoadConfig();
// 状态栏显示加载成功
Information("配置信息加载成功。");
}
private void exitMenuItem_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void serialSettingViewMenuItem_Click(object sender, RoutedEventArgs e)
{
bool state = serialSettingViewMenuItem.IsChecked;
if (state == false)
{
serialPortConfigPanel.Visibility = Visibility.Visible;
}
else
{
serialPortConfigPanel.Visibility = Visibility.Collapsed;
if (IsCompactViewMode())
{
serialPortConfigPanel.Visibility = Visibility.Visible;
EnterCompactViewMode();
}
}
serialSettingViewMenuItem.IsChecked = !state;
}
private void autoSendDataSettingViewMenuItem_Click(object sender, RoutedEventArgs e)
{
bool state = autoSendDataSettingViewMenuItem.IsChecked;
if (state == false)
{
autoSendConfigPanel.Visibility = Visibility.Visible;
}
else
{
autoSendConfigPanel.Visibility = Visibility.Collapsed;
if (IsCompactViewMode())
{
autoSendConfigPanel.Visibility = Visibility.Visible;
EnterCompactViewMode();
}
}
autoSendDataSettingViewMenuItem.IsChecked = !state;
}
private void serialCommunicationSettingViewMenuItem_Click(object sender, RoutedEventArgs e)
{
bool state = serialCommunicationSettingViewMenuItem.IsChecked;
if (state == false)
{
serialCommunicationConfigPanel.Visibility = Visibility.Visible;
}
else
{
serialCommunicationConfigPanel.Visibility = Visibility.Collapsed;
if (IsCompactViewMode())
{
serialCommunicationConfigPanel.Visibility = Visibility.Visible;
EnterCompactViewMode();
}
}
serialCommunicationSettingViewMenuItem.IsChecked = !state;
}
private void compactViewMenuItem_Click(object sender, RoutedEventArgs e)
{
if (IsCompactViewMode())
{
RestoreViewMode();
}
else
{
EnterCompactViewMode();
}
}
private void aboutMenuItem_Click(object sender, RoutedEventArgs e)
{
WPFSerialAssistant.About about = new About();
about.ShowDialog();
}
private void helpMenuItem_Click(object sender, RoutedEventArgs e)
{
}
#endregion
#region Event handler for buttons and so on.
private void openClosePortButton_Click(object sender, RoutedEventArgs e)
{
if (serialPort.IsOpen)
{
if (ClosePort())
{
openClosePortButton.Content = "打开";
}
}
else
{
if (OpenPort())
{
openClosePortButton.Content = "关闭";
}
}
}
private void findPortButton_Click(object sender, RoutedEventArgs e)
{
FindPorts();
}
private void autoSendEnableCheckBox_Click(object sender, RoutedEventArgs e)
{
if (autoSendEnableCheckBox.IsChecked == true)
{
Information(string.Format("使能串口自动发送功能,发送间隔:{0} {1}。", autoSendIntervalTextBox.Text, timeUnitComboBox.Text.Trim()));
}
else
{
Information("禁用串口自动发送功能。");
StopAutoSendDataTimer();
progressBar.Visibility = Visibility.Collapsed;
}
}
private void sendDataButton_Click(object sender, RoutedEventArgs e)
{
if (autoSendEnableCheckBox.IsChecked == true)
{
AutoSendData();
}
else
{
SendData();
}
}
private void saveRecvDataButton_Click(object sender, RoutedEventArgs e)
{
SaveData(GetSaveDataPath());
}
private void clearRecvDataBoxButton_Click(object sender, RoutedEventArgs e)
{
recvDataRichTextBox.Document.Blocks.Clear();
}
private void recvModeButton_Checked(object sender, RoutedEventArgs e)
{
RadioButton rb = sender as RadioButton;
if (recvDataRichTextBox == null)
{
return;
}
if (rb != null)
{
//
// TO-DO:
// 可以将已经存在在文本框中的内容全部转换成指定形式显示,而不是简单地清空
//
recvDataRichTextBox.Document.Blocks.Clear();
switch (rb.Tag.ToString())
{
case "char":
receiveMode = ReceiveMode.Character;
Information("提示:字符显示模式。");
break;
case "hex":
receiveMode = ReceiveMode.Hex;
Information("提示:十六进制显示模式。");
break;
case "dec":
receiveMode = ReceiveMode.Decimal;
Information("提示:十进制显示模式。");
break;
case "oct":
receiveMode = ReceiveMode.Octal;
Information("提示:八进制显示模式。");
break;
case "bin":
receiveMode = ReceiveMode.Binary;
Information("提示:二进制显示模式。");
break;
default:
break;
}
}
}
private bool showReceiveData = true;
private void showRecvDataCheckBox_Click(object sender, RoutedEventArgs e)
{
showReceiveData = (bool)showRecvDataCheckBox.IsChecked;
}
private void sendDataModeRadioButton_Click(object sender, RoutedEventArgs e)
{
RadioButton rb = sender as RadioButton;
if (rb != null)
{
switch (rb.Tag.ToString())
{
case "char":
sendMode = SendMode.Character;
Information("提示:发送字符文本。");
// 将文本框中内容转换成char
sendDataTextBox.Text = Utilities.ToSpecifiedText(sendDataTextBox.Text, SendMode.Character, serialPort.Encoding);
break;
case "hex":
// 将文本框中的内容转换成hex
sendMode = SendMode.Hex;
Information("提示:发送十六进制。输入十六进制数据之间用空格隔开,如:1D 2A 38。");
sendDataTextBox.Text = Utilities.ToSpecifiedText(sendDataTextBox.Text, SendMode.Hex, serialPort.Encoding);
break;
default:
break;
}
}
}
private void manualInputRadioButton_Click(object sender, RoutedEventArgs e)
{
}
private void loadFileRadioButton_Click(object sender, RoutedEventArgs e)
{
}
private void clearSendDataTextBox_Click(object sender, RoutedEventArgs e)
{
sendDataTextBox.Clear();
}
private void appendRadioButton_Click(object sender, RoutedEventArgs e)
{
RadioButton rb = sender as RadioButton;
if (rb != null)
{
switch (rb.Tag.ToString())
{
case "none":
appendContent = "";
break;
case "return":
appendContent = "\r";
break;
case "newline":
appendContent = "\n";
break;
case "retnewline":
appendContent = "\r\n";
break;
default:
break;
}
Information("发送追加:" + rb.Content.ToString());
}
}
#endregion
#region Event handler for timers
private void ClockTimer_Tick(object sender, EventArgs e)
{
UpdateTimeDate();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AutoSendDataTimer_Tick(object sender, EventArgs e)
{
bool ret = false;
ret = SendData();
if (ret == false)
{
StopAutoSendDataTimer();
}
}
/// <summary>
/// 窗口关闭前拦截
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 释放没有关闭的端口资源
if (serialPort.IsOpen)
{
ClosePort();
}
// 提示是否需要保存配置到文件中
if (MessageBox.Show("是否在退出前保存软件配置?", "小贴士", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes)
{
SaveConfig();
}
}
/// <summary>
/// 捕获窗口按键。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_KeyDown(object sender, KeyEventArgs e)
{
// Ctrl+S保存数据
if (e.Key == Key.S && e.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
{
SaveData(GetSaveDataPath());
}
// Ctrl+Enter 进入/退出简洁视图模式
if (e.Key == Key.Enter && e.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
{
if (IsCompactViewMode())
{
RestoreViewMode();
}
else
{
EnterCompactViewMode();
}
}
// Enter发送数据
if (e.Key == Key.Enter)
{
SendData();
}
}
#endregion
#region EventHandler for serialPort
// 数据接收缓冲区
private List<byte> receiveBuffer = new List<byte>();
// 一个阈值,当接收的字节数大于这么多字节数之后,就将当前的buffer内容交由数据处理的线程
// 分析。这里存在一个问题,假如最后一次传输之后,缓冲区并没有达到阈值字节数,那么可能就
// 没法启动数据处理的线程将最后一次传输的数据处理了。这里应当设定某种策略来保证数据能够
// 在尽可能短的时间内得到处理。
private const int THRESH_VALUE = 128;
private bool shouldClear = true;
/// <summary>
/// 更新:采用一个缓冲区,当有数据到达时,把字节读取出来暂存到缓冲区中,缓冲区到达定值
/// 时,在显示区显示数据即可。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.IO.Ports.SerialPort sp = sender as System.IO.Ports.SerialPort;
if (sp != null)
{
// 临时缓冲区将保存串口缓冲区的所有数据
int bytesToRead = sp.BytesToRead;
byte[] tempBuffer = new byte[bytesToRead];
// 将缓冲区所有字节读取出来
sp.Read(tempBuffer, 0, bytesToRead);
// 检查是否需要清空全局缓冲区先
if (shouldClear)
{
receiveBuffer.Clear();
shouldClear = false;
}
// 暂存缓冲区字节到全局缓冲区中等待处理
receiveBuffer.AddRange(tempBuffer);
if (receiveBuffer.Count >= THRESH_VALUE)
{
//Dispatcher.Invoke(new Action(() =>
//{
// recvDataRichTextBox.AppendText("Process data.\n");
//}));
// 进行数据处理,采用新的线程进行处理。
Thread dataHandler = new Thread(new ParameterizedThreadStart(ReceivedDataHandler));
dataHandler.Start(receiveBuffer);
}
// 启动定时器,防止因为一直没有到达缓冲区字节阈值,而导致接收到的数据一直留存在缓冲区中无法处理。
StartCheckTimer();
this.Dispatcher.Invoke(new Action(() =>
{
if (autoSendEnableCheckBox.IsChecked == false)
{
Information("");
}
dataRecvStatusBarItem.Visibility = Visibility.Visible;
}));
}
}
#endregion
#region
private void CheckTimer_Tick(object sender, EventArgs e)
{
// 触发了就把定时器关掉,防止重复触发。
StopCheckTimer();
// 只有没有到达阈值的情况下才会强制其启动新的线程处理缓冲区数据。
if (receiveBuffer.Count < THRESH_VALUE)
{
//recvDataRichTextBox.AppendText("Timeout!\n");
// 进行数据处理,采用新的线程进行处理。
Thread dataHandler = new Thread(new ParameterizedThreadStart(ReceivedDataHandler));
dataHandler.Start(receiveBuffer);
}
}
private void ReceivedDataHandler(object obj)
{
List<byte> recvBuffer = new List<byte>();
recvBuffer.AddRange((List<byte>)obj);
if (recvBuffer.Count == 0)
{
return;
}
// 必须应当保证全局缓冲区的数据能够被完整地备份出来,这样才能进行进一步的处理。
shouldClear = true;
this.Dispatcher.Invoke(new Action(() =>
{
if (showReceiveData)
{
// 根据显示模式显示接收到的字节.
recvDataRichTextBox.AppendText(Utilities.BytesToText(recvBuffer, receiveMode, serialPort.Encoding));
recvDataRichTextBox.ScrollToEnd();
}
dataRecvStatusBarItem.Visibility = Visibility.Collapsed;
}));
// TO-DO
// 处理数据,比如解析指令等等
}
#endregion
}
}
@@ -0,0 +1,749 @@
<Window
x:Class="WPFSerialAssistant.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="EF3串口解析助手 (Version 0.1)"
Width="1024"
Height="600"
MinWidth="1024"
MinHeight="550"
Closing="Window_Closing"
Icon="/icon/HexagonNew.ico"
PreviewKeyDown="Window_KeyDown"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<DockPanel Name="rootPanel">
<!-- 菜单栏 -->
<Menu
Name="menuBar"
Padding="2"
DockPanel.Dock="Top">
<!-- 文件 -->
<MenuItem Header="文件(_F)">
<MenuItem
Name="saveSerialDataMenuItem"
Click="saveSerialDataMenuItem_Click"
Header="保存(_S)"
Visibility="Collapsed" />
<MenuItem
Name="saveConfigMenuItem"
Click="saveConfigMenuItem_Click"
Header="保存配置" />
<MenuItem
Name="loadConfigMenuItem"
Click="loadConfigMenuItem_Click"
Header="加载配置(_L)"
Visibility="Collapsed" />
<MenuItem
Name="exitMenuItem"
Click="exitMenuItem_Click"
Header="退出" />
</MenuItem>
<!-- 设置 -->
<MenuItem Header="设置(_S)">
<MenuItem Header="发送追加">
<RadioButton
Name="appendNoneRadioButton"
Click="appendRadioButton_Click"
Content="无"
Tag="none" />
<RadioButton
Name="appendReturnRadioButton"
Click="appendRadioButton_Click"
Content="回车(\r)"
Tag="return" />
<RadioButton
Name="appednNewLineRadioButton"
Click="appendRadioButton_Click"
Content="新行(\n)"
IsChecked="True"
Tag="newline" />
<RadioButton
Name="appendReturnNewLineRadioButton"
Click="appendRadioButton_Click"
Content="回车新行(\r\n)"
Tag="retnewline" />
</MenuItem>
</MenuItem>
<!-- 视图 -->
<MenuItem Header="视图(_V)">
<MenuItem
Name="serialSettingViewMenuItem"
Click="serialSettingViewMenuItem_Click"
Header="串口配置面板"
IsChecked="True" />
<MenuItem
Name="autoSendDataSettingViewMenuItem"
Click="autoSendDataSettingViewMenuItem_Click"
Header="自动发送设置面板"
IsChecked="True" />
<MenuItem
Name="serialCommunicationSettingViewMenuItem"
Click="serialCommunicationSettingViewMenuItem_Click"
Header="通信设置面板"
IsChecked="True" />
<MenuItem
Name="compactViewMenuItem"
Click="compactViewMenuItem_Click"
Header="简洁视图"
IsChecked="False"
Visibility="Visible" />
</MenuItem>
<!-- 帮助 -->
<MenuItem Header="帮助(_H)">
<MenuItem
Name="aboutMenuItem"
Click="aboutMenuItem_Click"
Header="关于(_A)" />
<MenuItem
Name="helpMenuItem"
Click="helpMenuItem_Click"
Header="软件文档(_D)" />
</MenuItem>
</Menu>
<!-- 状态栏 -->
<StatusBar
Name="statusBar"
Padding="2"
Background="#FF007ACC"
DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Left">
<TextBlock Name="statusInfoTextBlock" Foreground="White">欢迎使用串口助手!</TextBlock>
</StatusBarItem>
<StatusBarItem>
<ProgressBar
Name="progressBar"
Width="150"
Height="10"
Margin="10,0,0,0"
Background="{Binding ElementName=statusBar, Path=Background}"
BorderBrush="White"
Foreground="White"
IsIndeterminate="True"
SmallChange="1"
Visibility="Collapsed"
Value="10" />
</StatusBarItem>
<StatusBarItem
Name="dataRecvStatusBarItem"
Margin="0,0,0,0"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal">
<Ellipse
Width="5"
Height="5"
Margin="0,0,5,0"
Fill="White" />
<Ellipse
Width="5"
Height="5"
Margin="0,0,5,0"
Fill="White" />
<Ellipse
Width="5"
Height="5"
Margin="0,0,5,0"
Fill="White" />
<Ellipse
Width="5"
Height="5"
Margin="0,0,5,0"
Fill="White" />
<Ellipse
Width="5"
Height="5"
Margin="0,0,5,0"
Fill="White" />
<Ellipse
Width="5"
Height="5"
Fill="White" />
</StackPanel>
</StatusBarItem>
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Name="timeDateTextBlock" Foreground="White" />
</StatusBarItem>
</StatusBar>
<Grid>
<DockPanel>
<!-- 串口配置面板 -->
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid DockPanel.Dock="Left" Visibility="Visible">
<GroupBox
Name="serialPortConfigPanel"
Margin="5,5,0,5"
BorderBrush="#FF7199E0"
BorderThickness="1"
Header="串口配置">
<DockPanel>
<!-- 可用端口 -->
<Grid Margin="0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="0,0,25,0"
VerticalAlignment="Center"
Text="可用端口" />
<ComboBox
Name="portsComboBox"
Grid.Column="1"
Width="120"
Padding="5"
IsEnabled="False" />
</Grid>
<!-- 通讯波特率 -->
<Grid Margin="0,0,0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="通讯波特率" />
<ComboBox
Name="baudRateComboBox"
Grid.Column="1"
Width="120"
Padding="5"
IsEditable="True">
<ComboBoxItem>1200</ComboBoxItem>
<ComboBoxItem>2400</ComboBoxItem>
<ComboBoxItem>4800</ComboBoxItem>
<ComboBoxItem>9600</ComboBoxItem>
<ComboBoxItem>19200</ComboBoxItem>
<ComboBoxItem>38400</ComboBoxItem>
<ComboBoxItem>115200</ComboBoxItem>
<ComboBoxItem IsSelected="True">256000</ComboBoxItem>
</ComboBox>
</Grid>
<!-- 奇偶校验位 -->
<Grid Margin="0,0,0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="奇偶校验位" />
<ComboBox
Name="parityComboBox"
Grid.Column="1"
Width="120"
Padding="5"
Text="无(None)">
<ComboBoxItem IsSelected="True" Tag="None">无(None)</ComboBoxItem>
<ComboBoxItem Tag="Even">偶校验(Even)</ComboBoxItem>
<ComboBoxItem Tag="Odd">奇校验(Odd)</ComboBoxItem>
<ComboBoxItem Tag="Space">保留为0(Space)</ComboBoxItem>
<ComboBoxItem Tag="Mark">保留为1(Mark)</ComboBoxItem>
</ComboBox>
</Grid>
<!-- 数据位 -->
<Grid Margin="0,0,0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="数据位" />
<ComboBox
Name="dataBitsComboBox"
Grid.Column="1"
Width="120"
Padding="5">
<ComboBoxItem IsSelected="True">8</ComboBoxItem>
<ComboBoxItem>7</ComboBoxItem>
<ComboBoxItem>6</ComboBoxItem>
<ComboBoxItem>5</ComboBoxItem>
</ComboBox>
</Grid>
<!-- 停止位 -->
<Grid Margin="0,0,0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="停止位" />
<ComboBox
Name="stopBitsComboBox"
Grid.Column="1"
Width="120"
Padding="5">
<ComboBoxItem IsSelected="True">1</ComboBoxItem>
<ComboBoxItem>1.5</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
</ComboBox>
</Grid>
<!-- 字节编码 -->
<Grid Margin="0,0,0,10" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="字节编码" />
<ComboBox
Name="encodingComboBox"
Grid.Column="1"
Width="120"
Padding="5">
<ComboBoxItem IsSelected="True">Default</ComboBoxItem>
<ComboBoxItem>ASCII</ComboBoxItem>
<ComboBoxItem>Unicode</ComboBoxItem>
<ComboBoxItem>UTF-8</ComboBoxItem>
</ComboBox>
</Grid>
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<Button
Name="openClosePortButton"
Grid.Column="0"
Margin="5"
Padding="3"
Click="openClosePortButton_Click"
IsEnabled="{Binding ElementName=portsComboBox, Path=IsEnabled}">
打开
</Button>
<Button
Name="findPortButton"
Grid.Column="1"
Margin="5"
Padding="3"
Click="findPortButton_Click">
查找
</Button>
</Grid>
<!-- 空的Grid,占位用 -->
<Grid>
<StackPanel Margin="5">
<TextBlock
Padding="5"
FontSize="16"
Foreground="#FF0078D7">
主要功能:
</TextBlock>
<TextBlock Padding="5">1. 可自由显示、隐藏的设置面板</TextBlock>
<TextBlock Padding="5">2. 简洁视图模式,专注于数据收发</TextBlock>
<TextBlock Padding="5">3. 软件主要配置可保存并恢复</TextBlock>
<TextBlock
Padding="5"
FontSize="16"
Foreground="#FF0078D7">
快捷键提示:
</TextBlock>
<TextBlock Padding="5">1. Ctrl+S: 保存接收区数据</TextBlock>
<TextBlock Padding="5">2. Ctrl+Enter: 进/退简洁视图模式</TextBlock>
<TextBlock Padding="5">3. Enter: 发送数据</TextBlock>
</StackPanel>
</Grid>
</DockPanel>
</GroupBox>
</Grid>
</ScrollViewer>
<!-- EF3模式设置 -->
<Grid
Name="EF3ConfigPanel"
Margin="0,5,5,5"
DockPanel.Dock="Right"
Visibility="Visible">
<GroupBox
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="EF3板设置">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*" />
<RowDefinition Height="80*" />
</Grid.RowDefinitions>
<!-- EF3锁存模式 -->
<GroupBox
Grid.Row="0"
Margin="5"
Header="锁存模式">
<StackPanel>
<RadioButton
Name="singleLatch"
Margin="5"
Checked="SingleLatch_Checked"
IsChecked="True"
Tag="char"
ToolTip="点击即锁存一次并读取一次">
单次锁存
</RadioButton>
<RadioButton
Name="timingLatches"
Margin="5"
Checked="TimingLatches_Checked"
Tag="hex"
ToolTip="定时模式,每10ms(1000*10us),即100HZ,锁存一次位置的值,发一个脉冲">
定时锁存
</RadioButton>
<ComboBox
Name="timeRateComboBox"
Grid.Column="1"
Width="100"
Margin="5"
Padding="5"
IsEditable="True">
<ComboBoxItem IsSelected="True">1000</ComboBoxItem>
<ComboBoxItem>2000</ComboBoxItem>
</ComboBox>
<RadioButton
Name="freDivsionRadioButton"
Margin="5"
Checked="FreDivsionRadioButton_Checked"
Tag="dec"
ToolTip="设置成分频锁存模式。以X轴为基准,每20个脉冲锁存一次,并发送一次脉冲">
分频锁存
</RadioButton>
<ComboBox
Name="freRateComboBox"
Grid.Column="1"
Width="100"
Margin="5"
Padding="5"
IsEditable="True">
<ComboBoxItem IsSelected="True">20</ComboBoxItem>
<ComboBoxItem>100</ComboBoxItem>
</ComboBox>
</StackPanel>
</GroupBox>
<!-- EF3功能按键 -->
<GroupBox
Grid.Row="1"
Margin="5"
Header="功能按键">
<StackPanel>
<Button
Name="clear5"
Margin="5"
Padding="3"
Content="获取EF3固件版本" />
<Button
Name="clear4"
Margin="5"
Padding="3"
Content="设置零点" />
<Button
Name="clear"
Margin="5"
Padding="3"
Content="开始锁存" />
<Button
Name="clear33"
Margin="5"
Padding="3"
Content="停止锁存" />
<Button
Name="clear34"
Margin="5"
Padding="3"
Content="读取锁存值" />
<Button
Name="clear2"
Margin="5"
Padding="3"
Content="清除flash" />
<Button
Name="clear3"
Margin="5"
Padding="3"
Content="获取锁存点的数量" />
</StackPanel>
</GroupBox>
</Grid>
</GroupBox>
</Grid>
<!-- 通讯设置面板 -->
<Grid
Name="serialCommunicationConfigPanel"
Margin="0,5,5,5"
DockPanel.Dock="Right"
Visibility="Visible">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<GroupBox
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="通信设置">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="12*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<GroupBox
Grid.Row="0"
Margin="5"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="接收设置">
<StackPanel>
<GroupBox
Margin="5"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="显示方式">
<StackPanel>
<RadioButton
Name="recvCharacterRadioButton"
Margin="0,5"
Checked="recvModeButton_Checked"
IsChecked="True"
Tag="char">
字符串
</RadioButton>
<RadioButton
Name="recvHexRadioButton"
Margin="0,0,50,5"
Checked="recvModeButton_Checked"
Tag="hex">
十六进制
</RadioButton>
<RadioButton
Name="recvDecRadioButton"
Margin="0,0,50,5"
Checked="recvModeButton_Checked"
Tag="dec">
十进制
</RadioButton>
<RadioButton
Name="recvOctRadioButton"
Margin="0,0,50,5"
Checked="recvModeButton_Checked"
Tag="oct">
八进制
</RadioButton>
<RadioButton
Name="recvBinRadioButton"
Margin="0,0,50,5"
Checked="recvModeButton_Checked"
Tag="bin">
二进制
</RadioButton>
</StackPanel>
</GroupBox>
<StackPanel>
<CheckBox
Name="showRecvDataCheckBox"
Margin="10,5"
Padding="0"
Click="showRecvDataCheckBox_Click"
Content="显示接收数据"
IsChecked="True" />
<Button
Name="saveRecvDataButton"
Margin="10,5"
Padding="3"
Click="saveRecvDataButton_Click"
Content="保存接收区数据"
ToolTip="使用Ctrl+S也可以快捷保存哦!" />
<Button
Name="clearRecvDataBoxButton"
Margin="10,5"
Padding="3"
Click="clearRecvDataBoxButton_Click"
Content="清空接收区" />
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox
Grid.Row="1"
Margin="5"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="发送设置">
<StackPanel>
<GroupBox
Margin="5,5,5,0"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="发送方式">
<StackPanel>
<RadioButton
Name="sendCharacterRadioButton"
Margin="0,5"
Click="sendDataModeRadioButton_Click"
Content="字符串"
IsChecked="True"
Tag="char" />
<RadioButton
Name="sendHexRadioButton"
Margin="0,0,0,5"
Click="sendDataModeRadioButton_Click"
Content="十六进制"
Tag="hex" />
</StackPanel>
</GroupBox>
<GroupBox
Margin="5"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="发送内容">
<StackPanel>
<RadioButton
Name="manualInputRadioButton"
Margin="0,5"
Click="manualInputRadioButton_Click"
Content="手动输入"
IsChecked="True" />
<RadioButton
Name="loadFileRadioButton"
Margin="0,0,0,5"
Click="loadFileRadioButton_Click"
Content="加载文件"
IsEnabled="False" />
</StackPanel>
</GroupBox>
<Button
Name="clearSendDataTextBox"
Margin="10,5,10,0"
Padding="3"
Click="clearSendDataTextBox_Click"
Content="清空发送区" />
</StackPanel>
</GroupBox>
</Grid>
</GroupBox>
</ScrollViewer>
</Grid>
<!-- 自动发送设置面板 -->
<Grid
Name="autoSendConfigPanel"
Margin="5"
DockPanel.Dock="Bottom"
Visibility="Visible">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<GroupBox
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="自动发送设置面板">
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock
Grid.Column="0"
Padding="5"
TextAlignment="Center">
自动发送时间间隔
</TextBlock>
<TextBox
Name="autoSendIntervalTextBox"
Grid.Column="1"
Width="100"
Margin="10,0"
Padding="5"
Text="1000"
ToolTip="自动发送时间只有在启动自动发送前修改有效;发送过程中修改时间只会在下一次启动自动发送时生效。" />
<ComboBox
Name="timeUnitComboBox"
Grid.Column="2"
Width="60"
Padding="5">
<ComboBoxItem IsSelected="True">毫秒</ComboBoxItem>
<ComboBoxItem>秒钟</ComboBoxItem>
<ComboBoxItem>分钟</ComboBoxItem>
</ComboBox>
</StackPanel>
<CheckBox
Name="autoSendEnableCheckBox"
Grid.Column="1"
Margin="0,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="autoSendEnableCheckBox_Click"
ToolTip="使能后,必须要再次点击发送按钮才可以启动自动发送。">
使能自动发送
</CheckBox>
</Grid>
</GroupBox>
</ScrollViewer>
</Grid>
<!-- 接收区/发送区 -->
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="85*" />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<GroupBox
Grid.Row="0"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="接收区">
<RichTextBox
Name="recvDataRichTextBox"
Margin="2,5"
BorderThickness="0"
IsReadOnly="True">
<FlowDocument
FontFamily="Lucida Console"
FontSize="14"
LineHeight="1.5" />
</RichTextBox>
</GroupBox>
<GroupBox
Grid.Row="1"
BorderBrush="{Binding ElementName=serialPortConfigPanel, Path=BorderBrush}"
BorderThickness="{Binding ElementName=serialPortConfigPanel, Path=BorderThickness}"
Header="发送区"
IsEnabled="{Binding ElementName=portsComboBox, Path=IsEnabled}">
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<TextBox
Name="sendDataTextBox"
Grid.Column="0"
Margin="5,0,0,0"
Padding="15"
VerticalAlignment="Center"
FontFamily="Lucida Console">
Hello, world!
</TextBox>
<Button
Name="sendDataButton"
Grid.Column="1"
Margin="5,0"
Padding="3"
VerticalAlignment="Center"
Click="sendDataButton_Click"
Content="发送" />
<Grid />
</Grid>
</GroupBox>
</Grid>
</DockPanel>
</Grid>
</DockPanel>
</Window>
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPFSerialAssistant
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitCore();
}
// 单次锁存
private void SingleLatch_Checked(object sender, RoutedEventArgs e)
{
}
//定时锁存
private void TimingLatches_Checked(object sender, RoutedEventArgs e)
{
}
//分频锁存
private void FreDivsionRadioButton_Checked(object sender, RoutedEventArgs e)
{
}
}
}
@@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("WPFSerialAssistant")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WPFSerialAssistant")]
[assembly: AssemblyCopyright("Copyright © Hexagon HMQ 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请
//<PropertyGroup> 中的 .csproj 文件中
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
//对以下 NeutralResourceLanguage 特性的注释。 更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
//(当资源未在页面
//或应用程序资源字典中找到时使用)
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
//(当资源未在页面
//、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace WPFSerialAssistant.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WPFSerialAssistant.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性
/// 重写当前线程的 CurrentUICulture 属性。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
/// </summary>
internal static System.Drawing.Icon HexagonNew {
get {
object obj = ResourceManager.GetObject("HexagonNew", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="HexagonNew" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\icon\HexagonNew.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace WPFSerialAssistant.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.IO;
using System.Windows.Documents;
using Microsoft.Win32;
namespace WPFSerialAssistant
{
public partial class MainWindow : Window
{
private string GetSaveDataPath()
{
string path = @"data.txt";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "选择存储数据的路径...";
sfd.FileName = string.Format("数据{0}", DateTime.Now.ToString("yyyyMdHHMMss"));
sfd.Filter = "文本文件|*.txt";
if (sfd.ShowDialog() == true)
{
path = sfd.FileName;
}
return path;
}
private void SaveData(string path)
{
try
{
using (System.IO.StreamWriter sr = new StreamWriter(path))
{
string text = (new TextRange(recvDataRichTextBox.Document.ContentStart, recvDataRichTextBox.Document.ContentEnd)).Text;
sr.Write(text);
Information(string.Format("成功保存数据到{0}", path));
}
}
catch (Exception ex)
{
Alert(ex.Message);
}
}
}
}
@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WPFSerialAssistant
{
public partial class MainWindow : Window
{
/// <summary>
/// SerialPort对象
/// </summary>
private SerialPort serialPort = new SerialPort();
// 需要一个定时器用来,用来保证即使缓冲区没满也能够及时将数据处理掉,防止一直没有到达
// 阈值,而导致数据在缓冲区中一直得不到合适的处理。
private DispatcherTimer checkTimer = new DispatcherTimer();
private void InitSerialPort()
{
serialPort.DataReceived += SerialPort_DataReceived;
InitCheckTimer();
}
/// <summary>
/// 查找端口
/// </summary>
private void FindPorts()
{
portsComboBox.ItemsSource = SerialPort.GetPortNames();
if (portsComboBox.Items.Count > 0)
{
portsComboBox.SelectedIndex = 0;
portsComboBox.IsEnabled = true;
Information(string.Format("查找到可以使用的端口{0}个。", portsComboBox.Items.Count.ToString()));
}
else
{
portsComboBox.IsEnabled = false;
Alert("Oops,没有查找到可用端口;您可以点击“查找”按钮手动查找。");
}
}
private bool OpenPort()
{
bool flag = false;
ConfigurePort();
try
{
serialPort.Open();
serialPort.DiscardInBuffer();
serialPort.DiscardOutBuffer();
Information(string.Format("成功打开端口{0}, 波特率{1}。", serialPort.PortName, serialPort.BaudRate.ToString()));
flag = true;
}
catch (Exception ex)
{
Alert(ex.Message);
}
return flag;
}
private bool ClosePort()
{
bool flag = false;
try
{
StopAutoSendDataTimer();
progressBar.Visibility = Visibility.Collapsed;
serialPort.Close();
Information(string.Format("成功关闭端口{0}。", serialPort.PortName));
flag = true;
}
catch (Exception ex)
{
Alert(ex.Message);
}
return flag;
}
private void ConfigurePort()
{
serialPort.PortName = GetSelectedPortName();
serialPort.BaudRate = GetSelectedBaudRate();
serialPort.Parity = GetSelectedParity();
serialPort.DataBits = GetSelectedDataBits();
serialPort.StopBits = GetSelectedStopBits();
serialPort.Encoding = GetSelectedEncoding();
}
private string GetSelectedPortName()
{
return portsComboBox.Text;
}
private int GetSelectedBaudRate()
{
int baudRate = 9600;
//string conv = baudRateComboBox.Text;
int.TryParse(baudRateComboBox.Text, out baudRate);
return baudRate;
}
private Parity GetSelectedParity()
{
string select = parityComboBox.Text;
Parity p = Parity.None;
if (select.Contains("Odd"))
{
p = Parity.Odd;
}
else if (select.Contains("Even"))
{
p = Parity.Even;
}
else if (select.Contains("Space"))
{
p = Parity.Space;
}
else if (select.Contains("Mark"))
{
p = Parity.Mark;
}
return p;
}
private int GetSelectedDataBits()
{
int dataBits = 8;
int.TryParse(dataBitsComboBox.Text, out dataBits);
return dataBits;
}
private StopBits GetSelectedStopBits()
{
StopBits stopBits = StopBits.None;
string select = stopBitsComboBox.Text.Trim();
if (select.Equals("1"))
{
stopBits = StopBits.One;
}
else if (select.Equals("1.5"))
{
stopBits = StopBits.OnePointFive;
}
else if (select.Equals("2"))
{
stopBits = StopBits.Two;
}
return stopBits;
}
private Encoding GetSelectedEncoding()
{
string select = encodingComboBox.Text;
Encoding enc = Encoding.Default;
if (select.Contains("UTF-8"))
{
enc = Encoding.UTF8;
}
else if (select.Contains("ASCII"))
{
enc = Encoding.ASCII;
}
else if (select.Contains("Unicode"))
{
enc = Encoding.Unicode;
}
return enc;
}
private bool SerialPortWrite(string textData)
{
SerialPortWrite(textData, false);
return false;
}
private string appendContent = "\n";
private bool SerialPortWrite(string textData, bool reportEnable)
{
if (serialPort == null)
{
return false;
}
if (serialPort.IsOpen == false)
{
Alert("串口未打开,无法发送数据。");
return false;
}
try
{
//serialPort.DiscardOutBuffer();
//serialPort.DiscardInBuffer();
if (sendMode == SendMode.Character)
{
serialPort.Write(textData + appendContent);
}
else if (sendMode == SendMode.Hex)
{
string[] grp = textData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
List<byte> list = new List<byte>();
foreach (var item in grp)
{
list.Add(Convert.ToByte(item, 16));
}
serialPort.Write(list.ToArray(), 0, list.Count);
}
if (reportEnable)
{
// 报告发送成功的消息,提示用户。
Information(string.Format("成功发送:{0}。", textData));
}
}
catch (Exception ex)
{
Alert(ex.Message);
return false;
}
return true;
}
#region
/// <summary>
/// 超时时间为50ms
/// </summary>
private const int TIMEOUT = 50;
private void InitCheckTimer()
{
// 如果缓冲区中有数据,并且定时时间达到前依然没有得到处理,将会自动触发处理函数。
checkTimer.Interval = new TimeSpan(0, 0, 0, 0, TIMEOUT);
checkTimer.IsEnabled = false;
checkTimer.Tick += CheckTimer_Tick;
}
private void StartCheckTimer()
{
checkTimer.IsEnabled = true;
checkTimer.Start();
}
private void StopCheckTimer()
{
checkTimer.IsEnabled = false;
checkTimer.Stop();
}
#endregion
}
}
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WPFSerialAssistant
{
public partial class MainWindow : Window
{
/// <summary>
/// 用于更新时间的定时器
/// </summary>
private DispatcherTimer clockTimer = new DispatcherTimer();
/// <summary>
/// 定时器初始化
/// </summary>
private void InitClockTimer()
{
clockTimer.Interval = new TimeSpan(0, 0, 1);
clockTimer.IsEnabled = true;
clockTimer.Tick += ClockTimer_Tick;
clockTimer.Start();
}
/// <summary>
/// 用于自动发送串口数据的定时器
/// </summary>
private DispatcherTimer autoSendDataTimer = new DispatcherTimer();
private void InitAutoSendDataTimer()
{
autoSendDataTimer.IsEnabled = false;
autoSendDataTimer.Tick += AutoSendDataTimer_Tick;
}
private void StartAutoSendDataTimer(int interval)
{
autoSendDataTimer.IsEnabled = true;
autoSendDataTimer.Interval = TimeSpan.FromMilliseconds(interval);
autoSendDataTimer.Start();
}
private void StopAutoSendDataTimer()
{
autoSendDataTimer.IsEnabled = false;
autoSendDataTimer.Stop();
}
}
}
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
namespace WPFSerialAssistant
{
public partial class MainWindow : Window
{
// 保存面板的显示状态
private Stack<Visibility> panelVisibilityStack = new Stack<Visibility>(3);
/// <summary>
/// 判断是否处于简洁视图模式
/// </summary>
/// <returns></returns>
private bool IsCompactViewMode()
{
if (autoSendConfigPanel.Visibility == Visibility.Collapsed &&
serialCommunicationConfigPanel.Visibility == Visibility.Collapsed &&
autoSendConfigPanel.Visibility == Visibility.Collapsed)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 进入简洁视图模式
/// </summary>
private void EnterCompactViewMode()
{
// 首先需要保持panel的显示状态
panelVisibilityStack.Push(serialPortConfigPanel.Visibility);
panelVisibilityStack.Push(autoSendConfigPanel.Visibility);
panelVisibilityStack.Push(serialCommunicationConfigPanel.Visibility);
// 进入简洁视图模式
serialPortConfigPanel.Visibility = Visibility.Collapsed;
autoSendConfigPanel.Visibility = Visibility.Collapsed;
serialCommunicationConfigPanel.Visibility = Visibility.Collapsed;
// 把对应的菜单项取消选中
serialSettingViewMenuItem.IsChecked = false;
autoSendDataSettingViewMenuItem.IsChecked = false;
serialCommunicationSettingViewMenuItem.IsChecked = false;
// 此时无法视图模式,必须恢复到原先的视图模式才可以
serialSettingViewMenuItem.IsEnabled = false;
autoSendDataSettingViewMenuItem.IsEnabled = false;
serialCommunicationSettingViewMenuItem.IsEnabled = false;
// 切换至简洁视图模式,菜单项选中
compactViewMenuItem.IsChecked = true;
//
Information("进入简洁视图模式。");
}
/// <summary>
/// 恢复到原来的视图模式
/// </summary>
private void RestoreViewMode()
{
// 恢复面板显示状态
serialCommunicationConfigPanel.Visibility = panelVisibilityStack.Pop();
autoSendConfigPanel.Visibility = panelVisibilityStack.Pop();
serialPortConfigPanel.Visibility = panelVisibilityStack.Pop();
// 恢复菜单选中状态
if (serialPortConfigPanel.Visibility == Visibility.Visible)
{
serialSettingViewMenuItem.IsChecked = true;
}
if (autoSendConfigPanel.Visibility == Visibility.Visible)
{
autoSendDataSettingViewMenuItem.IsChecked = true;
}
if (serialCommunicationConfigPanel.Visibility == Visibility.Visible)
{
serialCommunicationSettingViewMenuItem.IsChecked = true;
}
serialSettingViewMenuItem.IsEnabled = true;
autoSendDataSettingViewMenuItem.IsEnabled = true;
serialCommunicationSettingViewMenuItem.IsEnabled = true;
compactViewMenuItem.IsChecked = false;
//
Information("恢复原来的视图模式。");
}
}
}
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
//using System.Threading.Tasks;
namespace WPFSerialAssistant
{
public static class Utilities
{
public static double m_Resolution = 0.0001; //分辨率设置 0.1um
public static string BytesToText(List<byte> bytesBuffer, ReceiveMode mode, Encoding encoding)
{
string result = "";
{
switch (mode)
{
case ReceiveMode.Character: //
return encoding.GetString(bytesBuffer.ToArray<byte>());
case ReceiveMode.Hex: //十六进制
int totalBytes = bytesBuffer.ToArray<byte>().Length;
byte[] pointByteArray = bytesBuffer.ToArray<byte>();
result += "\n当前字节数: " + totalBytes.ToString() + ", 取整后点数: " + Math.Floor((double)totalBytes / 12) + "\n";
if (bytesBuffer.Count() == 2)
{
return "\n" +encoding.GetString(bytesBuffer.ToArray<byte>()) + "\n";
}
else if (bytesBuffer.Count() % 12 != 0)
{
foreach (var item in bytesBuffer)
{
result += Convert.ToString(item, 16).ToUpper() + " "; //普通显示
}
}
else //当返回字节数为 12倍数,认为XYZ坐标点
{
for (int i=0; i< Math.Floor((double)totalBytes/12); i++) //获得总点数
{
//每12个点一解析,循环
int tempX2 = pointByteArray[12 * i + 0] | pointByteArray[12 * i + 1] << 8 | pointByteArray[12 * i + 2] << 16 | pointByteArray[12 * i + 3] << 24;
int tempY2 = pointByteArray[12 * i + 4] | pointByteArray[12 * i + 5] << 8 | pointByteArray[12 * i + 6] << 16 | pointByteArray[12 * i + 7] << 24;
int tempZ2 = pointByteArray[12 * i + 8] | pointByteArray[12 * i + 9] << 8 | pointByteArray[12 * i + 10] << 16 | pointByteArray[12 * i + 11] << 24;
result += "Point: " + (i + 1).ToString()+"\t X: " + (tempX2 * m_Resolution).ToString() + "\t Y1: " + (tempY2 * m_Resolution).ToString() + "\t Y2: " + (tempZ2 * m_Resolution).ToString() + "\n";
}
}
break;
case ReceiveMode.Decimal: //十进制
foreach (var item in bytesBuffer)
{
result += Convert.ToString(item, 10) + " ";
}
break;
case ReceiveMode.Octal: //八进制
foreach (var item in bytesBuffer)
{
result += Convert.ToString(item, 8) + " ";
}
break;
case ReceiveMode.Binary: //二进制
foreach (var item in bytesBuffer)
{
result += Convert.ToString(item, 2) + " ";
}
break;
default:
break;
}
}
return result;
}
public static string ToSpecifiedText(string text, SendMode mode, Encoding encoding)
{
string result = "";
switch (mode)
{
case SendMode.Character:
text = text.Trim();
// 转换成字节
List<byte> src = new List<byte>();
string[] grp = text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in grp)
{
src.Add(Convert.ToByte(item, 16));
}
// 转换成字符串
result = encoding.GetString(src.ToArray<byte>());
break;
case SendMode.Hex:
byte[] byteStr = encoding.GetBytes(text.ToCharArray());
foreach (var item in byteStr)
{
result += Convert.ToString(item, 16).ToUpper() + " ";
}
break;
default:
break;
}
return result.Trim();
}
}
}
@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DA01B86D-5BC1-4863-BAAC-71B309B09CC0}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WPFSerialAssistant</RootNamespace>
<AssemblyName>WPFSerialAssistant</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.3.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>
</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>lib\Json\Net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="About.xaml.cs">
<DependentUpon>About.xaml</DependentUpon>
</Compile>
<Compile Include="Core.cs" />
<Compile Include="SAFile.cs" />
<Compile Include="SASerialPort.cs" />
<Compile Include="SATimer.cs" />
<Compile Include="SAViewMode.cs" />
<Compile Include="Utilities.cs" />
<Page Include="About.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="configuration\Configuration.cs" />
<Compile Include="EventHandler.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Resource Include="icon\1.ico" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.5 %28x86 和 x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Resource Include="icon\HexagonNew.ico" />
<Content Include="lib\Json\Net20\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Net20\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Net20\Newtonsoft.Json.xml" />
<Content Include="lib\Json\Net35\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Net35\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Net35\Newtonsoft.Json.xml" />
<Content Include="lib\Json\Net40\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Net40\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Net40\Newtonsoft.Json.xml" />
<Content Include="lib\Json\Net45\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Net45\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Net45\Newtonsoft.Json.xml" />
<Content Include="lib\Json\Portable40\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Portable40\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Portable40\Newtonsoft.Json.xml" />
<Content Include="lib\Json\Portable\Newtonsoft.Json.dll" />
<Content Include="lib\Json\Portable\Newtonsoft.Json.pdb" />
<Resource Include="lib\Json\Portable\Newtonsoft.Json.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
namespace WPFSerialAssistant
{
/// <summary>
/// 配置类
/// </summary>
class Configuration
{
#region
/// <summary>
/// 默认的配置文件存储路径
/// </summary>
private static readonly string defaultConfPath = @"Config\config.conf";
/// <summary>
/// 默认配置文件的存储编码格式
/// </summary>
private static readonly Encoding defaultConfEncoding = Encoding.Default;
/// <summary>
/// 存储配置信息的字典
/// </summary>
private Dictionary<string, object> configuration = null;
#endregion
private Dictionary<string, object> ConfigInfo
{
get
{
return configuration;
}
}
#region
/// <summary>
/// 构造函数
/// </summary>
public Configuration()
{
if (configuration == null)
{
configuration = new Dictionary<string, object>();
}
}
/// <summary>
/// 私有构造函数
/// </summary>
/// <param name="confDic"></param>
private Configuration(Dictionary<string, object> confDic)
{
if (confDic == null)
{
this.configuration = new Dictionary<string, object>();
}
else
{
this.configuration = confDic;
}
}
/// <summary>
/// 添加配置键值对
/// </summary>
/// <param name="key">键</param>
/// <param name="value">值</param>
public void Add(string key, object value)
{
configuration.Add(key, value);
}
/// <summary>
/// 移除配置信息键值对
/// </summary>
/// <param name="key">键</param>
public void Remove(string key)
{
configuration.Remove(key);
}
/// <summary>
/// 清除所有的配置信息键值对
/// </summary>
public void Clear()
{
configuration.Clear();
}
/// <summary>
/// 获取配置信息值
/// </summary>
/// <param name="key">键</param>
/// <returns></returns>
public object Get(string key)
{
return configuration[key];
}
/// <summary>
/// 获取string类型的配置信息
/// </summary>
/// <param name="key">键</param>
/// <returns>string类型的值</returns>
public string GetString(string key)
{
return Get(key).ToString();
}
/// <summary>
/// 获取int类型的配置信息
/// </summary>
/// <param name="key">键</param>
/// <returns>int类型的值</returns>
public int GetInt(string key)
{
return int.Parse(Get(key).ToString());
}
/// <summary>
/// 获取bool类型的配置信息
/// </summary>
/// <param name="key">键</param>
/// <returns>bool类型的值</returns>
public bool GetBool(string key)
{
return bool.Parse(Get(key).ToString());
}
/// <summary>
/// 获取double类型的配置信息
/// </summary>
/// <param name="key">键</param>
/// <returns>double类型的值</returns>
public double GetDouble(string key)
{
return double.Parse(Get(key).ToString());
}
/// <summary>
/// 获取decimal类型的配置信息
/// </summary>
/// <param name="key">键</param>
/// <returns>decimal类型的值</returns>
public decimal GetDecimal(string key)
{
return decimal.Parse(Get(key).ToString());
}
#endregion
#region
/// <summary>
/// 存储配置信息到默认的路径中
/// </summary>
/// <param name="conf"></param>
public static void Save(Configuration conf)
{
Save(conf, defaultConfPath);
}
/// <summary>
/// 存储配置信息到自定义路径
/// </summary>
/// <param name="conf"></param>
/// <param name="path">存储配置信息的路径</param>
public static void Save(Configuration conf, string path)
{
// 构建配置信息的json字符串
string confStr = Newtonsoft.Json.JsonConvert.SerializeObject(conf.ConfigInfo);
// 如果指定的配置路径不存在,则新建一个
if (System.IO.File.Exists(path) == false)
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
}
// 保存配置信息
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(path, false, defaultConfEncoding))
{
try
{
sw.Write(confStr);
}
catch (Exception ex)
{
throw ex;
}
}
}
/// <summary>
/// 从默认存储路径读取配置信息
/// </summary>
/// <returns>配置信息</returns>
public static Configuration Read()
{
return Read(defaultConfPath);
}
/// <summary>
/// 从自定义存储路径读取配置信息
/// </summary>
/// <param name="path">存储配置信息路径</param>
/// <returns>配置信息</returns>
public static Configuration Read(string path)
{
string confStr = "";
Configuration conf = null;
if (System.IO.File.Exists(path) == false)
{
return null;
}
// 读取配置信息
using (System.IO.StreamReader sr = new System.IO.StreamReader(path, defaultConfEncoding))
{
try
{
confStr = sr.ReadToEnd();
// 解析得到配置信息
Dictionary<string, object> confInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(confStr);
conf = new Configuration(confInfo);
}
catch (Exception ex)
{
throw ex;
}
}
return conf;
}
#endregion
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<config>
<source_groups>
<source_group_a170c5ff-c94e-42ce-8d92-5e1a6a461f15>
<build_file_path>
<compilation_db_path>compile_commands.json</compilation_db_path>
</build_file_path>
<indexed_header_paths>
<indexed_header_path>HSI_HexagonMI_EF3</indexed_header_path>
</indexed_header_paths>
<name>C/C++ from Compilation Database</name>
<pch_flags>
<use_compiler_flags>1</use_compiler_flags>
</pch_flags>
<status>enabled</status>
<type>C/C++ from Compilation Database</type>
</source_group_a170c5ff-c94e-42ce-8d92-5e1a6a461f15>
</source_groups>
<version>8</version>
</config>