-
逻辑式编程语言极简实现(使用C#) - 3. 运行原理
//前提需要
//需要一个 serialPort 工具 可在vs自带的工具栏中获得
//源代码加串口工具地址:
//链接:https://pan.baidu.com/s/1YbfvdXEmfsJX87D-Jxljyg 提取码:d32x
//记录用户打开的串口号 可改为泛型模式
string serialPortName;
//一般电脑是没有串口号的,所以要用个软件创建虚拟串口号
//软件名 - Configure Virtual Serial Port Driver
波特率设置的值为 1382400,921600,460800,256000,230400,128000,115200,76800,57600,
43000,38400,19200,14400,9600,4800,1200
停止位设置的值为:1,1.5,2
数据位设置的值为:8,7,6,5
校验位设置的值为:无,奇校验,偶校验
步骤:
1.设置串口的基本数据 (串口号,波特率,数据位,停止位,校验位)
2.使用Open打开串口
3.使用串口工具的DataReceived()事件进行数据的接收
4.创建发送数据 Write() ,一般发送的是byte数组
5.使用Close关闭串口
界面:
//一
//在窗口Load事件中,设置串口基本数据的默认值
//1.拿到电脑上可以使用的串口号,并赋值给窗口的串口下拉列表中,默认选项索引为0
string[] prots = System.IO.Ports.SerialPort.GetPortNames();
this.cbPort.Items.AddRange(prots);
this.cbPort.SelectedIndex = this.cbPort.Items.Count > 0 ? 0 : -1;
//2.设置波特率,停止位,数据位,校验位的默认值。
this.cbBaud.Text = "115200";
this.cbStopBit.Text = "1";
this.cbDataBit.Text = "8";
this.cbComparable.Text = "无";
//二
//设置打开串口和关闭串口按钮的单击事件
//为串口拿到基本数据
serialPort .PortName = this.cbPort.Text;//串口号
serialPortName = this.cbPort.Text;//记录用户打开的串口号
serialPort.BaudRate = int.Parse(this.cbBaud.Text);//波特率
serialPort.DataBits = int.Parse(this.cbDataBit.Text);//数据位
//设置停止位
if (this.cbStopBit.Text == "1") { serialPort.StopBits = StopBits.One; }
else if (this.cbStopBit.Text == "1.5") { serialPort.StopBits = StopBits.OnePointFive; }
else if (this.cbStopBit.Text == "2") { serialPort.StopBits = StopBits.Two; }
//设置奇偶校验
if (this.cbComparable.Text == "无") { serialPort.Parity = Parity.None; }
else if (this.cbComparable.Text == "奇校验") { serialPort.Parity = Parity.Odd; }
else if (this.cbComparable.Text == "偶校验") { serialPort.Parity = Parity.Even; }
//打开串口 只需要使用Open打开串口
serialPort1.Open();
this.btnOpenStat.Text = "关闭串口";
//如果按钮的Text为 关闭串口的话
serialPort.Close();//Close() - 关闭串口
//三 本次串口的简单数据协议 1 - 字符串 bp - 医疗数据 2 - 文件
//通过串口工具自带的DataReceived事件进行串口的数据接收
//这次的是自己定义的数据协议,一般都是用公司默认的协议 或者RS232和RS485
//获取可以读取的字节数 默认最大值是4096 如果想要更改可以直接改变串口的ReadBufferSize属性
int len = serialPort.BytesToRead;
if(len == serialPort.ReadBufferSize){
MessageBox.Show("程序最多只能接受"+serialPort1.ReadBufferSize+"字节的数据!","提示");
return;
}
//拿到数据,Read()
byte[] buff = new byte[len];
serialPort.Read(buff,0,len);
//如果选中了显示时间就赋值,没有就算
string DateNow = string.Empty;
//根据协议进行判断
if(buff[0]==1){
//拿出原来的数据并去除协议部分 然后放入另一个byte[]数组中
byte[] result = new byte[len];
Buffer.BlockCopy(buff,1,result,0,buff.Length-1);
//将byte数组保存可读string 根据自己的编码格式进行转码
string str = Encoding.Default.GetString(result);
//将数据分割为string[] 判断是否为医疗数据
string[] strPle = str.Split(',');
if(strPle[0]=="bp"){
//放入方法中,重新拼装成可用数据
str = RecHBpData(strPle);
}
//由于我们的接收数据的事件是在一个子线程里面的,
//所以需要Invoke才能给我们主线程创建的控件赋值
//当然你也可以使用InvokeRequired()来判断是否为主线程创建的控件,我这里就没使用了
Invoke(new Action(() => {
//是否显示时间
if(this.ckDateTime.Checked){
DateNow = "\r\n" + DateTime.Now.ToString();
}
//是否为16进制显示
if(this.checkBox1.Checked){
this.txtMsg.AppendText(DateNow + "\r\n" + byteToHexStr(buff));
}
//默认是使用字符串显示
else{
this.txtMsg.AppendText(DateNow + "\r\n" + str);
}
})));
}
//如果传送的文件的话
else if(buff[0]==2){
ProcessRecieveFile(buff);
}
//一般呢,这些方法是放在一个方法类库里面,但是我懒得放就在一个类里面了
#region 封装的方法
/// <summary>
/// 传输的是文件就进行文件的操作
/// </summary>
/// <param name="data">数据流</param>
public void ProcessRecieveFile(byte[] data)
{
using (SaveFileDialog dialog = new SaveFileDialog())
{
dialog.DefaultExt = "txt";
dialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
//由于大部分可能不是主线程,所以我们要加this,不然不会弹出保存框
if (dialog.ShowDialog(this) != DialogResult.OK)
{
return;
}
byte[] result = new byte[data.Length - 1];
Buffer.BlockCopy(data, 1, result, 0, data.Length - 1);
File.WriteAllBytes(dialog.FileName, result);
}
}
#region 封装 数据定义的协议
public static string RecHBpData(string[] strPle)
{
string str = string.Empty;
//判断仪器
str += $"{StaticConstant.DicMedicalnoun[strPle[1]]} ";
//增加时间
str += " " + strPle[2] + " " + strPle[3] + "\r\n";
//增加具体数据
str += $"{StaticConstant.DicMedicalnoun[strPle[4]]}: {strPle[5].TrimStart('0')} \r\n{StaticConstant.DicMedicalnoun[strPle[6]]}: {strPle[7].TrimStart('0')}";
return str;
}
#endregion
/// <summary>
/// 字节数组转16进制字符串
/// </summary>
/// <param name="bytes">byte数组</param>
/// <returns>16进制显示形式</returns>
public static string byteToHexStr(byte[] bytes)
{
string retuenStr = "";
try
{
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
retuenStr += bytes[i].ToString("X2") + " ";//变成16进制,两个中间用空格隔开
}
}
return retuenStr;
}
catch (Exception ex)
{
return retuenStr;
}
}
/// <summary>
/// 字符串转16进制格式,不够自动前面补0
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] strToHexByte(string hexString)
{
int i;
if ((hexString.Length % 2) != 0)
{//奇数个
byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
try
{
for (i = 0; i < hexString.Length - 1; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
i = i * 2;
}
returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
}
catch (Exception)
{
MessageBox.Show("含有非16进制字符", "提示");
return null;
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / 2];
try
{
for (i = 0; i < hexString.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
}
catch (Exception)
{
MessageBox.Show("含有非16进制字符", "提示");
return null;
}
return returnBytes;
}
}
#endregion
//我的医疗数据的封装是放在配置文件中,一般我们都是放在数据库中,不过我就做个小学习工具就 //没做到数据库里面了
//四 发送数据
//发送数据,我这边分为发送文件和发送字符串
//1.发送字符串
string str = this.txtSendMsg.Text.Trim().Tostring();
try{
if(str.Length > 0){
//16进制发送
if(this.ckSend.Checked){
byte[] byt = strToHexBytes(str);
byte[] result = new byte[byt.Length+1];
//加上数据协议
result[0] = 1;
//数据转移
Buffer.BlockCopy(byt,0,result,1,byt.Lengt);
//发送数据,byte形式发送
serialPort.Write(result,0,result.Length);
}
//默认字符串发送
else{
//根据自己的编码格式编码
byte[] dataByte = Encoding.Default.GetBytes(str);
byte[] result = new byte[dataByte.Length + 1];
//加上数据协议
result[0] = 1;
Buffer.BlockCopy(dataByte ,0,result,1,dataByte .Lengt);
//发送数据,byte形式发送
serialPort.Write(result,0,result.Length);
}
}
}catch(Exception ex){}
//2.发送文件
try{
using(OpenFileDialog dialog = new OpenFileDialog()){
if(dialog.ShowDialog(this) != DialogResult.OK){
return;
}
byte[] data = File.ReadAllBytes(dialog.FileName);
byte[] result = new byte[data.Length + 1];
result[0] = 2;
Buffer.BlockCopy(data,0,result,1,data.Length);
serialPort.Write(result,0,result.Lenght);
}
}catch(Exception ex){
MessageBox.Show(ex.Message);
}