using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace Serial_Assistant
{
public partial class Form1 : Form
{
private long receive_count = 0;                  //接收字节计数, 作用相当于全局变量
private long Send_count = 0;                    //发送字节计数, 作用相当于全局变量
private StringBuilder sb = new StringBuilder();  //声明一个全局变量 名称为sb 类型为StringBulider
private DateTime current_time = new DateTime();  //声明一个全局变量 名称为current_time 类型为DateTime
//窗体初始化
public Form1()
{
InitializeComponent();
}           
//窗体载入事件
private void Form1_Load(object sender, EventArgs e)
{
int i;
for (i = 300; i <= 38400; i = i * 2)
{
comboBox2.Items.Add(i.ToString());  //添加波特率列表
}
//批量添加波特率列表
/*string[] baud = { "43000", "56000", "57600", "115200", "128000", "230400", "256000", "460800" };
comboBox2.Items.AddRange(baud);*/
string[] wei = { "None", "Odd", "Even", "Mark", "Space" };
comboBox4.Items.AddRange(wei);
//设置默认值
comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames()); //自动获取当前串口
comboBox2.Text = "9600";
comboBox3.Text = "8";
comboBox4.Text = "None";
comboBox5.Text = "1";
label6.Text = "串口已关闭";
label6.ForeColor = Color.Red;
}
//打开/关闭串口按钮事件
private void button1_Click(object sender, EventArgs e)
{
try        //将可能产生异常的代码放置在try块中
{
if (serialPort1.IsOpen)  //如果串口已经处于打开状态,那么再次按下按钮时,关闭串口,并显示打开串口
{
label6.Text = "串口已关闭";
label6.ForeColor = Color.Green;
serialPort1.Close();
button1.Text = "打开串口";
button1.BackColor = Color.ForestGreen;
comboBox1.Enabled = true;
comboBox2.Enabled = true;
comboBox3.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;
textBox1.Text = "";    //清空接收区
textBox2.Text = "";    //清空发送区
}
else  //表示串口
关闭状态,
{
label6.Text = "串口已打开";
label6.ForeColor = Color.Red;
//第一步设置好参数
comboBox1.Enabled = false;
comboBox2.Enabled = false;
comboBox3.Enabled = false;
comboBox4.Enabled = false;
comboBox5.Enabled = false;
serialPort1.PortName = comboBox1.Text;  //获取串口
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);  //设置波特率
serialPort1.DataBits = Convert.ToInt16(comboBox3.Text);  //设置数据位
//设置奇偶校验位
if (comboBox4.Text.Equals("None"))                        //没有奇偶校验检查时发生
serialPort1.Parity = System.IO.Ports.Parity.None;
else if (comboBox4.Text.Equals("Odd"))                    // 设置奇校验位
serialPort1.Parity = System.IO.Ports.Parity.Odd;
else if (comboBox4.Text.Equals("Even"))                    //设置奇偶验位
serialPort1.Parity = System.IO.Ports.Parity.Even;
else if (comboBox4.Text.Equals("Mark"))                    //将奇偶校验位设置为 1
serialPort1.Parity = System.IO.Ports.Parity.Mark;
else if (comboBox4.Text.Equals("Space"))                    // 将奇偶校验位设置为 0
serialPort1.Parity = System.IO.Ports.Parity.Space;
//设置停止位
if (comboBox5.Text.Equals("1"))
serialPort1.StopBits = System.IO.Ports.StopBits.One;
else if (comboBox5.Text.Equals("1.5"))                          //1.5停止位,
serialPort1.StopBits = System.IO.Ports.StopBits.OnePointFive;
else if (comboBox5.Text.Equals("2"))
serialPort1.StopBits = System.IO.Ports.StopBits.Two;
//第二步:如果串口已经处于关闭状态,那么再次按下按钮时,打开串口,并显示关闭串口
serialPort1.Open();    //打开串口
button1.Text = "关闭串口";
button1.BackColor = Color.Firebrick;
}
}
catch (Exception ex)
{
//捕获可能发生的异常并进行处理
//捕获到异常,创建一个新的对象,之前的不可以再用
serialPort1 = new System.IO.Ports.SerialPort();
//刷新COM口选项
comboBox1.Items.Clear();
comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());
//响铃并显示异常给用
System.Media.SystemSounds.Beep.Play();
button1.Text = "打开串口";
button1.BackColor = Color.ForestGreen;
MessageBox.Show(ex.Message);
comboBox1.Enabled = true;
comboBox2.Enabled = true;
comboBox3.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;
}
}
//发送按钮事件
private void button2_Click(object sender, EventArgs e)
{
byte[] temp = new byte[1];
try
{
//首先判断串口是否开启
if (serialPort1.IsOpen)
{
int num = 0;  //获取本次发送字节数
/*我们希望发送的数据是0x31,所以功能应该被设计为在HEX发送模式下,用户输入“31”就应该发送0x31,这个不难,只需要将字符串每2个字符
* 提取一下,然后按16进制转化为一个byte类型的值,最后调用write(byte[] buffer,int offset,int count)将这一个字节数据发送就可以,
* 那么,当用户同时输入多个十六进制字符呢该符合发送呢?这个时候就需要用到正则表达式了,用户可以将输入的十六进制数据用任意多个空格
* 隔开,然后我们利用正则表达式匹配空格,并替换为“”,相当于删除掉空格,这样对整个字符串进行遍历,用刚才的方法逐个发送即可!*/
if (radioButton4.Checked)    //16进制发送情况     
{
string buf = textBox2.Text;    //将发送区文本赋值给buf字符串
string pattern = @"\s";
string replacement = "";
Regex rgx = new Regex(pattern);
string send_data = rgx.Replace(buf, replacement);    //当发送字符个数>2个的时候,将所有发送字符合并为一个字符串
num = (send_data.Length - send_data.Length % 2) / 2;  //send_data.Length:偶数,相当于send_data.Length / 2
//send_data.Length:奇数,相当于(send_data.Length -1)/ 2
for (int i = 0; i < num; i++)
{
temp[0] = Convert.ToByte(send_data.Substring(i * 2, 2), 16);  //将字符串以16进制形式,转换为表示为8位无符号整数
serialPort1.Write(temp, 0, 1);  //循环发送    从下标为0的的位置开始输出,总共输出1次,每次输出一个
}
//如果用户输入的字符是奇数,则单独处理,按照上面for循环后
,还有最后一个字符未处理
if (send_data.Length % 2 != 0)
{
//temp[0] = Convert.ToByte(send_data.Substring(send_data.Length - 1, 1), 16); //send_data.Length - 1为最后一个字符的位置,从该位置
temp[0] = Convert.ToByte(send_data.Substring(textBox2.Text.Length - 1, 1), 16);
serialPort1.Write(temp, 0, 1);
num++;
}
}
else            //ASCII码发送情况
{
serialPort1.Write(textBox2.Text);
num = textBox2.Text.Length; 
}
Send_count += num;      //计数变量累加
label8.Text = "Tx:" + Send_count.ToString() + "Bytes";  //刷新界面
}
}
catch (Exception ex)
{
//捕获到异常,创建一个新的对象,之前的不可以再用
serialPort1 = new System.IO.Ports.SerialPort();
//刷新COM口选项
comboBox1.Items.Clear();
comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());
//响铃并显示异常给用户
System.Media.SystemSounds.Beep.Play();
button1.Text = "打开串口";
button1.BackColor = Color.ForestGreen;
MessageBox.Show(ex.Message);
comboBox1.Enabled = true;
comboBox2.Enabled = true;
comboBox3.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;             
}
}
//串口接收事件
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
int num = serialPort1.BytesToRead;        //获取接收缓冲区中的字节数
byte[] received_buf = new byte[num];      //声明一个大小为num的byte类型数组,数组名为received_buf;
serialPort1.Read(received_buf, 0, num);  //读取接收缓冲区中num个字节到byte数组中
receive_count += num;                    //接收字节计数变量增加num;
sb.Clear();  //防止出错,首先清空字符串构造器
if (radioButton2.Checked)
{
foreach (byte b in received_buf)
{
//sb.Append(b.ToString());          //将字节型数组转化为字符串,进行拼接得到总的字符串
//ToString("X2") 为C#中的字符串格式控制符,将byte型数据转化为2位16进制文本显
示,用空格隔开,
/
/X:十六进制
//大写X:ToString("X2")即转化为大写的16进制、小写x:ToString("x2")即转化为小写的16进制。
//2表示输出两位,不足的2位的前面补0,如 0x0A 如果没有2,就只会输出
sb.Append(b.ToString("X2")+" " );
// sb.Append(b.ToString("X2") + '\n');
}
}
if (radioButton1.Checked)
{
sb.Append(Encoding.ASCII.GetString(received_buf));  ///字符--经过编码变为---ASCII--经过解码再变回字符,文本在文本框里面显示出来
}
try
{
//因为要访问UI资源,所以需要使用invoke方式同步ui
//因为要访问UI资源,所以需要使用invoke方式同步ui
Invoke((EventHandler)(delegate
{
if (checkBox1.Checked)
{
//显示时间
current_time = System.DateTime.Now;    //获取当前时间
//textBox1.AppendText(current_time.ToString("yyyy-MM-dd HH:mm:ss") + "  " + sb.ToString());
if (checkBox2.Checked)
{
textBox1.AppendText(current_time.ToString("yyyy-MM-dd HH:mm:ss") + " " + sb.ToString()+"\n");    //带有时间的换行显示
}
else
{
textBox1.AppendText(current_time.ToString("yyyy-MM-dd HH:mm:ss") + " " + sb.ToString() + " ");
}
}
else  //不显示时间
{
//textBox1.AppendText(sb.ToString()+ " ");
if (checkBox2.Checked)
{
textBox1.AppendText(sb.ToString() +"\n");        //换行显示
}
else
{
textBox1.AppendText(sb.ToString() + " " );
}
}
label7.Text = "Rx:" + receive_count.ToString() + "Bytes";
}
)
);
}
catch (Exception ex)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show(ex.Message);
}
}
//清空按钮事件
private void button3_Click(object sender, EventArgs e)
{
textBox1.Text = "";  //清空接收文本框
textBox2.Text = "";    //清空发送文本框
python 正则表达式 空格
receive_count = 0;          //计数清零
Send_count = 0;
la