| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using Unity.VisualScripting;
- using UnityEngine;
- using UnityEngine.SceneManagement;
- using MySerialPortInterface;
- using MyInfraredInsertcoin;
- public class SerialPortExample : MonoBehaviour
- {
- public int baudrate = 115200;
- //使用自定义串口
- private SerialPortInterface serialPortInterface;
- private static int baseAddress;
- private static int offsetAddress;
- private Queue<byte> tempDataQueue;
- public string PortName = "";
- private int coinsAmount = 0;
- private const int SlotCount = 1;
- bool urlBack = false;
- byte dataIndex = 1;
- // private static Dictionary<string, int> _isInit = new();
-
- private void Start()
- {
- //LOG($"_isInit contains {gameObject.name}: {_isInit.ContainsKey(gameObject.name)}");
- //if (!_isInit.TryGetValue(gameObject.name, out int instanceID) && instanceID == 0)
- //{
- // DontDestroyOnLoad(gameObject);
- // Init();
- //}
- DontDestroyOnLoad(gameObject);
- Init();
- }
- private void LOG(string msg)
- {
- Debug.Log($"<color=#00FF00>{msg}</color>");
- }
- private void Init()
- {
- //_isInit[gameObject.name] = GetInstanceID();
- #if UNITY_ANDROID && !UNITY_EDITOR
- serialPortInterface = gameObject.GetComponent<SerialPortInterface>();
- serialPortInterface?.Open();
- LOG($"{PortName} 串口打开");
- if(serialPortInterface.DeviceName == "ttyS6")
- {
- //拉取二维码
- RequestUrl(dataIndex);
- //拉取后台数据
- ReqSettingData();
- }
- #endif
- #if UNITY_EDITOR
- TestBackUrl();
- //serialPortInterface = gameObject.GetComponent<SerialPortInterface>();
- //if (serialPortInterface.DeviceName == "ttyS6")
- //{
- // //List<byte> data = new List<byte>();
- // //data.Add(0XAA);
- // //data.Add(0X04);//帧头
- // //data.Add(0x06);//帧长度
- // //data.Add(0x00);//数据编号
- // //data.Add(0X02);//指令
- // //data.Add(0X55);//数据
- // //HandleSerialPortUpdate(data.ToArray(), 6);
- //}
- #endif
- }
- void Update()
- {
- #region 测试代码
- #if UNITY_EDITOR
- if (Input.GetMouseButtonDown(0))
- {
- //TestPayQR();
- }
- #endif
- #endregion
- //if (serialPortUtility != null && serialPortUtility.IsOpened())
- // {
- // Debug.Log(serialPortUtility.GetSerialDebugString);
- // Debug.Log("串口接收到数据");
- // }
- }
- void TestPay()
- {
- if (!PortName.Contains("/dev/ttyS0"))
- return;
- Debug.LogError("TestPay");
- List<byte> data = new List<byte>();
- data.Add(0XAA);
- data.Add(0X55);//帧头
- data.Add(0x04);//帧长度
- data.Add(0x01);//数据编号
- data.Add(0XA6);//指令
- data.Add(0X01);//数据
- data.Add(0X00);//校验位
- PrintFrame(data.ToArray());
- }
- public void TestSetting() {
- List<byte> data = new List<byte>();
- data.Add(0XAA);
- data.Add(0X0F);//
- data.Add(0x05);//
- data.Add(0x0A);//
- data.Add(0X55);//
- HandleSerialPortUpdate(data.ToArray(), 6);
- }
- void TestPayQR()
- {
- if (!PortName.Contains("/dev/ttyS0"))
- return;
-
- List<byte> data = new List<byte>();
- data.Add(0XAA);
- data.Add(0X55);//帧头
- data.Add(0x05);//帧长度
- data.Add(0xDA);//数据编号
- data.Add(0XA1);//指令
- data.Add(0X02);//数据
- data.Add(0X64);//数据
- byte temp = 0;
- for (int i = 2; i < data.Count; i++)
- {
- temp ^= data[i];
- }
- data.Add(temp);//校验位
- Debug.LogError($"TestPayQR temp={temp}");
- PrintFrame(data.ToArray());
- }
- void OnUrlPayResponse(byte[] bytes)
- {
- //计算校验值
- byte tempResponseCheck = 0;
- for (int i = 2; i < bytes.Length - 1; i++)
- {
- tempResponseCheck ^= bytes[i];
- }
- byte responseCheck = bytes[bytes.Length - 1];
- //回复云上分
- List<byte> response = new List<byte>();
- response.Add(0XAA);
- response.Add(0X55);//帧头
- response.Add(0X04);//帧长
- var dataIndex = bytes[3];
- response.Add(dataIndex);//数据编号
- response.Add(0XA1);//数据编号
- var data = responseCheck == tempResponseCheck ? 0X01 : 0x00;
- response.Add((byte)data);//数据
- var endIdx = bytes[3];
- byte temp = 0;
- for (int i = 2; i < response.Count; i++)
- {
- temp ^= response[i];
- }
- response.Add(temp);//校验位
- Debug.LogError($"responseCheck={responseCheck} tempResponseCheck={tempResponseCheck} data ={data} dataIndex={dataIndex} temp={temp}");
- serialPortInterface?.Write(response.ToArray());
- if (data == 0X01)
- StandaloneAPI.InsertCoint(bytes[6]);
- }
- /// <summary>
- /// 请求后台数据
- /// </summary>
- public void ReqSettingData()
- {
- //数据包开始------->(1个字节)->AA
- //命令------------->(1个字节)->08(取值范围->00到FF)
- //数据长度--------->(1个字节)->05(取值范围->00到FF)
- //校验------------->(1个字节)->0D(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
- //数据包结束------->(1个字节)->55
- //例如:AA 08 05 0D 55
- List<byte> request = new List<byte>();
- request.Add(0xAA);
- request.Add(0x08);//命令
- request.Add(0x05);//数据长度
- byte temp = (byte)(request[1] ^ request[2]);
- request.Add(temp);//校验 (校验 = 命令 ^ 数据长度)
- request.Add(0x55);//结束
- serialPortInterface?.Write(request.ToArray());
- LOG($"请求后台数据!");
- }
- /// <summary>
- /// 后台数据消息回复
- /// </summary>
- /// <param name="bytes"></param>
- 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
- {
- //数据包开始-------------------------- > (1个字节)->AA
- //命令--------------------------------->(1个字节)->08(取值范围->00到FF)
- //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
- //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
- var coinPreGame = bytes[3];
- UserSettings.ins.PerRoundCoin = coinPreGame;
- //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
- var time1 = bytes[4];
- var time2 = bytes[5];
- int time = (time1 << 8) | time2;
- UserSettings.ins.PerRoundSeconds = time;
- //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
- var changeTicket1 = bytes[6];
- var changeTicket2 = bytes[7];
- int changeTicket = (changeTicket1 << 8) | changeTicket2;
- UserSettings.ins.ChangeTicket = changeTicket;
- //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
- var totalCoinNum1 = bytes[8];
- var totalCoinNum2 = bytes[9];
- var totalCoinNum3 = bytes[10];
- var totalCoinNum4 = bytes[11];
- UserSettings.ins.TotalCoinsNum = (totalCoinNum1 << 24) | (totalCoinNum2 << 16) | (totalCoinNum3 << 8) | totalCoinNum4;
- //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
- var totalTicketNum1 = bytes[12];
- var totalTicketNum2 = bytes[13];
- var totalTicketNum3 = bytes[14];
- var totalTicketNum4 = bytes[15];
- UserSettings.ins.TotalTicketNum = (totalTicketNum1 << 24) | (totalTicketNum2 << 16) | (totalTicketNum3 << 8) | totalTicketNum4;
- //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
- UserSettings.ins.NeedCoin = bytes[16];
- //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
- UserSettings.ins.GiveTicket = bytes[17];
- //校验-------------------------------- > (1个字节)->1F(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
- byte temp = 0;
- for (int i = 1; i < bytes.Length - 2; i++)
- {
- temp ^= bytes[i];
- }
- //数据包结束------------------------->(1个字节)->55
- LOG($"收到后台数据--->coinPreGame:{coinPreGame} time={time} 校验结果={bytes[18] == temp} temp={temp} bytes[18]={bytes[18]} !");
- }
- /// <summary>
- /// 请求保存后台数据
- /// </summary>
- public void ReqSavingSettingData() //AA 07 14 03 00 64 00 65 00 00 00 01 00 00 00 02 01 01 12 55
- {
- List<byte> request = new List<byte>();
- //数据包开始-------------------------- > (1个字节)->AA
- request.Add(0xAA);
- //命令--------------------------------->(1个字节)->07(取值范围->00到FF)
- request.Add(0x07);
- //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
- request.Add(0x14);
- //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
- request.Add((byte)UserSettings.ins.PerRoundCoin);
- //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
- int time = UserSettings.ins.PerRoundSeconds;
- request.Add((byte)(time >> 8));
- request.Add((byte)(time));
- //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
- int changeTicket = UserSettings.ins.ChangeTicket;
- request.Add((byte)(changeTicket >> 8));
- request.Add((byte)(changeTicket));
- //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
- int totalCoins = UserSettings.ins.TotalCoinsNum;
- request.Add((byte)(totalCoins >> 24));
- request.Add((byte)(totalCoins >> 16));
- request.Add((byte)(totalCoins >> 8));
- request.Add((byte)(totalCoins));
- //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
- int totalTicketNum = UserSettings.ins.TotalTicketNum;
- request.Add((byte)(totalTicketNum >> 24));
- request.Add((byte)(totalTicketNum >> 16));
- request.Add((byte)(totalTicketNum >> 8));
- request.Add((byte)(totalTicketNum));
- //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
- request.Add((byte)UserSettings.ins.NeedCoin);
- //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
- request.Add((byte)UserSettings.ins.GiveTicket);
- //校验-------------------------------- > (1个字节)->12(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
- byte temp = 0;
- for (int i = 2; i < request.Count; i++)
- {
- temp ^= request[i];
- }
- request.Add(temp);
- //数据包结束------------------------->(1个字节)->55
- request.Add(0x55);//结束
- serialPortInterface?.Write(request.ToArray());
- LOG($"请求保存后台数据!");
- }
- /// <summary>
- /// 保存后台数据回复
- /// </summary>
- private void OnSaveSettingDataBack(byte[] bytes)//AA 07 05 02 55
- {
- //--> 回复(保存后台数据)的命令
- //数据包开始------->(1个字节)->AA
- //命令------------->(1个字节)->07(取值范围->00到FF)
- //数据长度--------->(1个字节)->05(取值范围->00到FF)
- //校验------------->(1个字节)->02(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
- //数据包结束------->(1个字节)->55
- LOG($"保存后台数据回复!");
- }
- /// <summary>
- /// 串口读取二进制流数据
- /// </summary>
- /// <param name="data"></param>
- public void ReadStreamingBinary(object data)
- {
- var bin = data as byte[];
- PrintFrame(bin);
- }
-
- //自定义端口使用,根据
- public void HandleSerialPortUpdate(byte[] buffer, int size)
- {
- //Debug.Log($"Received Data: {BitConverter.ToString(buffer)} of size {size}");
- //PrintFrame(buffer);
- // 将新的数据加入缓存
- dataCache.AddRange(buffer);
- // 尝试解析缓存中的数据
- while (TryParseNextFrame(ref dataCache, out byte[] frame))
- {
- // 如果成功解析一帧数据,处理该帧
- // Debug.Log($"[SerialPortExample] frame Data: {BitConverter.ToString(frame)}");
- PrintFrame(frame);
- }
- }
- public void PrintFrame(byte[] bytes)
- {
- //string logStr = "";
- //for (int i = 0; i < bytes.Length; i++)
- //{
- // logStr += $"i={bytes[i]}-";
- //}
- // Debug.Log($"<color=#FF0000>{PortName} 接收到的串口命令:{logStr}</color>");
- Debug.Log($"<color=#FF0000> [SerialPortExample] {PortName} 接收到的串口命令:{BitConverter.ToString(bytes)}, 长度{bytes.Length}</color>");
- if (bytes[0] == 0xAA)
- {
- if (bytes[1] == 0x55)//旧
- {
- if (bytes[4] == 0xA6)//投币
- {
- Debug.Log("旧版投币数量:" + bytes[4]);
- StandaloneAPI.InsertCoint(bytes[5]);
- }
- else if (bytes[4] == 0xAA) //支付盒子链接请求返回
- {
- Debug.Log("支付盒子链接请求返回:" + bytes[4]);
- OnUrlResponse(bytes);
- }
- else if (bytes[4] == 0xA1) //支付盒子支付返回(云上分)
- {
- Debug.Log("支付盒子支付返回(云上分):" + bytes[4]);
- OnUrlPayResponse(bytes);
- }
- }
- else
- {
- //0x04:为按键是否按下的命令
- if (bytes[1] == 0x04)//退出
- {
- Debug.Log("确认按键退出游戏命令");
- if (bytes[3] == 0x01)
- {
- Debug.Log("确认按下按键");
- // AimHandler.ins.ExitIntoEvent();
- if (SceneManager.GetActiveScene().name == "Home")
- {
- StandbyVideoManager._ins?.StopVideo();
- }
- else {
- SceneManager.LoadScene("Home", LoadSceneMode.Single);
- }
- SendMessage(bytes, 3);
- return;
- }
- else if (bytes[3] == 0x00)
- {
- Debug.Log("松开按键");
- SendMessage(bytes, 3);
- return;
- }
- }
- else if (bytes[1] == 0x01)//投币
- {
- Debug.Log("确认投币命令");
- StandaloneAPI.InsertCoint(bytes[3]);
- SendMessage(bytes, 3);
- }
- else if (bytes[1] == 0x0F)//进入设置 AA 0F 05 0A 55
- OpenSetUp();
- else if(bytes[1] == 0x07)//回复保存后台数据 AA 07 05 02 55
- OnSaveSettingDataBack(bytes);
- 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
- OnSettingDataBack(bytes);
- }
- }
- }
- /// <summary>
- /// 打开设置
- /// </summary>
- GameObject settingsViewObj;
- private void OpenSetUp()
- {
- AudioMgr.ins.PlayBtn();
- if (settingsViewObj) return;
- settingsViewObj = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_SettingsView);
- settingsViewObj.GetComponent<SmartBow.SettingsView>().ShowBoxSound(true);
- }
- /// <summary>
- /// 请求二维码链接
- /// </summary>
- /// <param name="dataIndex">数据编号 从1开始</param>
- private void RequestUrl(byte dataIndex)
- {
- LOG($"请求后台支付URL");
- byte[] datas = new byte[7] { 0xAA, 0x55, 0x04, dataIndex, 0xAA, 0x01, 0xAE };
- datas[6] = GetXor(datas);
- serialPortInterface?.Write(datas);
- Invoke("CheckURLBack", 5f);
- }
- private void CheckURLBack()
- {
- if (!urlBack && dataIndex <= 3)
- {
- Debug.LogWarning("没有拉取到支付url");
- RequestUrl(++dataIndex);
- }
- }
- /// <summary>
- /// 测试URL回包
- /// </summary>
- private void TestBackUrl()
- {
- var url = $"http://pay.sy1999.com:8901?productNumber=SY4G4365";
- var urlData = System.Text.UTF8Encoding.Default.GetBytes(url);
- List<byte> data = new List<byte>();
- data.Add(0XAA); data.Add(0X55);//帧头
- data.Add((byte)(urlData.Length + 3));//帧长度(数据编号到校验位的所有数据长度)
- data.Add(0X01);//数据编号
- data.Add(0XAA);//指令
- data.AddRange(urlData);//数据
- data.Add(0X00);//校验位
- Debug.Log($"Test: url={url} length={urlData.Length}");
- PrintFrame(data.ToArray());
- }
- /// <summary>
- /// URL请求返回
- /// </summary>
- /// <param name="data"></param>
- //void OnUrlResponse(byte[] bytes)
- //{
- // urlBack = true;
- // int frameLength = bytes[2];
- // frameLength = frameLength - 3;
- // List<byte> datas = new List<byte>();
- // var startPos = 5;
- // for (int i = startPos; i < frameLength + startPos; i++)
- // {
- // datas.Add(bytes[i]);
- // }
- // byte[] bs = datas.ToArray();
- // var url = System.Text.Encoding.Default.GetString(bs);
- // StandaloneAPI.url = url;
- // var homeView = FindObjectOfType<HomeView>();
- // if (homeView != null)
- // homeView.FlushUrlQR();
- // SerialPortHelper.ins.GetPort()?.RequestLightState(true);
- //}
- void OnUrlResponse(byte[] bytes)
- {
- // 检查数据长度
- if (bytes.Length < 3)
- {
- Debug.LogError("Received data too short.");
- return;
- }
- int frameLength = bytes[2] - 3;
- // 确保 frameLength 合理,防止越界
- if (frameLength < 0 || frameLength + 5 > bytes.Length)
- {
- Debug.LogError("Invalid frame length.");
- return;
- }
- urlBack = true;
- List<byte> datas = new List<byte>();
- var startPos = 5;
- // 提取数据
- for (int i = startPos; i < startPos + frameLength; i++)
- {
- if (i >= bytes.Length) break; // 防止越界
- datas.Add(bytes[i]);
- }
- byte[] bs = datas.ToArray();
- // 使用 UTF-8 编码解析 URL
- var url = System.Text.Encoding.UTF8.GetString(bs);
- StandaloneAPI.url = url;
- var homeView = FindObjectOfType<HomeView>();
- if (homeView != null)
- homeView.FlushUrlQR();
- SerialPortHelper.ins.GetPort()?.RequestLightState(true);
- }
- void SendMessage(byte[] bytes,int index)
- {
- List<byte> s = new List<byte>(bytes);
- s.RemoveAt(index);
- bytes = s.ToArray();
- serialPortInterface?.Write(bytes);
- }
- byte GetXor(byte[] btyes)
- {
- byte temp = 0;
- for (int i = 2; i < btyes.Length; i++)
- {
- temp ^= btyes[i];
- }
- return temp;
- }
- private void OnDestroy()
- {
- //if (_isInit.TryGetValue(gameObject.name, out var instanceID) && instanceID == GetInstanceID())
- //{
- // LOG($"{PortName} 串口程序被销毁");
- // serialPortInterface?.Close();
- // _isInit.Remove(gameObject.name);
- //}
- }
- #region 解析自定义缓存
- private List<byte> dataCache = new List<byte>(); // 用于缓存接收到的数据
- private const byte FrameHeader1 = 0xAA; // 帧头1
- private const byte FrameHeader2 = 0x55; // 帧头2
- // 尝试从缓存中解析出下一帧
- private bool TryParseNextFrame(ref List<byte> cache, out byte[] frame)
- {
- frame = null;
- // 检查缓存中是否有足够的数据进行解析(至少需要4个字节:帧头 + 帧长 + 数据编号 + 校验位)
- if (cache.Count < 4)
- return false;
- // 先检查以 0xAA 0x55 为帧头的格式
- for (int i = 0; i < cache.Count - 1; i++)
- {
- // 检查帧头:0xAA 0x55
- if (cache[i] == FrameHeader1 && cache[i + 1] == FrameHeader2)
- {
- // 找到帧头,检查数据长度
- if (cache.Count < i + 4) // 至少需要 4 字节:帧头 + 帧长 + 数据编号
- return false;
- // 获取帧长
- byte frameLength = cache[i + 2]; // 帧长是第3字节
- // 检查缓存中是否有足够的数据来构成一个完整的帧
- if (cache.Count < i + 3 + frameLength)
- return false;
- // 解析出一帧数据
- frame = cache.GetRange(i, 3 + frameLength).ToArray();
- // 移除已经解析的帧
- cache.RemoveRange(0, i + 3 + frameLength);
- return true;
- }
- }
- // 如果没有解析到 0xAA 0x55 格式的数据帧,再检查 0xAA 开头,0x55 结尾的格式
- for (int i = 0; i < cache.Count - 1; i++)
- {
- // 检查帧头:0xAA
- if (cache[i] == 0xAA)
- {
- // 找到帧头,继续向后查找数据是否满足格式
- // 查找帧尾:0x55
- for (int j = i + 1; j < cache.Count; j++)
- {
- if (cache[j] == 0x55)
- {
- // 确保数据包格式正确
- if (j - i < 4) // 至少需要 4 字节:命令 + 数据长度 + 校验 + 帧尾
- continue;
- // 获取完整的帧数据(包括帧头、命令、数据长度、数据、校验和帧尾)
- frame = cache.GetRange(i, j - i + 1).ToArray(); // 从帧头到帧尾的所有数据
- // 移除已经解析的帧
- cache.RemoveRange(0, j + 1); // 从缓存中移除已解析的部分(包括帧头和帧尾)
- return true;
- }
- }
- }
- }
- // 如果没有找到符合条件的帧
- return false;
- }
- #endregion
- }
|