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 _devices = new(); private readonly Dictionary _handlers = new(); private readonly ConcurrentQueue _coinQueue = new(); private readonly ConcurrentQueue _inputQueue = new(); private readonly ConcurrentQueue _authorizedQueue = new(); private readonly ConcurrentQueue _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 _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 handlerFactory) { _useAllDetectedPorts = useAllDetectedPorts; _explicitPorts = explicitPorts ?? Array.Empty(); _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(); } HashSet 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; } 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(deviceId); } private void OnDeviceConnectionLost(string deviceId) { _disconnectedQueue.Enqueue(deviceId); } 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; } } }