From 0e9607f728601fafea7e172e87317eb3383b4393 Mon Sep 17 00:00:00 2001 From: _sega Date: Fri, 20 Feb 2026 10:56:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20ComWrapper=20=D0=B2=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Fonts/NEW/NexaRegular COOL.asset | 2 +- Assets/Scenes/GameScene3D.unity | 16 - .../GameFSM/CheckLeadersFromMenuState.cs | 4 +- Assets/Scripts/GameManager.cs | 2 +- Assets/Scripts/Inputs/COMinput.cs | 2 +- Assets/Scripts/Inputs/ComSystem/ComWrapper.cs | 502 ++++++++---------- 6 files changed, 215 insertions(+), 313 deletions(-) diff --git a/Assets/Fonts/NEW/NexaRegular COOL.asset b/Assets/Fonts/NEW/NexaRegular COOL.asset index df31775..956f73c 100644 --- a/Assets/Fonts/NEW/NexaRegular COOL.asset +++ b/Assets/Fonts/NEW/NexaRegular COOL.asset @@ -11349,7 +11349,7 @@ Material: - _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767} - _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0} - _FaceColor: {r: 1, g: 1, b: 1, a: 1} - - _GlowColor: {r: 1, g: 0, b: 0.175066, a: 1} + - _GlowColor: {r: 0.91854715, g: 0, b: 1, a: 1} - _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767} - _OutlineColor: {r: 0.554717, g: 0.554717, b: 0.554717, a: 1} - _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/Scenes/GameScene3D.unity b/Assets/Scenes/GameScene3D.unity index ba75d2e..8a7969d 100644 --- a/Assets/Scenes/GameScene3D.unity +++ b/Assets/Scenes/GameScene3D.unity @@ -5978,7 +5978,6 @@ GameObject: - component: {fileID: 259182289} - component: {fileID: 259182279} - component: {fileID: 259182281} - - component: {fileID: 259182282} - component: {fileID: 259182283} - component: {fileID: 259182284} - component: {fileID: 259182286} @@ -6112,21 +6111,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 644deb7c42b828041920ca8c69f133ad, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!114 &259182282 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 259182278} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 5f59acf8ffd30b44bbd96b3f99fb9c0a, type: 3} - m_Name: - m_EditorClassIdentifier: - saveMoneyBetweenSessions: 1 - enablePrizeChecking: 1 - keepAlive: 1 --- !u!114 &259182283 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GameFSM/CheckLeadersFromMenuState.cs b/Assets/Scripts/GameFSM/CheckLeadersFromMenuState.cs index d8b2c01..0730fb8 100644 --- a/Assets/Scripts/GameFSM/CheckLeadersFromMenuState.cs +++ b/Assets/Scripts/GameFSM/CheckLeadersFromMenuState.cs @@ -9,7 +9,7 @@ public class CheckLeadersFromMenuState : IState public void enter() { - ComWrapper.onInputRecieved.AddListener(ResetTimer); + ComWrapper.InputRecieved.AddListener(ResetTimer); lb = GameManager.instance.leaderboard; @@ -24,7 +24,7 @@ public class CheckLeadersFromMenuState : IState public void exit() { - ComWrapper.onInputRecieved.RemoveListener(ResetTimer); + ComWrapper.InputRecieved.RemoveListener(ResetTimer); lb.ClearRowsText(); lb.gameObject.SetActive(false); SoundManager.instance.playSwitch(); diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs index 6bde3a8..0866a14 100644 --- a/Assets/Scripts/GameManager.cs +++ b/Assets/Scripts/GameManager.cs @@ -452,7 +452,7 @@ public class GameManager : MonoBehaviour SettingsManager.Initialize(); GetComponent().Initialize(); - GetComponent().Initialize(); + ComWrapper.Initialize(); SingleWallet.Initialize(); diff --git a/Assets/Scripts/Inputs/COMinput.cs b/Assets/Scripts/Inputs/COMinput.cs index 5050f35..5068db4 100644 --- a/Assets/Scripts/Inputs/COMinput.cs +++ b/Assets/Scripts/Inputs/COMinput.cs @@ -38,7 +38,7 @@ public class COMinput : InputSource LoadPinMappingConfiguration(); //ОБЯЗАТЕЛЬНЫЙ БЛОК - ComWrapper.onInputRecieved.AddListener(UpdateInputState); + ComWrapper.InputRecieved.AddListener(UpdateInputState); } catch (Exception e) diff --git a/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs b/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs index fcfdee0..b532fe6 100644 --- a/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs +++ b/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs @@ -1,43 +1,41 @@ -using System; -using System.Collections; +//Рефакторинг и перевод на асинхронку ComWrapper'a +//Надо потом еще логирование нормальное прикрутить и логгер сделать +using System; using System.Text; using UnityEngine; using UnityEngine.Events; - using System.Threading.Tasks; using System.Threading; +using System.Diagnostics; -public class ComWrapper : MonoBehaviour +public static class ComWrapper { //Настраиваем из инспектора - - [SerializeField] bool enablePrizeChecking = false; // Если хопер с проверкой наличия приза ставим true - [SerializeField] bool keepAlive = true; - public static UnityEvent PaymentReceived; - public static UnityEvent onInputRecieved; + //То, что нужно вынести в конфиг + //[SerializeField] bool enablePrizeChecking = false; // Если хопер с проверкой наличия приза ставим true + //[SerializeField] bool keepAlive = true; + static bool enablePrizeChecking = false; // Если хопер с проверкой наличия приза ставим true. Да и что то не вижу реализации. Возможно оно уже где-то в ComInput + static bool keepAlive = true; - private const string REQUEST_INPUT = "91"; + public static UnityEvent PaymentReceived; + public static event Action InputRecieved; + + private const string REQUEST_INPUT_COMMAND = "91"; private static int h;//идентификатор хэндла private static byte[] buf = new byte[1024]; //Байт-буфер куда будут приходить данные с порта - - bool boardEncrypted = false; - string encString; - string confirmString; - Coroutine keepAliveCoroutine; + private static ILogger logger; - //async - private CancellationTokenSource pollCancellationTokenSource; - - //===========================start: PUBLIC API===========================// - - public void Initialize() + private static CancellationTokenSource poll_cts, keepAlive_cts; + public static void Initialize()//Добавить логгер в конструктор { - onInputRecieved = new UnityEvent(); - onInputRecieved?.AddListener(InputRecievedCather); - StartCoroutine(SetupBoard()); + + //Тут надо с конфига подтянуть настройки! + PaymentReceived = new UnityEvent(); + OpenPort(); + SetupBoardAsync(); } public static void Send(string message) @@ -46,45 +44,163 @@ public class ComWrapper : MonoBehaviour { int w = ComNativeWrapper.Serial_WriteStringUTF8(h, message, 1); //w-количество записаных символов в порт. можно обработать } - catch { Debug.LogError("Ошибка отправки строкового сигнала."); } - } - - - - //===========================end: PUBLIC API===========================// - - - - - private void OnDisable() - { - StopAsyncPolling(); - ClosePort(); + catch { logger?.Log("Ошибка отправки строкового сигнала."); } } - private void OnApplicationQuit() + + private static async Task SetupBoardAsync() + { + bool auth = await BoardAuthAsync(); + if(!auth) (new GameObject()).AddComponent(typeof(VfxPrefab));//Запускаем отвал + + poll_cts = new CancellationTokenSource(); + keepAlive_cts = new CancellationTokenSource(); + + StartPollingAsync(); + StartKeepAliveProcessAsync(keepAlive_cts.Token); + + + } + + private static void OpenPort() + { + Log("Инициализация порта"); + //ComNativeWrapper.Serial_CloseAll();//или все таки h?, Пока не удаляем, посмотрим как сработает + + + var ports = ComNativeWrapper.ListPorts(); + if (ports.Length == 0) { Log("Нет портов, завершаю"); return; } + + string port = ports[^1]; + Log("Открываю порт: " + port); + + h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 4, 10); + + + Log($"handle: {h}"); + if (h <= 0) + { + Log("Open failed: " + ComNativeWrapper.GetLastError()); + return; + } + else + { + Log($"Открыл порт: {port}"); + } + } + + //прописать нормальные логи + private static async Task BoardAuthAsync() + { + bool boardEncrypted = true; //предполагаем что зашифрована + float timeout = 1; + var sw = new Stopwatch(); + + //Проверяем зашифрована или нет + sw.Start(); + RequestInputs(); + while (sw.Elapsed.TotalSeconds < timeout) + { + string entry = await ReadBufferLineAsync(); + if (entry.StartsWith("INPUT")) + { + boardEncrypted = false; + + break; + } + } + sw.Stop(); + + //Не шифрованная -> На выход, пользуемся. + if (!boardEncrypted) + return true; + + + //А тут возможно шифрованная + + Log("Шифрованная плата"); + + + //Запрашиваем шифрованную строку + string encryptedString = string.Empty; + Send("666"); + sw.Start(); + while (sw.Elapsed.Seconds < timeout) + { + string entry = await ReadBufferLineAsync(); + if (entry.StartsWith("ENC:")) + { + //пишем сразу без суффикса + encryptedString = entry.Substring(4); + break; + } + } + sw.Stop(); + + //Шифрованная строка не пришла -> идем нафиг + if (string.IsNullOrEmpty(encryptedString)) + { + Log($"Зашифрованная строка не пришла"); + + return false; + } + + //Раз пришла, надо расшифровывать + Log($"Расшифровываю {encryptedString}"); + string decryptedString = BoardUtility.DecryptSerial(encryptedString); + + //ждем подтверждение от платы + string confirmationString = string.Empty; + sw.Start(); + Send("777" + decryptedString); + while (sw.Elapsed.Seconds < timeout) + { + string entry = await ReadBufferLineAsync(); + if (entry.IndexOf("OK") != -1) + { + //Успех, плату активировали + sw.Stop(); + return true; + } + } + + //А вот тут мы не дождались подтверждения. Плохо, будем ломать игру. + Application.Quit(); + return false; + } + + private static void OnApplicationQuit() { StopAsyncPolling(); + StopKeepAlive(); ClosePort(); } - private void StopAsyncPolling() + private static void StopAsyncPolling() { - pollCancellationTokenSource?.Cancel(); - pollCancellationTokenSource?.Dispose(); - pollCancellationTokenSource = null; + poll_cts?.Cancel(); + poll_cts?.Dispose(); + poll_cts = null; } - private async void StartAsyncPolling() + + private static void StopKeepAlive() + { + keepAlive_cts?.Cancel(); + keepAlive_cts?.Dispose(); + keepAlive_cts = null; + } + + //прописать норм логи + private static async void StartPollingAsync() { try { - Logger.Instance.Log("Запуск асинхронного опроса порта"); + Log("Запуск асинхронного опроса порта"); - while (pollCancellationTokenSource != null && - !pollCancellationTokenSource.Token.IsCancellationRequested) + while (poll_cts != null && !poll_cts.Token.IsCancellationRequested) { - await Task.Delay(10, pollCancellationTokenSource.Token); + await Task.Delay(10, poll_cts.Token); - string comInput = await ReadBufferLineAsync(pollCancellationTokenSource.Token); + string comInput = await ReadBufferLineAsync(poll_cts.Token); if (!string.IsNullOrEmpty(comInput)) { // Прямой вызов - мы уже в главном потоке после await @@ -94,90 +210,35 @@ public class ComWrapper : MonoBehaviour } catch (OperationCanceledException) { - Logger.Instance.Log("Асинхронный опрос порта остановлен"); + Log("Асинхронный опрос порта остановлен"); } catch (Exception e) { - Logger.Instance.Log($"Ошибка асинхронного опроса: {e.Message}"); + Log($"Ошибка асинхронного опроса: {e.Message}"); } } - private void ProcessComInputImmediate(string comInput) + + //Логи + private static void ProcessComInputImmediate(string comInput) { - Debug.LogError("COM: " + comInput); + Log($"COM. h={h}: {comInput}"); - if (comInput == "COIN") PaymentReceived?.Invoke(); - if (comInput.StartsWith("INPUT")) onInputRecieved?.Invoke(comInput.Substring(5)); - if (comInput.StartsWith("ENC:")) encString = comInput; - if (comInput.StartsWith("ACTIVATED:")) confirmString = comInput; + if (comInput == "COIN") + PaymentReceived?.Invoke(); + else if (comInput.StartsWith("INPUT")) + InputRecieved?.Invoke(comInput.Substring(5)); } - - private void ToggleInputRecieved(int value) => PlayerPrefs.SetInt("inputToggle", value); - private int GetInputToggle() => PlayerPrefs.GetInt("inputToggle", 0); - - private void InputRecievedCather(string input) - { - if (!string.IsNullOrEmpty(input)) - { - //пришел инпут - ToggleInputRecieved(1); - } - - } - - #region Приватные методы - private void initPort() - { - Logger.Instance.Log("Инициализация порта"); - ComNativeWrapper.Serial_CloseAll();//или все таки h? - //ComNativeWrapper.Serial_Close(h); - - var ports = ComNativeWrapper.ListPorts(); - if (ports.Length == 0) { Debug.LogError("Нет портов, завершаю"); return; } - - string port = ports[^1]; - Logger.Instance.Log("Открываю порт: " + port); - - h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 4, 10); - - - Logger.Instance.Log($"handle: {h}"); - if (h <= 0) - { - Logger.Instance.Log("Open failed: " + ComNativeWrapper.GetLastError()); - return; - } - else - { - Logger.Instance.Log($"Открыл порт: {port}"); - } - } - + + #region Private Methods private static void ClosePort() { - Logger.Instance.Log($"Отпускаю порт handle:{h}"); + Log($"Отпускаю порт handle:{h}"); if (h > 0) { ComNativeWrapper.Serial_Close(h); h = 0; } } - - private static string ReadBufferLine() - { - - int read = ComNativeWrapper.Serial_ReadLineUTF8_bytes(h, buf, buf.Length); - if (read > 0) - { - var s = Encoding.UTF8.GetString(buf, 0, read); - return s; - } - else if (read < 0) - { - //DebugWindow.Message($"ReadLine err={read} :: {ComNativeWrapper.GetLastError()}"); - } - return ""; - } - private static async Task ReadBufferLineAsync(CancellationToken cancellationToken = default) { try @@ -198,216 +259,73 @@ public class ComWrapper : MonoBehaviour } catch (OperationCanceledException) { - Logger.Instance.Log("Чтение порта отменено"); + Log("Чтение порта отменено"); return string.Empty; } catch (Exception e) { - Logger.Instance.Log($"Ошибка асинхронного чтения: {e.Message}"); + Log($"Ошибка асинхронного чтения: {e.Message}"); return string.Empty; } } - private void requestInputs() => Send(REQUEST_INPUT); - - private void pollPort() - { - //Тут все просто, читаем что нам приходит с порта и сравниваем. - string comInput = ReadBufferLine(); - if (comInput != "") - { - Debug.Log("COM: " + comInput); - - if (comInput == "COIN") SingleWallet.AddCredit(); - if (comInput.StartsWith("INPUT")) onInputRecieved?.Invoke(comInput.Substring(5)); //инвокаю сразу без префикса "INPUT" - if (comInput.StartsWith("ENC:")) encString = comInput; - if (comInput.StartsWith("ACTIVATED:")) confirmString = comInput; - } - } - - private IEnumerator SetupBoard() - { - if (keepAliveCoroutine != null) StopCoroutine(keepAliveCoroutine); - - initPort(); - boardEncrypted = true; //предполагаем что зашифрована - - //пробуем получить инпуты. Вдруг плата осталась расшифрована - ToggleInputRecieved(0); - requestInputs(); - float timer = 1; - while (GetInputToggle() == 0 && timer > 0) - { - pollPort(); - timer -= Time.deltaTime; - yield return null; - } - //Есть инпут. незашифровано - if (GetInputToggle() == 1) - { - boardEncrypted = false; - } + private static void RequestInputs() + => Send(REQUEST_INPUT_COMMAND); - if (boardEncrypted) - { - Logger.Instance.Log("Шифрованная плата"); - //запрос - encString = ""; - Send("666"); - - //ждем пока придет ENC: - timer = 1; - while (string.IsNullOrEmpty(encString) && timer > 0) - { - pollPort(); - timer -= Time.deltaTime; - yield return null; - } - //получили ENC: строку или вышли по таймеру - - //Строка пришла - if (!string.IsNullOrEmpty(encString)) - { - Logger.Instance.Log($"Расшифровываю {encString}"); - - string decr = BoardUtility.DecryptSerial(ExtractData(encString)); - - //подтверждение - confirmString = ""; - Send("777" + decr); - } - else - { - Logger.Instance.Log($"Зашифрованная строка не пришла"); //отвал игры пускать - (new GameObject()).AddComponent(typeof(VfxPrefab)); - } - - - //ждем подтверждение от платы - timer = 2; - while (string.IsNullOrEmpty(confirmString) && timer > 0) - { - pollPort(); - timer -= Time.deltaTime; - yield return null; - } - if (string.IsNullOrEmpty(confirmString)) - { - Application.Quit(); - } - else - { - if (confirmString.IndexOf("OK") != -1) - { - Logger.Instance.Log("Активировано."); - } - else - { - Application.Quit(); - } - } - } - else - { - Logger.Instance.Log("НЕшифрованная плата"); - } - - //async - //Каждые 5 сек запрашиваем инпуты у платы. Надо потому что при заходе в игру с пинов еще не пришла инфа - /* if (keepAlive) - keepAliveCoroutine = StartCoroutine(KeepPortAliveProcess());*/ - - //async - pollCancellationTokenSource = new CancellationTokenSource(); - StartAsyncPolling(); - StartKeepAliveProcess(); - - - } - private async void StartKeepAliveProcess() - { - if (keepAlive) - { - await StartKeepAliveProcessAsync(pollCancellationTokenSource.Token); - } - } - private async Task StartKeepAliveProcessAsync(CancellationToken cancellationToken) + //Сделать ожидание простоя около минуты на проверку и переподелючение. + private static async Task StartKeepAliveProcessAsync(CancellationToken cancellationToken) { try { - Logger.Instance.Log("Запуск асинхронной проверки порта"); + Log("Запуск асинхронной проверки порта"); while (!cancellationToken.IsCancellationRequested) { - await SendRequestInputsAsync(cancellationToken); - await Task.Delay(2000, cancellationToken); // 2 секунды - - if (!await CheckPortConnectionAsync(cancellationToken)) + if (!await CheckPortConnectionAsync()) { - Logger.Instance.Log("Плата отвалилась. Запускаю переподключение."); + Log("Плата отвалилась. Запускаю переподключение."); await ReinitializePortAsync(cancellationToken); } - else - { - ToggleInputRecieved(0); - } - await Task.Delay(10000, cancellationToken); // 10 секунд + await Task.Delay(10000, cancellationToken); //Задержка } } catch (OperationCanceledException) { - Logger.Instance.Log("Асинхронная проверка порта остановлена"); + Log("Асинхронная проверка порта остановлена"); } catch (Exception e) { - Logger.Instance.Log($"Ошибка проверки порта: {e.Message}"); + Log($"Ошибка проверки порта: {e.Message}"); } } - private async Task SendRequestInputsAsync(CancellationToken cancellationToken) + + + private static async Task CheckPortConnectionAsync() { - try + var sw = Stopwatch.StartNew(); + float timeout = 1; + RequestInputs(); + while (sw.Elapsed.Seconds < timeout) { - requestInputs(); - await Task.CompletedTask; - } - catch (Exception e) - { - Logger.Instance.Log($"Ошибка отправки запроса инпутов: {e.Message}"); + string entry = await ReadBufferLineAsync(); + if(!string.IsNullOrEmpty(entry) && entry.StartsWith("INPUT")) + { + return true; + } } + sw.Stop(); + return false; } - private async Task CheckPortConnectionAsync(CancellationToken cancellationToken) + //реализовать + private static async Task ReinitializePortAsync(CancellationToken cancellationToken) { - // Простая проверка, не требует Task.Run - return await Task.FromResult(GetInputToggle() != 0); - } + ClosePort(); + await SetupBoardAsync(); + } - private async Task ReinitializePortAsync(CancellationToken cancellationToken) - { - await Task.Run(() => initPort(), cancellationToken); - } - - private static string ExtractData(string input) - { - const string encPrefix = "ENC:"; - - int encIndex = input.IndexOf(encPrefix); - - if (encIndex == -1) - { - Logger.Instance.Log($"Неверный формат шифрованной строки {input}"); - throw new ArgumentException("Invalid input format"); - } - - int dataStart = encIndex + encPrefix.Length; - - - return input.Substring(4); - } + private static void Log(string message) => logger?.Log($"CW{message}"); #endregion - - - }