飞道的博客

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

219人阅读  评论(0)

前言

早在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();
     }
 }

转载:https://blog.csdn.net/Mculover666/article/details/116518368
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场