Danil Iarosh abca043229
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
fix: fixed ComHub error of using string deviceId instead of DeviceDisconnectMessage object
2026-03-04 14:00:51 +03:00

416 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using RR.Game2D.Core.Managers;
namespace RR.Common.Controls
{
internal sealed class ComHub
{
private readonly Dictionary<string, ComDeviceClient> _devices = new();
private readonly Dictionary<string, IComControllerHandler> _handlers = new();
private readonly ConcurrentQueue<string> _coinQueue = new();
private readonly ConcurrentQueue<DeviceInputMessage> _inputQueue = new();
private readonly ConcurrentQueue<string> _authorizedQueue = new();
private readonly ConcurrentQueue<DeviceDisconnectMessage> _disconnectedQueue = new();
private readonly bool _useAllDetectedPorts;
private readonly string[] _explicitPorts;
private readonly string _defaultDevicePort;
private readonly bool _enableEncryptedHandshake;
private readonly int _handshakeTimeoutMs;
private readonly bool _keepAlive;
private readonly string _roleRequestCommand;
private readonly string _roleResponsePrefix;
private readonly int _roleRequestTimeoutMs;
private readonly string _mainControllerRole;
private readonly Func<string, string, IComControllerHandler> _handlerFactory;
private string _defaultDeviceId = string.Empty;
public ComHub(
bool useAllDetectedPorts,
string[] explicitPorts,
string defaultDevicePort,
bool enableEncryptedHandshake,
int handshakeTimeoutMs,
bool keepAlive,
string roleRequestCommand,
string roleResponsePrefix,
int roleRequestTimeoutMs,
string mainControllerRole,
Func<string, string, IComControllerHandler> handlerFactory)
{
_useAllDetectedPorts = useAllDetectedPorts;
_explicitPorts = explicitPorts ?? Array.Empty<string>();
_defaultDevicePort = defaultDevicePort ?? string.Empty;
_enableEncryptedHandshake = enableEncryptedHandshake;
_handshakeTimeoutMs = handshakeTimeoutMs;
_keepAlive = keepAlive;
_roleRequestCommand = roleRequestCommand ?? string.Empty;
_roleResponsePrefix = roleResponsePrefix ?? "ROLE";
_roleRequestTimeoutMs = roleRequestTimeoutMs;
_mainControllerRole = string.IsNullOrWhiteSpace(mainControllerRole) ? "main_controller" : mainControllerRole;
_handlerFactory = handlerFactory;
}
public string DefaultDeviceId => _defaultDeviceId;
public string[] ConnectedDeviceIds => _devices.Keys.ToArray();
public void InitializeDevices()
{
ComNativeWrapper.Serial_CloseAll();
string[] allPorts = ComNativeWrapper.ListPorts();
if (allPorts.Length == 0)
{
GameManager.Logger?.LogWarning("ComHub", "Не найдено COM-портов");
return;
}
string[] selectedPorts = SelectPorts(allPorts);
foreach (string port in selectedPorts)
{
TryCreateDevice(port);
}
ResolveDefaultDeviceId();
}
public void ShutdownAll(bool sendShutdownCommands)
{
foreach (ComDeviceClient client in _devices.Values)
{
UnsubscribeDevice(client);
client.Stop(sendShutdownCommands);
}
foreach (IComControllerHandler handler in _handlers.Values)
{
handler.Dispose();
}
_devices.Clear();
_handlers.Clear();
_defaultDeviceId = string.Empty;
while (_coinQueue.TryDequeue(out _))
{
}
while (_inputQueue.TryDequeue(out _))
{
}
while (_authorizedQueue.TryDequeue(out _))
{
}
while (_disconnectedQueue.TryDequeue(out _))
{
}
}
public bool SendToDefault(string message)
{
if (string.IsNullOrEmpty(_defaultDeviceId))
{
return false;
}
return SendToDevice(_defaultDeviceId, message);
}
public bool SendToDevice(string deviceId, string message)
{
if (string.IsNullOrWhiteSpace(deviceId))
{
return false;
}
if (!_devices.TryGetValue(deviceId, out ComDeviceClient client))
{
return false;
}
client.Send(message);
return true;
}
public void SendToAll(string message)
{
foreach (ComDeviceClient client in _devices.Values)
{
client.Send(message);
}
}
public string GetLastInputState(string deviceId)
{
if (string.IsNullOrWhiteSpace(deviceId))
{
return "11111111111111111111111111";
}
return _devices.TryGetValue(deviceId, out ComDeviceClient client)
? client.LastInputBits
: "11111111111111111111111111";
}
public bool TryDequeueCoin(out string deviceId) => _coinQueue.TryDequeue(out deviceId);
public bool TryDequeueInput(out DeviceInputMessage message) => _inputQueue.TryDequeue(out message);
public bool TryDequeueAuthorized(out string deviceId) => _authorizedQueue.TryDequeue(out deviceId);
public bool TryDequeueDisconnected(out DeviceDisconnectMessage message) => _disconnectedQueue.TryDequeue(out message);
public string GetDeviceRole(string deviceId)
{
if (string.IsNullOrWhiteSpace(deviceId))
{
return string.Empty;
}
return _devices.TryGetValue(deviceId, out ComDeviceClient client)
? client.ControllerRole
: string.Empty;
}
public string GetDeviceMac(string deviceId)
{
if (string.IsNullOrWhiteSpace(deviceId))
{
return string.Empty;
}
return _devices.TryGetValue(deviceId, out ComDeviceClient client)
? client.MacAddress
: string.Empty;
}
public PrizeStandController GetPrizeStandController(string deviceId = null)
{
if (!string.IsNullOrWhiteSpace(deviceId))
{
return _handlers.TryGetValue(deviceId, out IComControllerHandler handler)
? handler as PrizeStandController
: null;
}
foreach (IComControllerHandler handler in _handlers.Values)
{
if (handler is PrizeStandController standController)
{
return standController;
}
}
return null;
}
private string[] SelectPorts(string[] allPorts)
{
if (_useAllDetectedPorts)
{
return allPorts;
}
if (_explicitPorts.Length == 0)
{
return Array.Empty<string>();
}
HashSet<string> explicitSet = new(_explicitPorts.Where(p => !string.IsNullOrWhiteSpace(p)));
return allPorts.Where(explicitSet.Contains).ToArray();
}
private void ResolveDefaultDeviceId()
{
if (!string.IsNullOrWhiteSpace(_defaultDevicePort) && _devices.ContainsKey(_defaultDevicePort))
{
_defaultDeviceId = _defaultDevicePort;
return;
}
_defaultDeviceId = _devices.Keys.FirstOrDefault() ?? string.Empty;
}
private void TryCreateDevice(string port)
{
ComDeviceClient client = new(
port,
_enableEncryptedHandshake,
_handshakeTimeoutMs,
_keepAlive,
_roleRequestCommand,
_roleResponsePrefix,
_roleRequestTimeoutMs);
SubscribeDevice(client);
if (!client.Start())
{
UnsubscribeDevice(client);
return;
}
_devices[client.DeviceId] = client;
}
private void SubscribeDevice(ComDeviceClient client)
{
client.CoinAccepted += OnDeviceCoinAccepted;
client.InputReceived += OnDeviceInputReceived;
client.RawLineReceived += OnDeviceRawLineReceived;
client.Authorized += OnDeviceAuthorized;
client.Disconnected += OnDeviceDisconnected;
client.ConnectionLost += OnDeviceConnectionLost;
}
private void UnsubscribeDevice(ComDeviceClient client)
{
client.CoinAccepted -= OnDeviceCoinAccepted;
client.InputReceived -= OnDeviceInputReceived;
client.RawLineReceived -= OnDeviceRawLineReceived;
client.Authorized -= OnDeviceAuthorized;
client.Disconnected -= OnDeviceDisconnected;
client.ConnectionLost -= OnDeviceConnectionLost;
}
private void OnDeviceCoinAccepted(string deviceId)
{
if (_handlers.TryGetValue(deviceId, out IComControllerHandler handler))
{
handler.HandleCoinAccepted();
return;
}
_coinQueue.Enqueue(deviceId);
}
private void OnDeviceInputReceived(string deviceId, string inputBits)
{
if (_handlers.TryGetValue(deviceId, out IComControllerHandler handler))
{
handler.HandleInputReceived(inputBits);
return;
}
_inputQueue.Enqueue(new DeviceInputMessage(deviceId, inputBits));
}
private void OnDeviceRawLineReceived(string deviceId, string line)
{
if (_handlers.TryGetValue(deviceId, out IComControllerHandler handler))
{
handler.HandleRawLine(line);
}
}
private void OnDeviceAuthorized(string deviceId)
{
if (_devices.TryGetValue(deviceId, out ComDeviceClient client) &&
string.Equals(client.ControllerRole, _mainControllerRole, StringComparison.OrdinalIgnoreCase))
{
_defaultDeviceId = deviceId;
}
else if (string.IsNullOrWhiteSpace(_defaultDeviceId))
{
_defaultDeviceId = deviceId;
}
if (_handlers.TryGetValue(deviceId, out IComControllerHandler existing))
{
existing.Dispose();
}
_handlers[deviceId] = CreateHandlerForDevice(deviceId);
_authorizedQueue.Enqueue(deviceId);
}
private void OnDeviceDisconnected(string deviceId)
{
if (!_devices.TryGetValue(deviceId, out ComDeviceClient client))
{
return;
}
string role = client.ControllerRole;
string mac = client.MacAddress;
UnsubscribeDevice(client);
_devices.Remove(deviceId);
if (_handlers.TryGetValue(deviceId, out IComControllerHandler handler))
{
handler.Dispose();
_handlers.Remove(deviceId);
}
if (_defaultDeviceId == deviceId)
{
_defaultDeviceId = _devices.Keys.FirstOrDefault() ?? string.Empty;
}
_disconnectedQueue.Enqueue(new DeviceDisconnectMessage(deviceId, role, mac));
}
private void OnDeviceConnectionLost(string deviceId)
{
string role = string.Empty;
string mac = string.Empty;
if (_devices.TryGetValue(deviceId, out ComDeviceClient client))
{
role = client.ControllerRole;
mac = client.MacAddress;
}
_disconnectedQueue.Enqueue(new DeviceDisconnectMessage(deviceId, role, mac));
}
private IComControllerHandler CreateHandlerForDevice(string deviceId)
{
string role = GetDeviceRole(deviceId);
IComControllerHandler customHandler = _handlerFactory?.Invoke(deviceId, role);
if (customHandler != null)
{
return customHandler;
}
return new DefaultComControllerHandler(deviceId, _coinQueue, _inputQueue);
}
}
internal readonly struct DeviceInputMessage
{
public readonly string DeviceId;
public readonly string InputBits;
public DeviceInputMessage(string deviceId, string inputBits)
{
DeviceId = deviceId;
InputBits = inputBits;
}
}
internal readonly struct DeviceDisconnectMessage
{
public readonly string DeviceId;
public readonly string Role;
public readonly string Mac;
public DeviceDisconnectMessage(string deviceId, string role, string mac)
{
DeviceId = deviceId;
Role = role ?? string.Empty;
Mac = mac ?? string.Empty;
}
}
}