SerialPortExample.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. using SerialPortUtility;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using Unity.VisualScripting;
  7. using UnityEngine;
  8. public class SerialPortHelper:MonoBehaviour
  9. {
  10. public static SerialPortExample[] SerialPortExampleGroup;
  11. private static ArrowSerialPort arrowPort;
  12. private static SerialPortHelper _ins;
  13. public static SerialPortHelper ins
  14. {
  15. get
  16. {
  17. if (_ins == null)
  18. {
  19. var go = new GameObject("SerialPortHelper");
  20. _ins = go.AddComponent<SerialPortHelper>();
  21. DontDestroyOnLoad(go);
  22. SerialPortExampleGroup = FindObjectsOfType<SerialPortExample>();
  23. }
  24. return _ins;
  25. }
  26. }
  27. /// <summary>
  28. /// 获取串口实例(0串口和8串口)
  29. /// </summary>
  30. public SerialPortExample GetPort(int port)
  31. {
  32. if (port == 0 || port == 8)
  33. {
  34. foreach (var item in SerialPortExampleGroup)
  35. {
  36. if (item.name.Contains("com0"))
  37. return item;
  38. }
  39. }
  40. return null;
  41. }
  42. /// <summary>
  43. /// 获取串口实例(1串口)
  44. /// </summary>
  45. public ArrowSerialPort GetPort()
  46. {
  47. return arrowPort;
  48. }
  49. }
  50. public class SerialPortExample : MonoBehaviour
  51. {
  52. public int baudrate = 115200;
  53. public SerialPortUtilityPro serialPortUtility;
  54. public SerialPortUtilityPro.OpenSystem openMode = SerialPortUtilityPro.OpenSystem.PCI;
  55. private static int baseAddress;
  56. private static int offsetAddress;
  57. private Queue<byte> tempDataQueue;
  58. public string PortName = "";
  59. private int coinsAmount = 0;
  60. private const int SlotCount = 1;
  61. bool urlBack = false;
  62. byte dataIndex = 1;
  63. private static Dictionary<string, int> _isInit = new();
  64. private void Awake()
  65. {
  66. if (!_isInit.TryGetValue(gameObject.name, out int instanceID) && instanceID == 0)
  67. {
  68. DontDestroyOnLoad(gameObject);
  69. Init();
  70. }
  71. }
  72. private void LOG(string msg)
  73. {
  74. Debug.Log($"<color=#00FF00>{msg}</color>");
  75. }
  76. private void Init()
  77. {
  78. _isInit[gameObject.name] = GetInstanceID();
  79. serialPortUtility = gameObject.GetComponent<SerialPortUtilityPro>();
  80. serialPortUtility.OpenMethod = openMode;
  81. serialPortUtility.DeviceName = PortName;
  82. serialPortUtility.StopBit = SerialPortUtilityPro.StopBitEnum.OneBit;
  83. serialPortUtility.DataBit = SerialPortUtilityPro.DataBitEnum.EightBit;
  84. serialPortUtility.Open();
  85. //serialPortUtility.ReadCompleteEventObject.AddListener(ReadStreamingBinary);
  86. //byte[] datas = new byte[8] { 0xAA, 0x08, 0x04, 0x01, 0x00, 0x00, 0x3C, 0xBB };
  87. //serialPortUtility.Write(datas);
  88. if (PortName.Contains("/dev/ttyS0"))
  89. {
  90. //拉取二维码
  91. RequestUrl(dataIndex);
  92. //拉取后台数据
  93. ReqSettingData();
  94. }
  95. }
  96. void Update()
  97. {
  98. #region 测试代码
  99. #if UNITY_EDITOR
  100. if (Input.GetMouseButtonDown(0))
  101. {
  102. //TestPayQR();
  103. }
  104. #endif
  105. #endregion
  106. //if (serialPortUtility != null && serialPortUtility.IsOpened())
  107. // {
  108. // Debug.Log(serialPortUtility.GetSerialDebugString);
  109. // Debug.Log("串口接收到数据");
  110. // }
  111. }
  112. void TestPay()
  113. {
  114. if (!PortName.Contains("/dev/ttyS0"))
  115. return;
  116. Debug.LogError("TestPay");
  117. List<byte> data = new List<byte>();
  118. data.Add(0XAA);
  119. data.Add(0X55);//帧头
  120. data.Add(0x04);//帧长度
  121. data.Add(0x01);//数据编号
  122. data.Add(0XA6);//指令
  123. data.Add(0X01);//数据
  124. data.Add(0X00);//校验位
  125. PrintFrame(data.ToArray());
  126. }
  127. void TestPayQR()
  128. {
  129. if (!PortName.Contains("/dev/ttyS0"))
  130. return;
  131. List<byte> data = new List<byte>();
  132. data.Add(0XAA);
  133. data.Add(0X55);//帧头
  134. data.Add(0x05);//帧长度
  135. data.Add(0xDA);//数据编号
  136. data.Add(0XA1);//指令
  137. data.Add(0X02);//数据
  138. data.Add(0X64);//数据
  139. byte temp = 0;
  140. for (int i = 2; i < data.Count; i++)
  141. {
  142. temp ^= data[i];
  143. }
  144. data.Add(temp);//校验位
  145. Debug.LogError($"TestPayQR temp={temp}");
  146. PrintFrame(data.ToArray());
  147. }
  148. void OnUrlPayResponse(byte[] bytes)
  149. {
  150. if (!PortName.Contains("/dev/ttyS0"))
  151. return;
  152. //计算校验值
  153. byte tempResponseCheck = 0;
  154. for (int i = 2; i < bytes.Length - 1; i++)
  155. {
  156. tempResponseCheck ^= bytes[i];
  157. }
  158. byte responseCheck = bytes[bytes.Length - 1];
  159. //回复云上分
  160. List<byte> response = new List<byte>();
  161. response.Add(0XAA);
  162. response.Add(0X55);//帧头
  163. response.Add(0X04);//帧长
  164. var dataIndex = bytes[3];
  165. response.Add(dataIndex);//数据编号
  166. response.Add(0XA1);//数据编号
  167. var data = responseCheck == tempResponseCheck ? 0X01 : 0x00;
  168. response.Add((byte)data);//数据
  169. var endIdx = bytes[3];
  170. byte temp = 0;
  171. for (int i = 2; i < response.Count; i++)
  172. {
  173. temp ^= response[i];
  174. }
  175. response.Add(temp);//校验位
  176. Debug.LogError($"responseCheck={responseCheck} tempResponseCheck={tempResponseCheck} data ={data} dataIndex={dataIndex} temp={temp}");
  177. serialPortUtility.Write(response.ToArray());
  178. if (data == 0X01)
  179. StandaloneAPI.InsertCoint(bytes[6]);
  180. }
  181. /// <summary>
  182. /// 请求后台数据
  183. /// </summary>
  184. public void ReqSettingData()
  185. {
  186. //数据包开始------->(1个字节)->AA
  187. //命令------------->(1个字节)->08(取值范围->00到FF)
  188. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  189. //校验------------->(1个字节)->0D(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  190. //数据包结束------->(1个字节)->55
  191. //例如:AA 08 05 0D 55
  192. List<byte> request = new List<byte>();
  193. request.Add(0xAA);
  194. request.Add(0x08);//命令
  195. request.Add(0x05);//数据长度
  196. byte temp = (byte)(request[1] ^ request[2]);
  197. request.Add(temp);//校验 (校验 = 命令 ^ 数据长度)
  198. request.Add(0x55);//结束
  199. serialPortUtility.Write(request.ToArray());
  200. LOG($"请求后台数据!");
  201. }
  202. /// <summary>
  203. /// 后台数据消息回复
  204. /// </summary>
  205. /// <param name="bytes"></param>
  206. 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
  207. {
  208. //数据包开始-------------------------- > (1个字节)->AA
  209. //命令--------------------------------->(1个字节)->08(取值范围->00到FF)
  210. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  211. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  212. var coinPreGame = bytes[3];
  213. UserSettings.ins.PerRoundCoin = coinPreGame;
  214. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  215. var time1 = bytes[4];
  216. var time2 = bytes[5];
  217. int time = (time1 << 8) | time2;
  218. UserSettings.ins.PerRoundSeconds = time;
  219. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  220. var changeTicket1 = bytes[6];
  221. var changeTicket2 = bytes[7];
  222. int changeTicket = (changeTicket1 << 8) | changeTicket2;
  223. UserSettings.ins.ChangeTicket = changeTicket;
  224. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  225. var totalCoinNum1 = bytes[8];
  226. var totalCoinNum2 = bytes[9];
  227. var totalCoinNum3 = bytes[10];
  228. var totalCoinNum4 = bytes[11];
  229. UserSettings.ins.TotalCoinsNum = (totalCoinNum1 << 24) | (totalCoinNum2 << 16) | (totalCoinNum3 << 8) | totalCoinNum4;
  230. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  231. var totalTicketNum1 = bytes[12];
  232. var totalTicketNum2 = bytes[13];
  233. var totalTicketNum3 = bytes[14];
  234. var totalTicketNum4 = bytes[15];
  235. UserSettings.ins.TotalTicketNum = (totalTicketNum1 << 24) | (totalTicketNum2 << 16) | (totalTicketNum3 << 8) | totalTicketNum4;
  236. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  237. UserSettings.ins.NeedCoin = bytes[16];
  238. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  239. UserSettings.ins.GiveTicket = bytes[17];
  240. //校验-------------------------------- > (1个字节)->1F(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  241. byte temp = 0;
  242. for (int i = 1; i < bytes.Length - 2; i++)
  243. {
  244. temp ^= bytes[i];
  245. }
  246. //数据包结束------------------------->(1个字节)->55
  247. LOG($"收到后台数据--->coinPreGame:{coinPreGame} time={time} 校验结果={bytes[18] == temp} temp={temp} bytes[18]={bytes[18]} !");
  248. }
  249. /// <summary>
  250. /// 请求保存后台数据
  251. /// </summary>
  252. public void ReqSavingSettingData() //AA 07 14 03 00 64 00 65 00 00 00 01 00 00 00 02 01 01 12 55
  253. {
  254. List<byte> request = new List<byte>();
  255. //数据包开始-------------------------- > (1个字节)->AA
  256. request.Add(0xAA);
  257. //命令--------------------------------->(1个字节)->07(取值范围->00到FF)
  258. request.Add(0x07);
  259. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  260. request.Add(0x14);
  261. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  262. request.Add((byte)UserSettings.ins.PerRoundCoin);
  263. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  264. int time = UserSettings.ins.PerRoundSeconds;
  265. request.Add((byte)(time >> 8));
  266. request.Add((byte)(time));
  267. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  268. int changeTicket = UserSettings.ins.ChangeTicket;
  269. request.Add((byte)(changeTicket >> 8));
  270. request.Add((byte)(changeTicket));
  271. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  272. int totalCoins = UserSettings.ins.TotalCoinsNum;
  273. request.Add((byte)(totalCoins >> 24));
  274. request.Add((byte)(totalCoins >> 16));
  275. request.Add((byte)(totalCoins >> 8));
  276. request.Add((byte)(totalCoins));
  277. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  278. int totalTicketNum = UserSettings.ins.TotalTicketNum;
  279. request.Add((byte)(totalTicketNum >> 24));
  280. request.Add((byte)(totalTicketNum >> 16));
  281. request.Add((byte)(totalTicketNum >> 8));
  282. request.Add((byte)(totalTicketNum));
  283. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  284. request.Add((byte)UserSettings.ins.NeedCoin);
  285. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  286. request.Add((byte)UserSettings.ins.GiveTicket);
  287. //校验-------------------------------- > (1个字节)->12(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  288. byte temp = 0;
  289. for (int i = 2; i < request.Count; i++)
  290. {
  291. temp ^= request[i];
  292. }
  293. request.Add(temp);
  294. //数据包结束------------------------->(1个字节)->55
  295. request.Add(0x55);//结束
  296. serialPortUtility.Write(request.ToArray());
  297. LOG($"请求保存后台数据!");
  298. }
  299. /// <summary>
  300. /// 保存后台数据回复
  301. /// </summary>
  302. private void OnSaveSettingDataBack(byte[] bytes)//AA 07 05 02 55
  303. {
  304. //--> 回复(保存后台数据)的命令
  305. //数据包开始------->(1个字节)->AA
  306. //命令------------->(1个字节)->07(取值范围->00到FF)
  307. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  308. //校验------------->(1个字节)->02(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  309. //数据包结束------->(1个字节)->55
  310. LOG($"保存后台数据回复!");
  311. }
  312. /// <summary>
  313. /// 串口读取二进制流数据
  314. /// </summary>
  315. /// <param name="data"></param>
  316. public void ReadStreamingBinary(object data)
  317. {
  318. var bin = data as byte[];
  319. PrintFrame(bin);
  320. }
  321. public void PrintFrame(byte[] bytes)
  322. {
  323. string logStr = "";
  324. for (int i = 0; i < bytes.Length; i++)
  325. {
  326. logStr += $"i={bytes[i]}-";
  327. }
  328. Debug.Log($"{PortName} 接收到的串口命令:{logStr}");
  329. if (bytes[0] == 0xAA)
  330. {
  331. if (bytes[1] == 0x55)//旧
  332. {
  333. if (!PortName.Contains("/dev/ttyS8"))
  334. return;
  335. if (bytes[4] == 0xA6)//投币
  336. {
  337. Debug.Log("旧版投币数量:" + bytes[4]);
  338. StandaloneAPI.InsertCoint(bytes[5]);
  339. }
  340. else if (bytes[4] == 0xAA) //支付盒子链接请求返回
  341. OnUrlResponse(bytes);
  342. else if (bytes[4] == 0xA1) //支付盒子支付返回(云上分)
  343. OnUrlPayResponse(bytes);
  344. }
  345. else
  346. {
  347. if (!PortName.Contains("/dev/ttyS0"))
  348. return;
  349. //0x04:为按键是否按下的命令
  350. if (bytes[1] == 0x04)//退出
  351. {
  352. Debug.Log("确认按键退出游戏命令");
  353. if (bytes[3] == 0x01)
  354. {
  355. Debug.Log("确认按下按键");
  356. AimHandler.ins.ExitIntoEvent();
  357. SendMessage(bytes, 3);
  358. return;
  359. }
  360. else if (bytes[3] == 0x00)
  361. {
  362. Debug.Log("松开按键");
  363. SendMessage(bytes, 3);
  364. return;
  365. }
  366. }
  367. else if (bytes[1] == 0x01)//投币
  368. {
  369. Debug.Log("确认投币命令");
  370. StandaloneAPI.InsertCoint(bytes[3]);
  371. SendMessage(bytes, 3);
  372. }
  373. else if (bytes[1] == 0x0F)//进入设置 AA 0F 05 0A 55
  374. OpenSetUp();
  375. else if(bytes[1] == 0x07)//回复保存后台数据 AA 07 05 02 55
  376. OnSaveSettingDataBack(bytes);
  377. 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
  378. OnSettingDataBack(bytes);
  379. }
  380. }
  381. }
  382. /// <summary>
  383. /// 打开设置
  384. /// </summary>
  385. private void OpenSetUp()
  386. {
  387. AudioMgr.ins.PlayBtn();
  388. GameObject settingsViewObj = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_SettingsView);
  389. settingsViewObj.GetComponent<SmartBow.SettingsView>().ShowBoxSound(true);
  390. }
  391. /// <summary>
  392. /// 请求二维码链接
  393. /// </summary>
  394. /// <param name="dataIndex">数据编号 从1开始</param>
  395. private void RequestUrl(byte dataIndex)
  396. {
  397. byte[] datas = new byte[7] { 0xAA, 0x55, 0x04, dataIndex, 0xAA, 0x01, 0xAE };
  398. datas[6] = GetXor(datas);
  399. serialPortUtility.Write(datas);
  400. Invoke("CheckURLBack", 5f);
  401. #if UNITY_EDITOR
  402. TestBackUrl();
  403. #endif
  404. }
  405. private void CheckURLBack()
  406. {
  407. if (!urlBack && dataIndex <= 3)
  408. {
  409. Debug.LogWarning("没有拉取到支付url");
  410. RequestUrl(++dataIndex);
  411. }
  412. }
  413. /// <summary>
  414. /// 测试URL回包
  415. /// </summary>
  416. private void TestBackUrl()
  417. {
  418. var url = $"http://pay.sy1999.com:8901?productNumber=SY4G4365";
  419. var urlData = System.Text.UTF8Encoding.Default.GetBytes(url);
  420. List<byte> data = new List<byte>();
  421. data.Add(0XAA); data.Add(0X55);//帧头
  422. data.Add((byte)(urlData.Length + 3));//帧长度(数据编号到校验位的所有数据长度)
  423. data.Add(0X01);//数据编号
  424. data.Add(0XAA);//指令
  425. data.AddRange(urlData);//数据
  426. data.Add(0X00);//校验位
  427. Debug.Log($"Test: url={url} length={urlData.Length}");
  428. PrintFrame(data.ToArray());
  429. }
  430. /// <summary>
  431. /// URL请求返回
  432. /// </summary>
  433. /// <param name="data"></param>
  434. void OnUrlResponse(byte[] bytes)
  435. {
  436. urlBack = true;
  437. int frameLength = bytes[2];
  438. frameLength = frameLength - 3;
  439. List<byte> datas = new List<byte>();
  440. var startPos = 5;
  441. for (int i = startPos; i < frameLength + startPos; i++)
  442. {
  443. datas.Add(bytes[i]);
  444. }
  445. byte[] bs = datas.ToArray();
  446. var url = System.Text.Encoding.Default.GetString(bs);
  447. StandaloneAPI.url = url;
  448. var homeView = FindObjectOfType<HomeView>();
  449. if (homeView != null)
  450. homeView.FlushUrlQR();
  451. }
  452. void SendMessage(byte[] bytes,int index)
  453. {
  454. List<byte> s = new List<byte>(bytes);
  455. s.RemoveAt(index);
  456. bytes = s.ToArray();
  457. serialPortUtility.Write(bytes);
  458. }
  459. byte GetXor(byte[] btyes)
  460. {
  461. byte temp = 0;
  462. for (int i = 2; i < btyes.Length; i++)
  463. {
  464. temp ^= btyes[i];
  465. }
  466. return temp;
  467. }
  468. private void OnDestroy()
  469. {
  470. if (_isInit.TryGetValue(gameObject.name, out var instanceID) && instanceID == GetInstanceID())
  471. {
  472. LOG($"{PortName} 串口程序被销毁");
  473. serialPortUtility.Close();
  474. _isInit.Remove(gameObject.name);
  475. }
  476. }
  477. }