SerialPortService.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. 
  2. using System;
  3. using System.IO.Ports;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Microsoft.AspNetCore.Components;
  8. using Microsoft.Extensions.Hosting;
  9. using Microsoft.Extensions.Logging;
  10. namespace EasyTemplate.Service
  11. {
  12. public class SerialPortBackgroundService : BackgroundService
  13. {
  14. private readonly ILogger<SerialPortBackgroundService> _logger;
  15. private SerialPort _serialPort;
  16. private readonly string _portName;
  17. private readonly int _baudRate;
  18. private bool _isRunning = false;
  19. public event EventHandler<string> OnDataReceived;
  20. public event EventHandler<string> OnError;
  21. public event EventHandler<string> OnStatusChanged;
  22. private const int VRC_BUF_SIZE = 5120;
  23. private byte[] m_buf = new byte[VRC_BUF_SIZE];
  24. private int m_buflen = 0;
  25. private bool isLastFa = false;
  26. public SerialPortBackgroundService(ILogger<SerialPortBackgroundService> logger)
  27. {
  28. _logger = logger;
  29. _portName = "COM11"; // 可从配置文件读取
  30. _baudRate = 9600; // 可从配置文件读取
  31. }
  32. public override Task StartAsync(CancellationToken cancellationToken)
  33. {
  34. _logger.LogInformation("串口后台服务开始启动");
  35. return base.StartAsync(cancellationToken);
  36. }
  37. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  38. {
  39. _logger.LogInformation("串口后台服务执行中...");
  40. while (!stoppingToken.IsCancellationRequested)
  41. {
  42. if (!_isRunning)
  43. {
  44. await InitializeSerialPort();
  45. }
  46. await Task.Delay(1000, stoppingToken); // 避免过度占用CPU
  47. }
  48. }
  49. private async Task InitializeSerialPort()
  50. {
  51. try
  52. {
  53. if (_serialPort != null)
  54. {
  55. _serialPort.DataReceived -= SerialPort_DataReceived;
  56. _serialPort.ErrorReceived -= SerialPort_ErrorReceived;
  57. _serialPort.Dispose();
  58. }
  59. _serialPort = new SerialPort(_portName, _baudRate);
  60. _serialPort.DataReceived += SerialPort_DataReceived;
  61. _serialPort.ErrorReceived += SerialPort_ErrorReceived;
  62. _serialPort.Open();
  63. _isRunning = true;
  64. OnStatusChanged?.Invoke(this, "串口服务已启动");
  65. _logger.LogInformation($"串口 {_portName} 已打开,波特率 {_baudRate}");
  66. }
  67. catch (Exception ex)
  68. {
  69. _isRunning = false;
  70. _logger.LogError(ex, $"初始化串口 {_portName} 失败");
  71. OnError?.Invoke(this, $"串口初始化失败: {ex.Message}");
  72. await Task.Delay(5000); // 等待5秒后重试
  73. }
  74. }
  75. public async Task<bool> SendDataAsync(string data)
  76. {
  77. try
  78. {
  79. if (_serialPort == null || !_serialPort.IsOpen)
  80. {
  81. _logger.LogWarning("串口未就绪,无法发送数据");
  82. return false;
  83. }
  84. byte[] buffer = Encoding.UTF8.GetBytes(data);
  85. _serialPort.Write(buffer, 0, buffer.Length);
  86. _logger.LogInformation($"发送数据: {data}");
  87. return true;
  88. }
  89. catch (Exception ex)
  90. {
  91. _logger.LogError(ex, "发送数据时发生错误");
  92. OnError?.Invoke(this, $"发送失败: {ex.Message}");
  93. return false;
  94. }
  95. }
  96. private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
  97. {
  98. try
  99. {
  100. if (_serialPort != null && _serialPort.IsOpen)
  101. {
  102. //string data = _serialPort.ReadExisting();
  103. var s = _serialPort.BytesToRead;
  104. byte[] buffer = new byte[s];
  105. var ss = _serialPort.Read(buffer, 0, s);
  106. ProcessComData(buffer, (uint)buffer.Length);
  107. // OnDataReceived?.Invoke(this, data);
  108. // _logger.LogInformation($"接收到串口数据: {data}");
  109. }
  110. }
  111. catch (Exception ex)
  112. {
  113. _logger.LogError(ex, "读取串口数据时发生错误");
  114. OnError?.Invoke(this, $"读取数据失败: {ex.Message}");
  115. }
  116. }
  117. public bool ProcessComData(byte[] buf, uint bufLen)
  118. {
  119. if (bufLen > VRC_BUF_SIZE / 2)
  120. {
  121. GlobalTool.vr_log("收到数据长度过长,本次弃用");
  122. GlobalTool.vr_log_error("收到数据长度过长,本次弃用");
  123. return false;
  124. }
  125. // 记录收到的数据
  126. StringBuilder s = new StringBuilder();
  127. for (int i = 0; i < bufLen; i++)
  128. {
  129. s.Append($"{buf[i]:X2} ");
  130. }
  131. string chlog = $"收到串口{_portName}的数据:{s}";
  132. GlobalTool.vr_log(chlog);
  133. // 拆包组包 转义处理 CRC校验
  134. int len = (int)bufLen;
  135. byte[] tmpbuf = new byte[VRC_BUF_SIZE];
  136. Array.Copy(buf, tmpbuf, len);
  137. int minlen = 10;
  138. while (len > 0)
  139. {
  140. if (m_buflen == 0) // 1.1
  141. {
  142. // 定位第一个单独fa
  143. int pos = -1;
  144. for (int i = 0; i < len; i++)
  145. {
  146. if (tmpbuf[i] == 0xfa)
  147. {
  148. if (i == len - 1)
  149. {
  150. pos = i;
  151. }
  152. else
  153. {
  154. if (tmpbuf[i + 1] != 0xfa)
  155. {
  156. pos = i;
  157. break;
  158. }
  159. else
  160. {
  161. i++;
  162. }
  163. }
  164. }
  165. }
  166. // 处理上次可能遗留的fa
  167. if (isLastFa && len >= 2 && tmpbuf[0] == 0xfa && tmpbuf[1] == 0xfa)
  168. {
  169. pos = 1;
  170. isLastFa = false;
  171. }
  172. if (pos != -1)
  173. {
  174. if (pos != 0)
  175. {
  176. byte[] tmpbuf2 = new byte[VRC_BUF_SIZE];
  177. Array.Copy(tmpbuf, pos, tmpbuf2, 0, len - pos);
  178. Array.Copy(tmpbuf2, tmpbuf, len - pos);
  179. len = len - pos;
  180. }
  181. }
  182. else
  183. {
  184. GlobalTool.vr_log("没找到单独fa,弃用");
  185. GlobalTool.vr_log_error("没找到单独fa,弃用");
  186. return false;
  187. }
  188. // 2.1
  189. if (len < minlen)
  190. {
  191. Array.Copy(tmpbuf, m_buf, len);
  192. m_buflen = len;
  193. return false;
  194. }
  195. else
  196. {
  197. // 2.2 获取数据包长度
  198. byte[] lenBytes = new byte[2] { tmpbuf[6], tmpbuf[7] };
  199. ushort nLen = (ushort)GlobalTool.BCDtoDec(lenBytes, 2);
  200. if (nLen > 1000)
  201. {
  202. byte[] tmpbuf2 = new byte[VRC_BUF_SIZE];
  203. Array.Copy(tmpbuf, 1, tmpbuf2, 0, len - 1);
  204. Array.Copy(tmpbuf2, tmpbuf, len - 1);
  205. len = len - 1;
  206. GlobalTool.vr_log("数据包长度不能超过1000,tmpbuf扔掉包头,重新进入循环判断");
  207. GlobalTool.vr_log_error("数据包长度不能超过1000,tmpbuf扔掉包头,重新进入循环判断");
  208. continue;
  209. }
  210. // 定位下一个单独fa
  211. int sepos = -1;
  212. for (int i = 2; i < len - 1; i++)
  213. {
  214. if (tmpbuf[i] == 0xfa)
  215. {
  216. if (i == len - 1)
  217. {
  218. sepos = i;
  219. }
  220. else
  221. {
  222. if (tmpbuf[i + 1] != 0xfa)
  223. {
  224. sepos = i;
  225. break;
  226. }
  227. else
  228. {
  229. i++;
  230. }
  231. }
  232. }
  233. }
  234. if (sepos != -1)
  235. {
  236. if (sepos < nLen + minlen) // 如果下个独立fa在该数据包的涵盖范围内
  237. {
  238. byte[] tmpbuf2 = new byte[VRC_BUF_SIZE];
  239. Array.Copy(tmpbuf, sepos, tmpbuf2, 0, len - sepos);
  240. Array.Copy(tmpbuf2, tmpbuf, len - sepos);
  241. len = len - sepos;
  242. GlobalTool.vr_log("该数据包不完整,弃用");
  243. GlobalTool.vr_log_error("该数据包不完整,弃用");
  244. continue;
  245. }
  246. }
  247. // 检查最后一个字符是否为单独的fa
  248. if (tmpbuf[len - 1] == 0xfa && len > 1 && tmpbuf[len - 2] != 0xfa)
  249. {
  250. isLastFa = true;
  251. }
  252. // 2.3 计算非转义长度
  253. int calclen = len;
  254. GlobalTool.calcEscapeLength(tmpbuf, ref calclen, 0xfa);
  255. // 2.4
  256. if (calclen < (nLen + minlen)) // 数据包没有全部接收
  257. {
  258. Array.Copy(tmpbuf, m_buf, len);
  259. m_buflen = len;
  260. return false;
  261. }
  262. // 处理转义
  263. GlobalTool.checkEscapeCharacter(ref tmpbuf, ref len, 0xfa);
  264. byte[] destinationBuffer = new byte[nLen + minlen - 1];
  265. Buffer.BlockCopy(tmpbuf, 1, destinationBuffer, 0, nLen + minlen - 1);
  266. // 计算CRC
  267. ushort nSum = GlobalTool.chkcrc(destinationBuffer, (ushort)(nLen + 7), 0xA001);
  268. // 字节序转换
  269. ushort newSum = (ushort)((nSum >> 8) | (nSum << 8));
  270. ushort oldSum = BitConverter.ToUInt16(tmpbuf, nLen + 8);
  271. if (oldSum == newSum)
  272. {
  273. // 处理交易计数等业务逻辑
  274. if (tmpbuf[8] == 0x04 || tmpbuf[8] == 0x0D)
  275. {
  276. int fip = tmpbuf[4];
  277. // 这里需要实现singleton<GData>的对应功能
  278. GlobalTool.vr_log($"(a)主板{fip}收到交易数量");
  279. }
  280. if ((tmpbuf[5] & 0x80) == 0x80) // 收到结束帧
  281. {
  282. // 这里需要实现singleton<GData>的对应功能
  283. long currentTick = Environment.TickCount;
  284. GlobalTool.vr_log($"收到结束帧, 耗时:{currentTick - /*vrceachtick*/0}毫秒");
  285. }
  286. byte[] bdbuf = new byte[nLen + minlen];
  287. Buffer.BlockCopy(tmpbuf, 0, bdbuf, 0, nLen + minlen);
  288. BufferData bd = new BufferData();
  289. bd.type = 1;
  290. bd.buffer = bdbuf;
  291. bd.serialPort = _serialPort;
  292. GlobalTool.g_dataQueue.Enqueue(bd);
  293. }
  294. else
  295. {
  296. GlobalTool.vr_log("crc校验失败");
  297. GlobalTool.vr_log_error("crc校验失败");
  298. }
  299. // 继续处理剩余数据
  300. if (len > (nLen + minlen))
  301. {
  302. byte[] tmpbuf2 = new byte[VRC_BUF_SIZE];
  303. Array.Copy(tmpbuf, nLen + minlen, tmpbuf2, 0, len - (nLen + minlen));
  304. Array.Copy(tmpbuf2, tmpbuf, len - (nLen + minlen));
  305. len = len - (nLen + minlen);
  306. }
  307. else
  308. {
  309. return true;
  310. }
  311. }
  312. }
  313. else // 1.2 有缓存数据
  314. {
  315. if (m_buflen + len <= VRC_BUF_SIZE)
  316. {
  317. Array.Copy(m_buf, 0, tmpbuf, 0, m_buflen);
  318. Array.Copy(buf, 0, tmpbuf, m_buflen, len);
  319. len += m_buflen;
  320. }
  321. Array.Clear(m_buf, 0, m_buf.Length);
  322. m_buflen = 0;
  323. }
  324. }
  325. return true;
  326. }
  327. private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
  328. {
  329. string errorMessage = $"串口错误: {e.EventType}";
  330. _logger.LogError(errorMessage);
  331. OnError?.Invoke(this, errorMessage);
  332. }
  333. public override async Task StopAsync(CancellationToken cancellationToken)
  334. {
  335. _logger.LogInformation("串口后台服务正在停止");
  336. _isRunning = false;
  337. if (_serialPort != null && _serialPort.IsOpen)
  338. {
  339. _serialPort.Close();
  340. _serialPort.DataReceived -= SerialPort_DataReceived;
  341. _serialPort.ErrorReceived -= SerialPort_ErrorReceived;
  342. _serialPort.Dispose();
  343. }
  344. await base.StopAsync(cancellationToken);
  345. }
  346. public bool IsConnected() => _serialPort?.IsOpen ?? false;
  347. public string[] GetAvailablePorts() => SerialPort.GetPortNames();
  348. }
  349. }