179 lines
5.7 KiB
C#
179 lines
5.7 KiB
C#
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
|
||
|
||
/// <summary>
|
||
/// Сканирует все доступные COM-порты и возвращает словарь {порт = тип устройства}
|
||
/// </summary>
|
||
public static Dictionary<string, DeviceType> ScanPorts()
|
||
{
|
||
var result = new Dictionary<string, DeviceType>();
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет один порт на наличие известных устройств
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очищает входной буфер порта
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет устройство на порту и возвращает ответ
|
||
/// </summary>
|
||
private static string CheckDevice(string port, string command, Func<string, bool> 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 _);
|
||
}
|