SerialPortExample.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using Unity.VisualScripting;
  6. using UnityEngine;
  7. using UnityEngine.SceneManagement;
  8. using MySerialPortInterface;
  9. using MyInfraredInsertcoin;
  10. public class SerialPortExample : MonoBehaviour
  11. {
  12. public int baudrate = 115200;
  13. //使用自定义串口
  14. private SerialPortInterface serialPortInterface;
  15. private static int baseAddress;
  16. private static int offsetAddress;
  17. private Queue<byte> tempDataQueue;
  18. public string PortName = "";
  19. private int coinsAmount = 0;
  20. private const int SlotCount = 1;
  21. bool urlBack = false;
  22. byte dataIndex = 1;
  23. // private static Dictionary<string, int> _isInit = new();
  24. private void Start()
  25. {
  26. //LOG($"_isInit contains {gameObject.name}: {_isInit.ContainsKey(gameObject.name)}");
  27. //if (!_isInit.TryGetValue(gameObject.name, out int instanceID) && instanceID == 0)
  28. //{
  29. // DontDestroyOnLoad(gameObject);
  30. // Init();
  31. //}
  32. DontDestroyOnLoad(gameObject);
  33. Init();
  34. }
  35. private void LOG(string msg)
  36. {
  37. Debug.Log($"<color=#00FF00>{msg}</color>");
  38. }
  39. private void Init()
  40. {
  41. //_isInit[gameObject.name] = GetInstanceID();
  42. #if UNITY_ANDROID && !UNITY_EDITOR
  43. serialPortInterface = gameObject.GetComponent<SerialPortInterface>();
  44. serialPortInterface?.Open();
  45. LOG($"{PortName} 串口打开");
  46. if(serialPortInterface.DeviceName == "ttyS6")
  47. {
  48. //拉取二维码
  49. RequestUrl(dataIndex);
  50. //拉取后台数据
  51. ReqSettingData();
  52. }
  53. #endif
  54. #if UNITY_EDITOR
  55. TestBackUrl();
  56. //serialPortInterface = gameObject.GetComponent<SerialPortInterface>();
  57. //if (serialPortInterface.DeviceName == "ttyS6")
  58. //{
  59. // //List<byte> data = new List<byte>();
  60. // //data.Add(0XAA);
  61. // //data.Add(0X04);//帧头
  62. // //data.Add(0x06);//帧长度
  63. // //data.Add(0x00);//数据编号
  64. // //data.Add(0X02);//指令
  65. // //data.Add(0X55);//数据
  66. // //HandleSerialPortUpdate(data.ToArray(), 6);
  67. //}
  68. #endif
  69. }
  70. void Update()
  71. {
  72. #region 测试代码
  73. #if UNITY_EDITOR
  74. if (Input.GetMouseButtonDown(0))
  75. {
  76. //TestPayQR();
  77. }
  78. #endif
  79. #endregion
  80. //if (serialPortUtility != null && serialPortUtility.IsOpened())
  81. // {
  82. // Debug.Log(serialPortUtility.GetSerialDebugString);
  83. // Debug.Log("串口接收到数据");
  84. // }
  85. }
  86. void TestPay()
  87. {
  88. if (!PortName.Contains("/dev/ttyS0"))
  89. return;
  90. Debug.LogError("TestPay");
  91. List<byte> data = new List<byte>();
  92. data.Add(0XAA);
  93. data.Add(0X55);//帧头
  94. data.Add(0x04);//帧长度
  95. data.Add(0x01);//数据编号
  96. data.Add(0XA6);//指令
  97. data.Add(0X01);//数据
  98. data.Add(0X00);//校验位
  99. PrintFrame(data.ToArray());
  100. }
  101. public void TestSetting() {
  102. List<byte> data = new List<byte>();
  103. data.Add(0XAA);
  104. data.Add(0X0F);//
  105. data.Add(0x05);//
  106. data.Add(0x0A);//
  107. data.Add(0X55);//
  108. HandleSerialPortUpdate(data.ToArray(), 6);
  109. }
  110. void TestPayQR()
  111. {
  112. if (!PortName.Contains("/dev/ttyS0"))
  113. return;
  114. List<byte> data = new List<byte>();
  115. data.Add(0XAA);
  116. data.Add(0X55);//帧头
  117. data.Add(0x05);//帧长度
  118. data.Add(0xDA);//数据编号
  119. data.Add(0XA1);//指令
  120. data.Add(0X02);//数据
  121. data.Add(0X64);//数据
  122. byte temp = 0;
  123. for (int i = 2; i < data.Count; i++)
  124. {
  125. temp ^= data[i];
  126. }
  127. data.Add(temp);//校验位
  128. Debug.LogError($"TestPayQR temp={temp}");
  129. PrintFrame(data.ToArray());
  130. }
  131. void OnUrlPayResponse(byte[] bytes)
  132. {
  133. //计算校验值
  134. byte tempResponseCheck = 0;
  135. for (int i = 2; i < bytes.Length - 1; i++)
  136. {
  137. tempResponseCheck ^= bytes[i];
  138. }
  139. byte responseCheck = bytes[bytes.Length - 1];
  140. //回复云上分
  141. List<byte> response = new List<byte>();
  142. response.Add(0XAA);
  143. response.Add(0X55);//帧头
  144. response.Add(0X04);//帧长
  145. var dataIndex = bytes[3];
  146. response.Add(dataIndex);//数据编号
  147. response.Add(0XA1);//数据编号
  148. var data = responseCheck == tempResponseCheck ? 0X01 : 0x00;
  149. response.Add((byte)data);//数据
  150. var endIdx = bytes[3];
  151. byte temp = 0;
  152. for (int i = 2; i < response.Count; i++)
  153. {
  154. temp ^= response[i];
  155. }
  156. response.Add(temp);//校验位
  157. Debug.LogError($"responseCheck={responseCheck} tempResponseCheck={tempResponseCheck} data ={data} dataIndex={dataIndex} temp={temp}");
  158. serialPortInterface?.Write(response.ToArray());
  159. if (data == 0X01)
  160. StandaloneAPI.InsertCoint(bytes[6]);
  161. }
  162. /// <summary>
  163. /// 请求后台数据
  164. /// </summary>
  165. public void ReqSettingData()
  166. {
  167. //数据包开始------->(1个字节)->AA
  168. //命令------------->(1个字节)->08(取值范围->00到FF)
  169. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  170. //校验------------->(1个字节)->0D(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  171. //数据包结束------->(1个字节)->55
  172. //例如:AA 08 05 0D 55
  173. List<byte> request = new List<byte>();
  174. request.Add(0xAA);
  175. request.Add(0x08);//命令
  176. request.Add(0x05);//数据长度
  177. byte temp = (byte)(request[1] ^ request[2]);
  178. request.Add(temp);//校验 (校验 = 命令 ^ 数据长度)
  179. request.Add(0x55);//结束
  180. serialPortInterface?.Write(request.ToArray());
  181. LOG($"请求后台数据!");
  182. }
  183. /// <summary>
  184. /// 后台数据消息回复
  185. /// </summary>
  186. /// <param name="bytes"></param>
  187. private void OnSettingDataBack(byte[] bytes) //AA 08 14 03 00 64 00 64 00 00 00 01 00 00 00 01 01 01 1F 55
  188. {
  189. //数据包开始-------------------------- > (1个字节)->AA
  190. //命令--------------------------------->(1个字节)->08(取值范围->00到FF)
  191. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  192. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  193. var coinPreGame = bytes[3];
  194. UserSettings.ins.PerRoundCoin = coinPreGame;
  195. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  196. var time1 = bytes[4];
  197. var time2 = bytes[5];
  198. int time = (time1 << 8) | time2;
  199. UserSettings.ins.PerRoundSeconds = time;
  200. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  201. var changeTicket1 = bytes[6];
  202. var changeTicket2 = bytes[7];
  203. int changeTicket = (changeTicket1 << 8) | changeTicket2;
  204. UserSettings.ins.ChangeTicket = changeTicket;
  205. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  206. var totalCoinNum1 = bytes[8];
  207. var totalCoinNum2 = bytes[9];
  208. var totalCoinNum3 = bytes[10];
  209. var totalCoinNum4 = bytes[11];
  210. UserSettings.ins.TotalCoinsNum = (totalCoinNum1 << 24) | (totalCoinNum2 << 16) | (totalCoinNum3 << 8) | totalCoinNum4;
  211. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  212. var totalTicketNum1 = bytes[12];
  213. var totalTicketNum2 = bytes[13];
  214. var totalTicketNum3 = bytes[14];
  215. var totalTicketNum4 = bytes[15];
  216. UserSettings.ins.TotalTicketNum = (totalTicketNum1 << 24) | (totalTicketNum2 << 16) | (totalTicketNum3 << 8) | totalTicketNum4;
  217. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  218. UserSettings.ins.NeedCoin = bytes[16];
  219. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  220. UserSettings.ins.GiveTicket = bytes[17];
  221. //校验-------------------------------- > (1个字节)->1F(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  222. byte temp = 0;
  223. for (int i = 1; i < bytes.Length - 2; i++)
  224. {
  225. temp ^= bytes[i];
  226. }
  227. //数据包结束------------------------->(1个字节)->55
  228. LOG($"收到后台数据--->coinPreGame:{coinPreGame} time={time} 校验结果={bytes[18] == temp} temp={temp} bytes[18]={bytes[18]} !");
  229. }
  230. /// <summary>
  231. /// 请求保存后台数据
  232. /// </summary>
  233. public void ReqSavingSettingData() //AA 07 14 03 00 64 00 65 00 00 00 01 00 00 00 02 01 01 12 55
  234. {
  235. List<byte> request = new List<byte>();
  236. //数据包开始-------------------------- > (1个字节)->AA
  237. request.Add(0xAA);
  238. //命令--------------------------------->(1个字节)->07(取值范围->00到FF)
  239. request.Add(0x07);
  240. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  241. request.Add(0x14);
  242. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  243. request.Add((byte)UserSettings.ins.PerRoundCoin);
  244. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  245. int time = UserSettings.ins.PerRoundSeconds;
  246. request.Add((byte)(time >> 8));
  247. request.Add((byte)(time));
  248. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  249. int changeTicket = UserSettings.ins.ChangeTicket;
  250. request.Add((byte)(changeTicket >> 8));
  251. request.Add((byte)(changeTicket));
  252. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  253. int totalCoins = UserSettings.ins.TotalCoinsNum;
  254. request.Add((byte)(totalCoins >> 24));
  255. request.Add((byte)(totalCoins >> 16));
  256. request.Add((byte)(totalCoins >> 8));
  257. request.Add((byte)(totalCoins));
  258. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  259. int totalTicketNum = UserSettings.ins.TotalTicketNum;
  260. request.Add((byte)(totalTicketNum >> 24));
  261. request.Add((byte)(totalTicketNum >> 16));
  262. request.Add((byte)(totalTicketNum >> 8));
  263. request.Add((byte)(totalTicketNum));
  264. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  265. request.Add((byte)UserSettings.ins.NeedCoin);
  266. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  267. request.Add((byte)UserSettings.ins.GiveTicket);
  268. //校验-------------------------------- > (1个字节)->12(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  269. byte temp = 0;
  270. for (int i = 2; i < request.Count; i++)
  271. {
  272. temp ^= request[i];
  273. }
  274. request.Add(temp);
  275. //数据包结束------------------------->(1个字节)->55
  276. request.Add(0x55);//结束
  277. serialPortInterface?.Write(request.ToArray());
  278. LOG($"请求保存后台数据!");
  279. }
  280. /// <summary>
  281. /// 保存后台数据回复
  282. /// </summary>
  283. private void OnSaveSettingDataBack(byte[] bytes)//AA 07 05 02 55
  284. {
  285. //--> 回复(保存后台数据)的命令
  286. //数据包开始------->(1个字节)->AA
  287. //命令------------->(1个字节)->07(取值范围->00到FF)
  288. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  289. //校验------------->(1个字节)->02(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  290. //数据包结束------->(1个字节)->55
  291. LOG($"保存后台数据回复!");
  292. }
  293. /// <summary>
  294. /// 串口读取二进制流数据
  295. /// </summary>
  296. /// <param name="data"></param>
  297. public void ReadStreamingBinary(object data)
  298. {
  299. var bin = data as byte[];
  300. PrintFrame(bin);
  301. }
  302. //自定义端口使用,根据
  303. public void HandleSerialPortUpdate(byte[] buffer, int size)
  304. {
  305. //Debug.Log($"Received Data: {BitConverter.ToString(buffer)} of size {size}");
  306. //PrintFrame(buffer);
  307. // 将新的数据加入缓存
  308. dataCache.AddRange(buffer);
  309. // 尝试解析缓存中的数据
  310. while (TryParseNextFrame(ref dataCache, out byte[] frame))
  311. {
  312. // 如果成功解析一帧数据,处理该帧
  313. // Debug.Log($"[SerialPortExample] frame Data: {BitConverter.ToString(frame)}");
  314. PrintFrame(frame);
  315. }
  316. }
  317. public void PrintFrame(byte[] bytes)
  318. {
  319. //string logStr = "";
  320. //for (int i = 0; i < bytes.Length; i++)
  321. //{
  322. // logStr += $"i={bytes[i]}-";
  323. //}
  324. // Debug.Log($"<color=#FF0000>{PortName} 接收到的串口命令:{logStr}</color>");
  325. Debug.Log($"<color=#FF0000> [SerialPortExample] {PortName} 接收到的串口命令:{BitConverter.ToString(bytes)}, 长度{bytes.Length}</color>");
  326. if (bytes[0] == 0xAA)
  327. {
  328. if (bytes[1] == 0x55)//旧
  329. {
  330. if (bytes[4] == 0xA6)//投币
  331. {
  332. Debug.Log("旧版投币数量:" + bytes[4]);
  333. StandaloneAPI.InsertCoint(bytes[5]);
  334. }
  335. else if (bytes[4] == 0xAA) //支付盒子链接请求返回
  336. {
  337. Debug.Log("支付盒子链接请求返回:" + bytes[4]);
  338. OnUrlResponse(bytes);
  339. }
  340. else if (bytes[4] == 0xA1) //支付盒子支付返回(云上分)
  341. {
  342. Debug.Log("支付盒子支付返回(云上分):" + bytes[4]);
  343. OnUrlPayResponse(bytes);
  344. }
  345. }
  346. else
  347. {
  348. //0x04:为按键是否按下的命令
  349. if (bytes[1] == 0x04)//退出
  350. {
  351. Debug.Log("确认按键退出游戏命令");
  352. if (bytes[3] == 0x01)
  353. {
  354. Debug.Log("确认按下按键");
  355. // AimHandler.ins.ExitIntoEvent();
  356. if (SceneManager.GetActiveScene().name == "Home")
  357. {
  358. StandbyVideoManager._ins?.StopVideo();
  359. }
  360. else {
  361. SceneManager.LoadScene("Home", LoadSceneMode.Single);
  362. }
  363. SendMessage(bytes, 3);
  364. return;
  365. }
  366. else if (bytes[3] == 0x00)
  367. {
  368. Debug.Log("松开按键");
  369. SendMessage(bytes, 3);
  370. return;
  371. }
  372. }
  373. else if (bytes[1] == 0x01)//投币
  374. {
  375. Debug.Log("确认投币命令");
  376. StandaloneAPI.InsertCoint(bytes[3]);
  377. SendMessage(bytes, 3);
  378. }
  379. else if (bytes[1] == 0x0F)//进入设置 AA 0F 05 0A 55
  380. OpenSetUp();
  381. else if(bytes[1] == 0x07)//回复保存后台数据 AA 07 05 02 55
  382. OnSaveSettingDataBack(bytes);
  383. else if (bytes[1] == 0x08)//回复读后台数据 AA 08 14 03 00 64 00 64 00 00 00 01 00 00 00 01 01 01 1F 55
  384. OnSettingDataBack(bytes);
  385. }
  386. }
  387. }
  388. /// <summary>
  389. /// 打开设置
  390. /// </summary>
  391. GameObject settingsViewObj;
  392. private void OpenSetUp()
  393. {
  394. AudioMgr.ins.PlayBtn();
  395. if (settingsViewObj) return;
  396. settingsViewObj = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_SettingsView);
  397. settingsViewObj.GetComponent<SmartBow.SettingsView>().ShowBoxSound(true);
  398. }
  399. /// <summary>
  400. /// 请求二维码链接
  401. /// </summary>
  402. /// <param name="dataIndex">数据编号 从1开始</param>
  403. private void RequestUrl(byte dataIndex)
  404. {
  405. LOG($"请求后台支付URL");
  406. byte[] datas = new byte[7] { 0xAA, 0x55, 0x04, dataIndex, 0xAA, 0x01, 0xAE };
  407. datas[6] = GetXor(datas);
  408. serialPortInterface?.Write(datas);
  409. Invoke("CheckURLBack", 5f);
  410. }
  411. private void CheckURLBack()
  412. {
  413. if (!urlBack && dataIndex <= 3)
  414. {
  415. Debug.LogWarning("没有拉取到支付url");
  416. RequestUrl(++dataIndex);
  417. }
  418. }
  419. /// <summary>
  420. /// 测试URL回包
  421. /// </summary>
  422. private void TestBackUrl()
  423. {
  424. var url = $"http://pay.sy1999.com:8901?productNumber=SY4G4365";
  425. var urlData = System.Text.UTF8Encoding.Default.GetBytes(url);
  426. List<byte> data = new List<byte>();
  427. data.Add(0XAA); data.Add(0X55);//帧头
  428. data.Add((byte)(urlData.Length + 3));//帧长度(数据编号到校验位的所有数据长度)
  429. data.Add(0X01);//数据编号
  430. data.Add(0XAA);//指令
  431. data.AddRange(urlData);//数据
  432. data.Add(0X00);//校验位
  433. Debug.Log($"Test: url={url} length={urlData.Length}");
  434. PrintFrame(data.ToArray());
  435. }
  436. /// <summary>
  437. /// URL请求返回
  438. /// </summary>
  439. /// <param name="data"></param>
  440. //void OnUrlResponse(byte[] bytes)
  441. //{
  442. // urlBack = true;
  443. // int frameLength = bytes[2];
  444. // frameLength = frameLength - 3;
  445. // List<byte> datas = new List<byte>();
  446. // var startPos = 5;
  447. // for (int i = startPos; i < frameLength + startPos; i++)
  448. // {
  449. // datas.Add(bytes[i]);
  450. // }
  451. // byte[] bs = datas.ToArray();
  452. // var url = System.Text.Encoding.Default.GetString(bs);
  453. // StandaloneAPI.url = url;
  454. // var homeView = FindObjectOfType<HomeView>();
  455. // if (homeView != null)
  456. // homeView.FlushUrlQR();
  457. // SerialPortHelper.ins.GetPort()?.RequestLightState(true);
  458. //}
  459. void OnUrlResponse(byte[] bytes)
  460. {
  461. // 检查数据长度
  462. if (bytes.Length < 3)
  463. {
  464. Debug.LogError("Received data too short.");
  465. return;
  466. }
  467. int frameLength = bytes[2] - 3;
  468. // 确保 frameLength 合理,防止越界
  469. if (frameLength < 0 || frameLength + 5 > bytes.Length)
  470. {
  471. Debug.LogError("Invalid frame length.");
  472. return;
  473. }
  474. urlBack = true;
  475. List<byte> datas = new List<byte>();
  476. var startPos = 5;
  477. // 提取数据
  478. for (int i = startPos; i < startPos + frameLength; i++)
  479. {
  480. if (i >= bytes.Length) break; // 防止越界
  481. datas.Add(bytes[i]);
  482. }
  483. byte[] bs = datas.ToArray();
  484. // 使用 UTF-8 编码解析 URL
  485. var url = System.Text.Encoding.UTF8.GetString(bs);
  486. StandaloneAPI.url = url;
  487. var homeView = FindObjectOfType<HomeView>();
  488. if (homeView != null)
  489. homeView.FlushUrlQR();
  490. if(!CommonConfig.bOpenTheLaserWithoutQRCode)
  491. SerialPortHelper.ins.GetPort()?.RequestLightState(true);
  492. }
  493. void SendMessage(byte[] bytes,int index)
  494. {
  495. List<byte> s = new List<byte>(bytes);
  496. s.RemoveAt(index);
  497. bytes = s.ToArray();
  498. serialPortInterface?.Write(bytes);
  499. }
  500. byte GetXor(byte[] btyes)
  501. {
  502. byte temp = 0;
  503. for (int i = 2; i < btyes.Length; i++)
  504. {
  505. temp ^= btyes[i];
  506. }
  507. return temp;
  508. }
  509. private void OnDestroy()
  510. {
  511. //if (_isInit.TryGetValue(gameObject.name, out var instanceID) && instanceID == GetInstanceID())
  512. //{
  513. // LOG($"{PortName} 串口程序被销毁");
  514. // serialPortInterface?.Close();
  515. // _isInit.Remove(gameObject.name);
  516. //}
  517. }
  518. #region 解析自定义缓存
  519. private List<byte> dataCache = new List<byte>(); // 用于缓存接收到的数据
  520. private const byte FrameHeader1 = 0xAA; // 帧头1
  521. private const byte FrameHeader2 = 0x55; // 帧头2
  522. // 尝试从缓存中解析出下一帧
  523. private bool TryParseNextFrame(ref List<byte> cache, out byte[] frame)
  524. {
  525. frame = null;
  526. // 检查缓存中是否有足够的数据进行解析(至少需要4个字节:帧头 + 帧长 + 数据编号 + 校验位)
  527. if (cache.Count < 4)
  528. return false;
  529. // 先检查以 0xAA 0x55 为帧头的格式
  530. for (int i = 0; i < cache.Count - 1; i++)
  531. {
  532. // 检查帧头:0xAA 0x55
  533. if (cache[i] == FrameHeader1 && cache[i + 1] == FrameHeader2)
  534. {
  535. // 找到帧头,检查数据长度
  536. if (cache.Count < i + 4) // 至少需要 4 字节:帧头 + 帧长 + 数据编号
  537. return false;
  538. // 获取帧长
  539. byte frameLength = cache[i + 2]; // 帧长是第3字节
  540. // 检查缓存中是否有足够的数据来构成一个完整的帧
  541. if (cache.Count < i + 3 + frameLength)
  542. return false;
  543. // 解析出一帧数据
  544. frame = cache.GetRange(i, 3 + frameLength).ToArray();
  545. // 移除已经解析的帧
  546. cache.RemoveRange(0, i + 3 + frameLength);
  547. return true;
  548. }
  549. }
  550. // 如果没有解析到 0xAA 0x55 格式的数据帧,再检查 0xAA 开头,0x55 结尾的格式
  551. for (int i = 0; i < cache.Count - 1; i++)
  552. {
  553. // 检查帧头:0xAA
  554. if (cache[i] == 0xAA)
  555. {
  556. // 找到帧头,继续向后查找数据是否满足格式
  557. // 查找帧尾:0x55
  558. for (int j = i + 1; j < cache.Count; j++)
  559. {
  560. if (cache[j] == 0x55)
  561. {
  562. // 确保数据包格式正确
  563. if (j - i < 4) // 至少需要 4 字节:命令 + 数据长度 + 校验 + 帧尾
  564. continue;
  565. // 获取完整的帧数据(包括帧头、命令、数据长度、数据、校验和帧尾)
  566. frame = cache.GetRange(i, j - i + 1).ToArray(); // 从帧头到帧尾的所有数据
  567. // 移除已经解析的帧
  568. cache.RemoveRange(0, j + 1); // 从缓存中移除已解析的部分(包括帧头和帧尾)
  569. return true;
  570. }
  571. }
  572. }
  573. }
  574. // 如果没有找到符合条件的帧
  575. return false;
  576. }
  577. #endregion
  578. }