using System; using System.IO.Ports; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace EasyTemplate.Service { public class SerialPortBackgroundService : BackgroundService { private readonly ILogger _logger; private SerialPort _serialPort; private readonly string _portName; private readonly int _baudRate; private bool _isRunning = false; public event EventHandler OnDataReceived; public event EventHandler OnError; public event EventHandler OnStatusChanged; private const int VRC_BUF_SIZE = 5120; private byte[] m_buf = new byte[VRC_BUF_SIZE]; private int m_buflen = 0; private bool isLastFa = false; public SerialPortBackgroundService(ILogger logger) { _logger = logger; _portName = "COM11"; // 可从配置文件读取 _baudRate = 9600; // 可从配置文件读取 } public override Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("串口后台服务开始启动"); return base.StartAsync(cancellationToken); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("串口后台服务执行中..."); while (!stoppingToken.IsCancellationRequested) { if (!_isRunning) { await InitializeSerialPort(); } await Task.Delay(1000, stoppingToken); // 避免过度占用CPU } } private async Task InitializeSerialPort() { try { if (_serialPort != null) { _serialPort.DataReceived -= SerialPort_DataReceived; _serialPort.ErrorReceived -= SerialPort_ErrorReceived; _serialPort.Dispose(); } _serialPort = new SerialPort(_portName, _baudRate); _serialPort.DataReceived += SerialPort_DataReceived; _serialPort.ErrorReceived += SerialPort_ErrorReceived; _serialPort.Open(); _isRunning = true; OnStatusChanged?.Invoke(this, "串口服务已启动"); _logger.LogInformation($"串口 {_portName} 已打开,波特率 {_baudRate}"); } catch (Exception ex) { _isRunning = false; _logger.LogError(ex, $"初始化串口 {_portName} 失败"); OnError?.Invoke(this, $"串口初始化失败: {ex.Message}"); await Task.Delay(5000); // 等待5秒后重试 } } public async Task SendDataAsync(string data) { try { if (_serialPort == null || !_serialPort.IsOpen) { _logger.LogWarning("串口未就绪,无法发送数据"); return false; } byte[] buffer = Encoding.UTF8.GetBytes(data); _serialPort.Write(buffer, 0, buffer.Length); _logger.LogInformation($"发送数据: {data}"); return true; } catch (Exception ex) { _logger.LogError(ex, "发送数据时发生错误"); OnError?.Invoke(this, $"发送失败: {ex.Message}"); return false; } } private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { if (_serialPort != null && _serialPort.IsOpen) { //string data = _serialPort.ReadExisting(); var s = _serialPort.BytesToRead; byte[] buffer = new byte[s]; var ss = _serialPort.Read(buffer, 0, s); ProcessComData(buffer, (uint)buffer.Length); // OnDataReceived?.Invoke(this, data); // _logger.LogInformation($"接收到串口数据: {data}"); } } catch (Exception ex) { _logger.LogError(ex, "读取串口数据时发生错误"); OnError?.Invoke(this, $"读取数据失败: {ex.Message}"); } } public bool ProcessComData(byte[] buf, uint bufLen) { if (bufLen > VRC_BUF_SIZE / 2) { GlobalTool.vr_log("收到数据长度过长,本次弃用"); GlobalTool.vr_log_error("收到数据长度过长,本次弃用"); return false; } // 记录收到的数据 StringBuilder s = new StringBuilder(); for (int i = 0; i < bufLen; i++) { s.Append($"{buf[i]:X2} "); } string chlog = $"收到串口{_portName}的数据:{s}"; GlobalTool.vr_log(chlog); // 拆包组包 转义处理 CRC校验 int len = (int)bufLen; byte[] tmpbuf = new byte[VRC_BUF_SIZE]; Array.Copy(buf, tmpbuf, len); int minlen = 10; while (len > 0) { if (m_buflen == 0) // 1.1 { // 定位第一个单独fa int pos = -1; for (int i = 0; i < len; i++) { if (tmpbuf[i] == 0xfa) { if (i == len - 1) { pos = i; } else { if (tmpbuf[i + 1] != 0xfa) { pos = i; break; } else { i++; } } } } // 处理上次可能遗留的fa if (isLastFa && len >= 2 && tmpbuf[0] == 0xfa && tmpbuf[1] == 0xfa) { pos = 1; isLastFa = false; } if (pos != -1) { if (pos != 0) { byte[] tmpbuf2 = new byte[VRC_BUF_SIZE]; Array.Copy(tmpbuf, pos, tmpbuf2, 0, len - pos); Array.Copy(tmpbuf2, tmpbuf, len - pos); len = len - pos; } } else { GlobalTool.vr_log("没找到单独fa,弃用"); GlobalTool.vr_log_error("没找到单独fa,弃用"); return false; } // 2.1 if (len < minlen) { Array.Copy(tmpbuf, m_buf, len); m_buflen = len; return false; } else { // 2.2 获取数据包长度 byte[] lenBytes = new byte[2] { tmpbuf[6], tmpbuf[7] }; ushort nLen = (ushort)GlobalTool.BCDtoDec(lenBytes, 2); if (nLen > 1000) { byte[] tmpbuf2 = new byte[VRC_BUF_SIZE]; Array.Copy(tmpbuf, 1, tmpbuf2, 0, len - 1); Array.Copy(tmpbuf2, tmpbuf, len - 1); len = len - 1; GlobalTool.vr_log("数据包长度不能超过1000,tmpbuf扔掉包头,重新进入循环判断"); GlobalTool.vr_log_error("数据包长度不能超过1000,tmpbuf扔掉包头,重新进入循环判断"); continue; } // 定位下一个单独fa int sepos = -1; for (int i = 2; i < len - 1; i++) { if (tmpbuf[i] == 0xfa) { if (i == len - 1) { sepos = i; } else { if (tmpbuf[i + 1] != 0xfa) { sepos = i; break; } else { i++; } } } } if (sepos != -1) { if (sepos < nLen + minlen) // 如果下个独立fa在该数据包的涵盖范围内 { byte[] tmpbuf2 = new byte[VRC_BUF_SIZE]; Array.Copy(tmpbuf, sepos, tmpbuf2, 0, len - sepos); Array.Copy(tmpbuf2, tmpbuf, len - sepos); len = len - sepos; GlobalTool.vr_log("该数据包不完整,弃用"); GlobalTool.vr_log_error("该数据包不完整,弃用"); continue; } } // 检查最后一个字符是否为单独的fa if (tmpbuf[len - 1] == 0xfa && len > 1 && tmpbuf[len - 2] != 0xfa) { isLastFa = true; } // 2.3 计算非转义长度 int calclen = len; GlobalTool.calcEscapeLength(tmpbuf, ref calclen, 0xfa); // 2.4 if (calclen < (nLen + minlen)) // 数据包没有全部接收 { Array.Copy(tmpbuf, m_buf, len); m_buflen = len; return false; } // 处理转义 GlobalTool.checkEscapeCharacter(ref tmpbuf, ref len, 0xfa); byte[] destinationBuffer = new byte[nLen + minlen - 1]; Buffer.BlockCopy(tmpbuf, 1, destinationBuffer, 0, nLen + minlen - 1); // 计算CRC ushort nSum = GlobalTool.chkcrc(destinationBuffer, (ushort)(nLen + 7), 0xA001); // 字节序转换 ushort newSum = (ushort)((nSum >> 8) | (nSum << 8)); ushort oldSum = BitConverter.ToUInt16(tmpbuf, nLen + 8); if (oldSum == newSum) { // 处理交易计数等业务逻辑 if (tmpbuf[8] == 0x04 || tmpbuf[8] == 0x0D) { int fip = tmpbuf[4]; // 这里需要实现singleton的对应功能 GlobalTool.vr_log($"(a)主板{fip}收到交易数量"); } if ((tmpbuf[5] & 0x80) == 0x80) // 收到结束帧 { // 这里需要实现singleton的对应功能 long currentTick = Environment.TickCount; GlobalTool.vr_log($"收到结束帧, 耗时:{currentTick - /*vrceachtick*/0}毫秒"); } byte[] bdbuf = new byte[nLen + minlen]; Buffer.BlockCopy(tmpbuf, 0, bdbuf, 0, nLen + minlen); BufferData bd = new BufferData(); bd.type = 1; bd.buffer = bdbuf; bd.serialPort = _serialPort; GlobalTool.g_dataQueue.Enqueue(bd); } else { GlobalTool.vr_log("crc校验失败"); GlobalTool.vr_log_error("crc校验失败"); } // 继续处理剩余数据 if (len > (nLen + minlen)) { byte[] tmpbuf2 = new byte[VRC_BUF_SIZE]; Array.Copy(tmpbuf, nLen + minlen, tmpbuf2, 0, len - (nLen + minlen)); Array.Copy(tmpbuf2, tmpbuf, len - (nLen + minlen)); len = len - (nLen + minlen); } else { return true; } } } else // 1.2 有缓存数据 { if (m_buflen + len <= VRC_BUF_SIZE) { Array.Copy(m_buf, 0, tmpbuf, 0, m_buflen); Array.Copy(buf, 0, tmpbuf, m_buflen, len); len += m_buflen; } Array.Clear(m_buf, 0, m_buf.Length); m_buflen = 0; } } return true; } private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { string errorMessage = $"串口错误: {e.EventType}"; _logger.LogError(errorMessage); OnError?.Invoke(this, errorMessage); } public override async Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("串口后台服务正在停止"); _isRunning = false; if (_serialPort != null && _serialPort.IsOpen) { _serialPort.Close(); _serialPort.DataReceived -= SerialPort_DataReceived; _serialPort.ErrorReceived -= SerialPort_ErrorReceived; _serialPort.Dispose(); } await base.StopAsync(cancellationToken); } public bool IsConnected() => _serialPort?.IsOpen ?? false; public string[] GetAvailablePorts() => SerialPort.GetPortNames(); } }