using System; using System.Collections.Generic; using System.Text; using UnityEngine; public static class ComPortScanner { // Параметры соединения private const int BaudRate = 115200; private const int DataBits = 8; private const int Parity = 0; // 0 = None private const int StopBits = 1; // 1 = One private const int ReadTimeoutMs = 5; private const int WriteTimeoutMs = 10; // Команды устройств private const string CmdPrize = "97"; // Для витрины и периферийника2 private const string CmdControllerAuth = "07"; private const int DeviceResponseTimeoutMs = 1000; // Типы устройств public enum DeviceType { None, ControllerV1, // Периферийник1 ControllerV2, // Периферийник2 PrizeShowcase // Витрина } // Префиксы ответов private const string RolePrefix = "ROLE :"; // Периферийник2 и витрина на 97 private const string PrizeRoleValue = "vitrina"; // Витрина: ROLE : vitrina /// /// Сканирует все доступные COM-порты и возвращает словарь {порт = тип устройства} /// public static Dictionary ScanPorts() { var result = new Dictionary(); string[] ports = ComNativeWrapper.ListPorts(); foreach (string port in ports) { if (port == "COM1") continue; DeviceType type = CheckPort(port); if (type != DeviceType.None) { result[port] = type; } } return result; } /// /// Проверяет один порт на наличие известных устройств /// private static DeviceType CheckPort(string port) { Debug.Log($"Проверка порта {port}"); string response97 = CheckDevice( port, CmdPrize, line => line.StartsWith(RolePrefix, StringComparison.Ordinal)); if (response97 != null && response97.StartsWith(RolePrefix)) { Debug.Log($"Role: {response97}"); // Извлекаем значение после "ROLE : " string roleValue = response97.Substring(RolePrefix.Length).Trim(); // Если роль = "vitrina" - это витрина if (string.Equals(roleValue, PrizeRoleValue, StringComparison.OrdinalIgnoreCase)) { return DeviceType.PrizeShowcase; } else { return DeviceType.ControllerV2; } } string computerChallenge = BoardAuthentication.CreateChallengeHex(); string authResponse = CheckDevice( port, CmdControllerAuth + computerChallenge, IsAuthenticationResponse); if (authResponse != null) return DeviceType.ControllerV1; return DeviceType.None; } /// /// Очищает входной буфер порта /// private static void ClearPortBuffer(int handle) { byte[] tempBuffer = new byte[1024]; const float idleTimeout = 0.5f; const int maxreads = 50; int reads = 0; float startTime = Time.unscaledTime; while (reads < maxreads) { int bytesRead = ComNativeWrapper.Serial_ReadLineUTF8_bytes(handle, tempBuffer, tempBuffer.Length); reads++; if (bytesRead > 0) { startTime = Time.unscaledTime; continue; } else if (bytesRead == 0) { float elapsed = Time.unscaledTime - startTime; if (elapsed >= idleTimeout) { break; } } else { break; } } } /// /// Проверяет устройство на порту и возвращает ответ /// private static string CheckDevice(string port, string command, Func isExpectedResponse) { int handle = ComNativeWrapper.Serial_Open(port, BaudRate, DataBits, Parity, StopBits, 10, WriteTimeoutMs); if (handle <= 0) { return null; } try { ClearPortBuffer(handle); int writeResult = ComNativeWrapper.Serial_WriteStringUTF8(handle, command, 1); if (writeResult < 0) { return null; } byte[] buffer = new byte[1024]; long deadline = DateTime.UtcNow.Ticks + DeviceResponseTimeoutMs * TimeSpan.TicksPerMillisecond; while (DateTime.UtcNow.Ticks < deadline) { int bytesRead = ComNativeWrapper.Serial_ReadLineUTF8_bytes(handle, buffer, buffer.Length); if (bytesRead <= 0) continue; string line = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim(); if (!string.IsNullOrEmpty(line) && isExpectedResponse(line)) return line; } return null; } finally { ComNativeWrapper.Serial_Close(handle); } } private static bool IsAuthenticationResponse(string line) => BoardAuthentication.TryExtractHexPayload(line, "CHAL", out _) || BoardAuthentication.TryExtractHexPayload(line, "RAES", out _); }