C#上位机开发——SerialAssistant功能优化(串口自动扫描功能接收数据保存功能加载发送文件发送历史记录打开浏览器功能定时发送功能)

Posted Mculover666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#上位机开发——SerialAssistant功能优化(串口自动扫描功能接收数据保存功能加载发送文件发送历史记录打开浏览器功能定时发送功能)相关的知识,希望对你有一定的参考价值。

前言

早在18年,博主第一次开始写博客,内容为 C# 上位机开发,写完后收获了不错的阅读量和好评:

在第五篇中优化了软件界面,在第三篇中实现了串口助手的基本功能,接下来再实现一些小功能:

  • 串口自动扫描功能
  • 接收数据保存功能
  • 加载发送文件
  • 发送数据历史记录
  • 配置文件保存和加载
  • 点击右下角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();
     }
 }

以上是关于C#上位机开发——SerialAssistant功能优化(串口自动扫描功能接收数据保存功能加载发送文件发送历史记录打开浏览器功能定时发送功能)的主要内容,如果未能解决你的问题,请参考以下文章

C#上位机开发—— 美化界面(给按钮添加背景)

C#上位机开发—— 修改窗口图标和exe文件图标

c#上位机开发

C#上位机开发

C# | 上位机开发新手指南摘要算法

用C#语言开发上位机(来控制下位机比如了解下位机测量的温度湿度压力并控制下位机)要用到啥知识?