前言
早在18年,博主第一次开始写博客,内容为 C# 上位机开发,写完后收获了不错的阅读量和好评:
- C#上位机开发(一)—— 了解上位机
- C#上位机开发(二)—— Hello,World
- C#上位机开发(三)—— 构建SerialAssistant雏形
- C#上位机开发(四)—— SerialAssistant功能完善
- C#上位机开发(五)——SerialAssistant界面升级(WinForm界面布局进阶)
在第五篇中优化了软件界面,在第三篇中实现了串口助手的基本功能,接下来再实现一些小功能:
- 串口自动扫描功能
- 接收数据保存功能
- 加载发送文件
- 发送数据历史记录
- 配置文件保存和加载
- 点击右下角linkLabel能打开浏览器功能
Github开源仓库:https://github.com/Mculover666/SerialAssistant。
一、串口自动扫描功能
这个功能需要使用定时器实现,在串口未打开的情况下,每隔2s扫描一次端口,并添加到串口列表中,当串口打开后,停止扫描。
1. 添加定时器
在设计器中拖动一个定时器控件:
2. 添加扫描串口逻辑
首先将扫描串口并添加到选择列表的功能编写为一个方法:
private bool search_port_is_exist(String item, String[] port_list)
{
for (int i = 0; i < port_list.Length; i++)
{
if (port_list[i].Equals(item))
{
return true;
}
}
return false;
}
/* 扫描串口列表并添加到选择框 */
private void Update_Serial_List()
{
try
{
/* 搜索串口 */
String[] cur_port_list = System.IO.Ports.SerialPort.GetPortNames();
/* 刷新串口列表comboBox */
int count = comboBox1.Items.Count;
if (count == 0)
{
//combox中无内容,将当前串口列表全部加入
comboBox1.Items.AddRange(cur_port_list);
return;
}
else
{
//combox中有内容
//判断有无新插入的串口
for (int i = 0; i < cur_port_list.Length; i++)
{
if (!comboBox1.Items.Contains(cur_port_list[i]))
{
//找到新插入串口,添加到combox中
comboBox1.Items.Add(cur_port_list[i]);
}
}
//判断有无拔掉的串口
for (int i = 0; i < count; i++)
{
if (!search_port_is_exist(comboBox1.Items[i].ToString(), cur_port_list))
{
//找到已被拔掉的串口,从combox中移除
comboBox1.Items.RemoveAt(i);
}
}
}
/* 如果当前选中项为空,则默认选择第一项 */
if (comboBox1.Items.Count > 0)
{
if (comboBox1.Text.Equals(""))
{
//软件刚启动时,列表项的文本值为空
comboBox1.Text = comboBox1.Items[0].ToString();
}
}
else
{
//无可用列表,清空文本值
comboBox1.Text = "";
}
}
catch (Exception ex)
{
//当下拉框被打开时,修改下拉框会发生异常
return;
}
}
然后双击定时器,进入定时器的超时回调函数,在其中调用串口扫描函数即可:
private void timer1_Tick(object sender, EventArgs e)
{
Update_Serial_List();
}
3. 定时器启动/停止控制
使用下面的代码即可设置定时器的时间为1s,并启动定时器:
timer1.Interval = 1000;
timer1.Start();
在软件启动时、用户点击关闭串口时,启动定时器即可。
停止定时器使用如下代码:
timer1.Stop();
在用户点击打开串口时,停止定时器。
二、接收数据保存功能
此功能需要使用C#的文件操作功能实现,参考教程:C# 文件IO操作。
1. 进入按钮回调函数
双击【保存日志】按钮,进入该函数回调函数:
2. 编写保存文件逻辑
因为串口接收区显示的都是ASCII文本信息,所以直接按文本形式保存为.txt文件即可。
private void button4_Click(object sender, EventArgs e)
{
DateTime time = new DateTime();
String fileName;
/* 获取当前接收区内容 */
String recv_data = textBox1.Text;
if (recv_data.Equals(""))
{
MessageBox.Show("接收数据为空,无需保存!");
return;
}
/* 获取当前时间,用于填充文件名 */
//eg.log_2021_05_08_10_13_31.txt
time = System.DateTime.Now;
fileName = "log" + "_" + time.ToString("yyyy_MM_dd_HH_mm_ss") + ".txt";
try
{
/* 保存串口接收区的内容 */
//创建 FileStream 类的实例
FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
//将字符串转换为字节数组
byte[] bytes = Encoding.UTF8.GetBytes(recv_data);
//向文件中写入字节数组
fileStream.Write(bytes, 0, bytes.Length);
//刷新缓冲区
fileStream.Flush();
//关闭流
fileStream.Close();
//提示用户
MessageBox.Show("日志已保存!(" + fileName + ")");
}
catch (Exception ex)
{
//提示用户
MessageBox.Show("发生异常!(" + ex.ToString() + ")");
}
}
三、加载发送文件
加载发送文件时需要分两步实现:
① 弹出文件选择框,供用户选择要加载的文件;
② 读取用户选择的文件中的内容,加载到发送框。
1. 用户文件选择框
参考教程:C# winform选择文件、选择文件夹、打开文件。
private void button3_Click(object sender, EventArgs e)
{
/* 弹出文件选择框供用户选择 */
OpenFileDialog dialog = new OpenFileDialog();
dialog.Multiselect = false;//该值确定是否可以选择多个文件
dialog.Title = "请选择要加载的文件(文本格式)";
dialog.Filter = "所有文件(*.*)|*.*";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string file = dialog.FileName;
textBox2.AppendText(file);
}
}
运行一下看以看到选择成功:
将用于调试的代码删除:
//textBox2.AppendText(file);
2. 加载用户选择的文件内容
这里我新建一个临时文件send_data.json,存放一段JSON格式的内容:
{
"MAC":"11:22:33:44:55:66",
"ID":12345
}
接着编写读取文件内容的代码:
private void button3_Click(object sender, EventArgs e)
{
string file;
/* 弹出文件选择框供用户选择 */
OpenFileDialog dialog = new OpenFileDialog();
dialog.Multiselect = false;//该值确定是否可以选择多个文件
dialog.Title = "请选择要加载的文件(文本格式)";
dialog.Filter = "文本文件(*.txt)|*.txt";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
file = dialog.FileName;
}
else
{
return;
}
/* 读取文件内容 */
try
{
//清空发送缓冲区
textBox2.Text = "";
// 使用 StreamReader 来读取文件
using (StreamReader sr = new StreamReader(file))
{
string line;
// 从文件读取并显示行,直到文件的末尾
while ((line = sr.ReadLine()) != null)
{
line = line + "\r\n";
textBox2.AppendText(line);
}
}
}
catch (Exception ex)
{
MessageBox.Show("加载文件发生异常!(" + ex.ToString()+")");
}
}
运行之后,选择文件:
加载成功:
四、发送历史记录
每次点击发送之后,将发送的内容添加到下方的combox中记录,方便下次直接选中。
1. 记录发送数据
记录发送数据的时候要先检查一下是否有重复,不要重复添加。
/* 记录发送数据 */
//先检查当前是否存在该项
if (comboBox7.Items.Contains(textBox2.Text) == true)
{
return;
}
else
{
comboBox7.Items.Add(textBox2.Text);
}
运行看看效果,多次发送相同的数据和不同的数据:
2. 选择历史数据
当用户选择某条历史数据时,将其加载到发送文本框中。
进入comboBox的回调函数中:
在选项更改时,添加逻辑:
private void comboBox7_SelectedIndexChanged(object sender, EventArgs e)
{
//清空发送缓冲区
textBox2.Text = comboBox7.SelectedItem.ToString();
}
五、配置文件保存和加载
六、linkLabel功能
1. 进入点击回调函数
可以看到系统生成的回调函数:
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
}
2. 编写回调函数
在回调函数中,调用系统浏览器访问链接:
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.linkLabel1.Links[this.linkLabel1.Links.IndexOf(e.Link)].Visited = true;
string targetUrl = "https://github.com/Mculover666/SerialAssistant";
try
{
//尝试用edge打开
System.Diagnostics.Process.Start("msedge.exe", targetUrl);
return;
}
catch (Exception)
{
//edge它不香吗
}
try
{
//好吧,那用chrome
System.Diagnostics.Process.Start("chrome.exe", targetUrl);
return;
}
catch
{
//chrome不好用吗
}
try
{
//IE也不是不可以
System.Diagnostics.Process.Start("iexplore.exe", targetUrl);
}
catch
{
//没救了,砸了吧!
}
}
七、定时发送功能
1. 添加定时器
双击进入定时器回调函数:
private void timer2_Tick(object sender, EventArgs e)
{
button5_Click(button5, new EventArgs()); //调用发送按钮回调函数
}
2. 定时时间的设置
3. 开启/关闭自动发送
需要提前处理好发送CheckBox的使能与失能逻辑。
进入checkBox的回调函数:
private void checkBox4_CheckedChanged(object sender, EventArgs e)
{
if (checkBox4.Checked)
{
//自动发送功能选中,开始自动发送
numericUpDown1.Enabled = false;
timer2.Interval = (int)numericUpDown1.Value;
timer2.Start();
}
else
{
//自动发送功能未选中,停止自动发送
numericUpDown1.Enabled = true;
timer2.Stop();
}
}
转载:https://blog.csdn.net/Mculover666/article/details/116518368