串口通信
1 封装串口通信
using System; using System.IO.Ports; using System.Linq; namespace Business { /// <summary> /// 串口通信服务 /// </summary> public class SerialPortService { //串口 private readonly SerialPort _serialPort = new SerialPort(); /// <summary> /// 串口服务构造 /// </summary> ///<remarks>设备未固定通信串口</remarks> /// <param name="baudRate">比特率</param> /// <param name="dataBits">数据位数</param> /// <param name="stopBits">停止位</param> /// <param name="parity">校验位</param> /// <param name="handshake">握手协议</param> public SerialPortService(int baudRate, int dataBits, StopBits stopBits, Parity parity, Handshake handshake) { _serialPort.BaudRate = baudRate; _serialPort.DataBits = dataBits; _serialPort.StopBits = stopBits; _serialPort.Parity = parity; _serialPort.Handshake = handshake; _serialPort.DataReceived += OnDataReceived; _serialPort.ErrorReceived += OnErrorReceived; } /// <summary> /// 串口通信服务构造 /// </summary> /// <remarks>设备固定通信串口</remarks> /// <param name="portName">串口名称</param> /// <param name="baudRate">比特率</param> /// <param name="dataBits">数据位数</param> /// <param name="stopBits">停止位</param> /// <param name="parity">校验位</param> /// <param name="handshake">握手协议</param> public SerialPortService(string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity, Handshake handshake) : this(baudRate, dataBits, stopBits, parity, handshake) { var portNames = SerialPort.GetPortNames(); if (!portNames.Any(x => x.Equals(portName))) throw new NotSupportedException($"无法找到串口名为{portName}的串口"); _serialPort.PortName = portName; } /// <summary> /// 设置串口绑定设备名称 /// </summary> /// <param name="portName"></param> public void SetPortName(string portName) { _serialPort.PortName = portName; } /// <summary> /// 获取串口绑定设备名称 /// </summary> public string GetPortName() { return _serialPort.PortName; } /// <summary> /// 停止 /// </summary> public void Close() { if (!_serialPort.IsOpen) return; _serialPort.Close(); } /// <summary> /// 开始 /// </summary> public void Open() { if (_serialPort.IsOpen) return; _serialPort.Open(); } /// <summary> /// 接收数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) { try { if (_serialPort.BytesToRead == 0) return; var receivedData = new byte[_serialPort.BytesToRead]; _serialPort.Read(receivedData, 0, receivedData.Length); var hex = StringHexHelper.ByteToHexStr(receivedData, 0, receivedData.Length); DataReceived?.Invoke(this, hex); } catch (Exception exception) { ErrorReceived?.Invoke(sender, $"{_serialPort.PortName} 数据读取异常: {exception.Message}"); } } /// <summary> /// 接收错误 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e) { ErrorReceived?.Invoke(sender, e.EventType.ToString()); } /// <summary> /// 发送Hex格式的数据 /// </summary> /// <param name="text"></param> public void WriteHex(string text) { if (!_serialPort.IsOpen) return; var textBytes = StringHexHelper.StrToHexBytes(text); _serialPort.Write(textBytes, 0, textBytes.Length); DataSent?.Invoke(this, text); } /// <summary> /// 接收到完成的事件 /// </summary> public event EventHandler<string> DataReceived; /// <summary> /// 接收到错误的事件 /// </summary> public event EventHandler<string> ErrorReceived; /// <summary> /// 发送完成事件之后触发 /// </summary> public event EventHandler<string> DataSent; } }
2 通信进制转换
using System; using System.Text; namespace Business { /// <summary> /// 字符串和16进制字符串转换 /// </summary> internal class StringHexHelper { /// <summary> /// 字符串转为16进制字符串 /// </summary> /// <param name="s"></param> /// <param name="encode"></param> /// <returns></returns> public static string StringToHexString(string s, Encoding encode) { byte[] b = encode.GetBytes(s);//按照指定编码将string编程字节数组 string result = string.Empty; for (int i = 0; i < b.Length; i++)//逐字节变为16进制字符,以%隔开 { result += "%" + Convert.ToString(b[i], 16); } return result; } /// <summary> /// 16进制字符串转为字符串 /// </summary> /// <param name="hs"></param> /// <param name="encode"></param> /// <returns></returns> public static string HexStringToString(string hs, Encoding encode) { //以%分割字符串,并去掉空字符 string[] chars = hs.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries); byte[] b = new byte[chars.Length]; //逐个字符变为16进制字节数据 for (int i = 0; i < chars.Length; i++) { b[i] = Convert.ToByte(chars[i], 16); } //按照指定编码将字节数组变为字符串 return encode.GetString(b); } /// <summary> /// 字符串转换16进制字节数组 /// </summary> /// <param name="hexString"></param> /// <returns></returns> public static byte[] StrToHexBytes(string hexString) { //清除所有空格 hexString = hexString.Replace(" ", ""); //若字符个数为奇数,补一个0 hexString += hexString.Length % 2 != 0 ? "0" : ""; byte[] result = new byte[hexString.Length / 2]; for (int i = 0, c = result.Length; i < c; i++) { result[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } return result; } /// <summary> /// 字节数组转16进制字符串 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string ByteToHexStr(byte[] bytes, int start = 0, int length = 10) { var returnStr = ""; if (bytes == null) return string.IsNullOrEmpty(returnStr) ? "0" : returnStr; for (var i = start; i < bytes.Length && i < length; i++) { var aa = bytes[i].ToString("X2"); returnStr += aa; } return string.IsNullOrEmpty(returnStr) ? "0" : returnStr; } } }
3 确认通信可用的串口
using System; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MagicWindowsCenter.Business { /// <summary> /// 设备服务:用于获取设备模型、Bios Sn /// </summary> internal class DeviceService { //串口通信服务 private readonly SerialPortService _serialPortService; //缓冲接收包 private readonly StringBuilder _receivedBuffer = new StringBuilder(); //同步发送到接收信号 private readonly AutoResetEvent _sendReceiveSignal = new AutoResetEvent(false); //找到Jd98寸通信COM端口 private bool _isFoundCom = false; public DeviceService() { _serialPortService = new SerialPortService(9600, 8, StopBits.One, Parity.None, Handshake.None); _serialPortService.DataReceived += OnDataReceived; _serialPortService.ErrorReceived += OnErrorReceived; _serialPortService.DataSent += OnDataSent; } #region 私有方案 /// <summary> /// 通过通信命令获取串口通信的端口 /// </summary> /// <param name="commands"></param> /// <exception cref="NotSupportedException"></exception> private async Task<string> GetPortByProtocolAsync(string commands) { return await Task.Run(() => { var portNames = SerialPort.GetPortNames(); if (!portNames.Any()) throw new NotSupportedException($"本地设备不存在串口设备"); Console.WriteLine("通过命令{commands} 轮询获取对应的串口"); foreach (var portName in portNames) { try { _serialPortService.SetPortName(portName); _serialPortService.Open(); _serialPortService.WriteHex(commands); if (_sendReceiveSignal.WaitOne(1500)) { Console.WriteLine($"通过命令{commands} 获取串口: {portName}"); return portName; } Console.WriteLine($"通过命令{commands} 获取串口: {portName} -->超时"); } catch (Exception e) { Console.WriteLine($"通过命令{commands} 获取串口: {portName} -->错误:{e.Message}"); } finally { _serialPortService.Close(); } } return string.Empty; }); } private void OnDataSent(object sender, string sendData) { Console.WriteLine($"已发送串口信息:{sendData}"); } private void OnErrorReceived(object sender, string e) { Console.WriteLine($"接收串口错误信息: {e}"); } private void OnDataReceived(object sender, string data) { _receivedBuffer.Append(data); var packet = _receivedBuffer.ToString(); var isOK = packet.StartsWith(ProtocolStart) && packet.EndsWith(ProtocolEnd); if (isOK) { Console.WriteLine($"接收串口信息: {packet}"); _sendReceiveSignal.Set(); // 清除已处理的数据 _receivedBuffer.Clear(); } } /// <summary> ///开启串口 /// </summary> /// <returns></returns> private async Task<bool> OpenSerialPortAsync() { try { if (_isFoundCom) { _serialPortService.Open(); Console.WriteLine($"打开串口: {_serialPortService.GetPortName()}"); return true; } var portName = await GetPortByProtocolAsync(Protocol98ObtainMuteStatus); if (string.IsNullOrWhiteSpace(portName)) return false; _serialPortService.SetPortName(portName); _serialPortService.Open(); Console.WriteLine($"打开串口: {portName}"); _isFoundCom = true; return true; } catch (Exception e) { Console.WriteLine(e); } return false; } /// <summary> /// 关闭串口 /// </summary> private void CloseSerialPort() { _serialPortService.Close(); } #endregion /// <summary> /// 获取设备型号 /// </summary> /// <returns></returns> public async Task<(bool success, string deviceModel)> GetDeviceModelAsync() { try { var open = await OpenSerialPortAsync(); if (open) { return (true, "XX-XX"); } return (false, string.Empty); } catch (Exception e) { Console.WriteLine(e); return (false, string.Empty); } finally { CloseSerialPort(); } } /// <summary> /// 获取Bios Sn /// </summary> /// <returns></returns> public async Task<(bool success, string biosSn)> GetDeviceBiosSnAsync() { try { var open = await OpenSerialPortAsync(); if (!open) return (false, string.Empty); var biosSn = "XXXXXXXXX"; return (true, biosSn); } catch (Exception e) { MagicCenters.Log.Error(e); return (false, string.Empty); } finally { CloseSerialPort(); } } //协议头 private const string ProtocolStart = "FE"; //协议尾 private const string ProtocolEnd = "CF"; //获取静音状态:确认串口 private const string Protocol98ObtainMuteStatus = "FE XX XX CF"; } }