SerialPortExample.cs 22 KB

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