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 CmdControllerV1 = "666"; // Только для периферийника1 // Типы устройств public enum DeviceType { None, ControllerV1, // Периферийник1 ControllerV2, // Периферийник2 PrizeShowcase // Витрина } // Префиксы ответов private const string ControllerV1ResponsePrefix = "ENC:"; // Периферийник1 на 666 private const string ControllerV1NotActivatedPrefix = "NOT_ACTIVATED:"; // Периферийник1 на всё остальное 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}"); // ШАГ 1: Проверяем периферийник1 по команде 666 string response666 = CheckDevice(port, CmdControllerV1); if (response666 != null) { // Если ответ начинается с "ENC:" - это точно периферийник1 if (response666.StartsWith(ControllerV1ResponsePrefix) || response666.StartsWith("ALREADY_ACTIVATED")) { return DeviceType.ControllerV1; } } // ШАГ 2: Проверяем по команде 97 (витрина и периферийник2) string response97 = CheckDevice(port, CmdPrize); 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; } } // ШАГ 3: Дополнительная проверка для неактивированного периферийника1 // Если устройство ответило на 97 с "NOT_ACTIVATED:" - это периферийник1 if (response97 != null && response97.StartsWith(ControllerV1NotActivatedPrefix)) { 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) { 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]; int bytesRead = ComNativeWrapper.Serial_ReadLineUTF8_bytes(handle, buffer, buffer.Length); if (bytesRead <= 0) { return null; } string line = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim(); return string.IsNullOrEmpty(line) ? null : line; } finally { ComNativeWrapper.Serial_CloseAll(); } } }