写一个完整SerialPort Helper类,其实SerialPort功能封装已经很ok了。后面一个例子是闲着用MVC模式再写一把。
模拟工具
C#public class SerialPortHelper
{
public delegate void SerialPortCallback(object sender, string message);
public event SerialPortCallback ReceiveEvent;
SerialPort _serialPort;
/// <summary>
/// 返回所有Com Port
/// </summary>
public static List<string> ComPorts
{
get
{
return System.IO.Ports.SerialPort.GetPortNames().ToList();
}
}
public string PortName { get; set; }
public int BaudRate { get; set; }
public Parity Parity { get; set; }
public int DataBits { get; set; }
public StopBits StopBits { get; set; }
public Handshake Handshake { get; set; }
/// <summary>
/// 接收数据类型
/// </summary>
public ReceiveType DataType { get; set; } = ReceiveType.Hex;
/// <summary>
/// 接收数据的编码格式
/// </summary>
public System.Text.Encoding DataEncoding { get; set; } = Encoding.UTF8;
/// <summary>
/// 接收数据是的间隔,可以防止粘包
/// </summary>
public int ReceiveInterval { get; set; }
/// <summary>
/// 接收数据长度
/// </summary>
public int DataSize { get; set; } = 8;
public enum ReceiveType
{
String,
Hex
}
public SerialPortHelper()
{
}
public SerialPortHelper(string portName, int baudRate, int dataBits, Parity parity = Parity.None
, StopBits stopBits = StopBits.One, Handshake handshake = Handshake.None, ReceiveType dataType = ReceiveType.Hex)
{
this.PortName = portName;
this.BaudRate = baudRate;
this.Parity = parity;
this.DataBits = dataBits;
this.StopBits = stopBits;
this.Handshake = handshake;
this.DataType = dataType;
Init();
}
public void Init()
{
_serialPort = new SerialPort();
_serialPort.PortName = PortName;
_serialPort.BaudRate = BaudRate;
_serialPort.Parity = Parity;
_serialPort.DataBits = DataBits;
_serialPort.StopBits = StopBits;
_serialPort.Handshake = Handshake;
_serialPort.DataReceived += _serialPort_DataReceived;
}
public bool Open()
{
try
{
if (_serialPort != null && !_serialPort.IsOpen)
{
_serialPort.Open();
return true;
}
return false;
}
catch
{
return false;
}
}
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] b = new byte[DataSize];
int A = _serialPort.Read(b, 0, b.Length);
string s = "";
if (DataType == ReceiveType.Hex)
{
s = ByteToHex(b);
}
else
{
s = DataEncoding.GetString(b);
}
if (ReceiveInterval > 0)
{
Thread.Sleep(ReceiveInterval);
}
if (ReceiveEvent != null)
{
ReceiveEvent(_serialPort, s);
}
}
public void Close()
{
if (_serialPort != null && _serialPort.IsOpen)
{
_serialPort.DataReceived -= _serialPort_DataReceived;
_serialPort.Close();
}
}
private string ByteToHex(byte[] Bytes)
{
string str = string.Empty;
foreach (byte Byte in Bytes)
{
str += String.Format("{0:X2}", Byte) + " ";
}
return str.Trim();
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="value"></param>
public bool SendData(string value)
{
if (_serialPort == null || !_serialPort.IsOpen)
{
return false;
}
try
{
_serialPort.Write(value);
return true;
}
catch
{
return false;
}
}
}
继续改进,我们发现这里是不是可以继承SerialPort,这样更简单些吧?
C#public class SerialPortHelper : SerialPort
{
public event Action<object, string> ReceiveEvent;
/// <summary>
/// 返回所有Com Port
/// </summary>
public static List<string> ComPorts
{
get
{
return System.IO.Ports.SerialPort.GetPortNames().ToList();
}
}
/// <summary>
/// 接收数据类型
/// </summary>
public ReceiveType DataType { get; set; } = ReceiveType.Hex;
/// <summary>
/// 接收数据的编码格式
/// </summary>
public System.Text.Encoding DataEncoding { get; set; } = Encoding.UTF8;
/// <summary>
/// 接收数据是的间隔,可以防止粘包
/// </summary>
public int ReceiveInterval { get; set; }
/// <summary>
/// 接收数据长度
/// </summary>
public int DataSize { get; set; } = 8;
public enum ReceiveType
{
String,
Hex
}
public SerialPortHelper()
{
}
public SerialPortHelper(string portName, int baudRate, int dataBits, Parity parity = Parity.None
, StopBits stopBits = StopBits.One, Handshake handshake = Handshake.None, ReceiveType dataType = ReceiveType.Hex)
{
this.PortName = portName;
this.BaudRate = baudRate;
this.Parity = parity;
this.DataBits = dataBits;
this.StopBits = stopBits;
this.Handshake = handshake;
this.DataType = dataType;
}
public bool Open()
{
try
{
if (!this.IsOpen)
{
this.DataReceived += _serialPort_DataReceived;
base.Open();
return true;
}
return false;
}
catch
{
return false;
}
}
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] b = new byte[DataSize];
int A = this.Read(b, 0, b.Length);
string s = "";
if (DataType == ReceiveType.Hex)
{
s = ByteToHex(b);
}
else
{
s = DataEncoding.GetString(b);
}
if (ReceiveInterval > 0)
{
Thread.Sleep(ReceiveInterval);
}
if (ReceiveEvent != null)
{
ReceiveEvent(this, s);
}
}
public void Close()
{
if (this.IsOpen)
{
this.DataReceived -= _serialPort_DataReceived;
base.Close();
}
}
private string ByteToHex(byte[] Bytes)
{
string str = string.Empty;
foreach (byte Byte in Bytes)
{
str += String.Format("{0:X2}", Byte) + " ";
}
return str.Trim();
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="value"></param>
public bool SendData(string value)
{
if (!this.IsOpen)
{
return false;
}
try
{
this.Write(value);
return true;
}
catch
{
return false;
}
}
}
我们再试一下,我不想有Event,直接用Action是不是也可以,这个就是回调
C#public Action<object, string> ReceiveAction;
修改一下Receive事件
C#if (ReceiveAction != null)
{
ReceiveAction(this, s);
}
这个调用变成
C#serialPort.ReceiveAction = new Action<object, string>((obj, msg) =>
{
this.Invoke(new Action(() =>
{
txtReceive.AppendText(System.Environment.NewLine + msg);
}));
});
来个MVC模式
创建Model - SerialPortHelper
在C#中使用SerialPort
类时,PinChanged
事件可以用于检测某些串口信号的变化,例如CD (Carrier Detect), CTS (Clear to Send), DSR (Data Set Ready), RI (Ring Indicator)等。然而,PinChanged
事件并不能直接用于检测服务端连接的断开,因为串口通信本质上是点对点的物理连接,没有明确的“服务端”和“客户端”概念。
不过,您可以通过检测DCD (Data Carrier Detect)信号的变化来间接判断连接状态。DCD信号通常用于表示调制解调器连接状态,但具体取决于您的硬件和应用场景。
C#using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SerialPortTools
{
public class SerialPortEventArge : EventArgs
{
public bool isOpened { get; set; } = false;
public string Message { get; set; } = "";
}
public delegate void SerialPortEventHandler(object sender, SerialPortEventArge e);
public class SerialPortHelper : SerialPort
{
public event SerialPortEventHandler OpenEvent = null;
public event SerialPortEventHandler CloseEvent = null;
public event SerialPortEventHandler ReceiveDataEvent = null;
public event SerialPortEventHandler DisconnectEvent = null;
public static List<string> ComPorts
{
get
{
return SerialPort.GetPortNames().ToList();
}
}
public ReceiveType DataType { get; set; } = ReceiveType.Hex;
public enum ReceiveType
{
String,
Hex
}
public SerialPortHelper()
{
}
public SerialPortHelper(string portName, int baudRate,
int dataBits, Parity parity, StopBits stopBits = StopBits.None
, Handshake handshake = Handshake.None, ReceiveType dataType = ReceiveType.Hex)
{
this.PortName = portName;
this.BaudRate = baudRate;
this.DataBits = dataBits;
this.Parity = parity;
this.StopBits = stopBits;
this.Handshake = handshake;
this.DataType = dataType;
}
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] b = new byte[8];
int A = this.Read(b, 0, 8);
string s = "";
if (DataType == ReceiveType.Hex)
{
s = ByteToHex(b);
}
else
{
s = System.Text.Encoding.Default.GetString(b);
}
if (ReceiveDataEvent != null)
{
ReceiveDataEvent.Invoke(this, new SerialPortEventArge()
{
isOpened = true,
Message = s
});
}
}
new public void Open()
{
try
{
if (!this.IsOpen)
{
this.DataReceived += _serialPort_DataReceived;
this.PinChanged += SerialPortHelper_PinChanged;
base.Open();
}
}
catch (Exception ex)
{
//log
}
if (OpenEvent != null)
{
OpenEvent.Invoke(this, new SerialPortEventArge()
{
isOpened = this.IsOpen,
Message = "打开"
});
}
}
/// <summary>
/// 检测某些串口信号的变化,例如CD (Carrier Detect), CTS (Clear to Send), DSR (Data Set Ready), RI (Ring Indicator)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SerialPortHelper_PinChanged(object sender, SerialPinChangedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
if (e.EventType == SerialPinChange.CDChanged)
{
bool isConnected = sp.CDHolding;
if (!isConnected)
{
if (DisconnectEvent != null)
{
DisconnectEvent(this, new SerialPortEventArge()
{
isOpened = false,
Message = "目标断开"
});
}
}
}
}
new public void Close()
{
try
{
if (this.IsOpen)
{
this.DataReceived -= _serialPort_DataReceived;
base.Close();
}
}
catch (Exception ex)
{
//log
}
if (CloseEvent != null)
{
CloseEvent(this, new SerialPortEventArge()
{
isOpened = this.IsOpen,
Message = "关闭"
});
}
}
public bool SendData(string value)
{
if (!this.IsOpen)
{
return false;
}
try
{
this.Write(value);
return true;
}
catch
{
return false;
}
}
private string ByteToHex(byte[] Bytes)
{
string str = string.Empty;
foreach (byte Byte in Bytes)
{
str += String.Format("{0:X2}", Byte) + " ";
}
return str.Trim();
}
}
}
定义一个接口,主要是绑定事件IView
C#internal interface IView
{
void OpenComEvent(Object sender, SerialPortEventArgs e);
void CloseComEvent(Object sender, SerialPortEventArgs e);
void ReceiveDataEvent(Object sender, SerialPortEventArgs e);
void DisconnectEvent(object sender, SerialPortEventArge e);
}
定义一个Controller ,这里做交互用的
C#internal class Controller
{
SerialPortHelper serialModel = new SerialPortHelper();
IView view;
public Controller(IView view)
{
serialModel.OpenEvent += new SerialPortEventHandler(view.OpenComEvent);
serialModel.CloseEvent += new SerialPortEventHandler(view.CloseComEvent);
serialModel.ReceiveDataEvent += new SerialPortEventHandler(view.ReceiveDataEvent);
}
public void OpenSerialPort(string portName, int baudRate, int dataBits, Parity parity = Parity.None
, StopBits stopBits = StopBits.One, Handshake handshake = Handshake.None, ReceiveType dataType = ReceiveType.Hex
{
if(serialModel.IsOpen)
{
serialModel.Close();
}
serialModel.PortName=portName;
serialModel.BaudRate=baudRate;
serialModel.DataBits=dataBits;
serialModel.Parity=parity;
serialModel.StopBits=stopBits;
serialModel.Handshake=handshake;
serialModel.DataType=dataType;
serialModel.Open();
}
public void CloseSerialPort()
{
if (serialModel.IsOpen)
{
serialModel.Close();
}
}
public void ChangeDataType(ReceiveType dataType)
{
serialModel.DataType=dataType;
}
public void SendData(string value)
{
serialModel.SendData(value);
}
}
界面设计,继承这个IView
C#public partial class FrmMain : Form, IView
{
private Controller controller;
public FrmMain()
{
InitializeComponent();
Init();
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
controller = new Controller(this);
}
private void LoadPorts()
{
cboPort.Items.Clear();
foreach (var item in SerialPortHelper.ComPorts)
{
cboPort.Items.Add(item);
}
cboPort.SelectedIndex = 0;
}
private void Init()
{
LoadPorts();
object[] baudRate = { 4800, 9600, 19200, 38400, 57600, 115200, 230400 };
cboBaudRate.Items.AddRange(baudRate);
cboBaudRate.SelectedIndex = 1;
cboDataBits.Items.Add(7);
cboDataBits.Items.Add(8);
cboDataBits.SelectedIndex = 1;
cboStopBits.Items.Add("One");
cboStopBits.Items.Add("OnePointFive");
cboStopBits.Items.Add("Two");
cboStopBits.SelectedIndex = 0;
//Parity
cboParity.Items.Add("None");
cboParity.Items.Add("Even");
cboParity.Items.Add("Mark");
cboParity.Items.Add("Odd");
cboParity.Items.Add("Space");
cboParity.SelectedIndex = 0;
cboHandshaking.Items.Add("None");
cboHandshaking.Items.Add("XOnXOff");
cboHandshaking.Items.Add("RequestToSend");
cboHandshaking.Items.Add("RequestToSendXOnXOff");
cboHandshaking.SelectedIndex = 0;
btnClosePort.Enabled = false;
}
private void btnRefresh_Click(object sender, EventArgs e)
{
LoadPorts();
}
private void btnOpenPort_Click(object sender, EventArgs e)
{
controller.OpenSerialPort(cboPort.Text, int.Parse(cboBaudRate.Text), int.Parse(cboDataBits.Text),
(Parity)Enum.Parse(typeof(Parity), cboParity.Text), (StopBits)Enum.Parse(typeof(StopBits), cboStop
(Handshake)Enum.Parse(typeof(Handshake), cboHandshaking.Text), rdoHex.Checked ? SerialPortHelper.R
}
private void btnClosePort_Click(object sender, EventArgs e)
{
controller.CloseSerialPort();
}
private void btnSend_Click(object sender, EventArgs e)
{
controller.SendData(txtSend.Text);
}
private void rdoHex_CheckedChanged(object sender, EventArgs e)
{
controller.ChangeDataType(SerialPortHelper.ReceiveType.Hex);
}
private void rdoString_CheckedChanged(object sender, EventArgs e)
{
controller.ChangeDataType(SerialPortHelper.ReceiveType.String);
}
public void OpenComEvent(object sender, SerialPortEventArgs e)
{
if (e.isOpend)
{
stsMain_lblStatus.Text = "端口已打开";
btnOpenPort.Enabled = false;
btnClosePort.Enabled = true;
}
else
{
stsMain_lblStatus.Text = "端口出错";
btnOpenPort.Enabled = true;
btnClosePort.Enabled = false;
}
}
public void CloseComEvent(object sender, SerialPortEventArgs e)
{
if (!e.isOpend)
{
stsMain_lblStatus.Text = "端口已关闭";
btnOpenPort.Enabled = true;
btnClosePort.Enabled = false;
}
else
{
stsMain_lblStatus.Text = "端口关闭出错";
btnOpenPort.Enabled = false;
btnClosePort.Enabled = true;
}
}
public void ReceiveDataEvent(object sender, SerialPortEventArgs e)
{
this.Invoke(new Action(() =>
{
txtReceive.AppendText(System.Environment.NewLine + e.Message);
txtReceive.ScrollToCaret();
}));
}
/// <summary>
/// 目标断开
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void DisconnectEvent(object sender, SerialPortEventArge e)
{
this.Invoke(new Action(() =>
{
txtReceive.AppendText(System.Environment.NewLine + e.Message);
txtReceive.ScrollToCaret();
}));
}
}
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!