Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
Архитектура COM и роли - ComMultiManager переименован в ComHub и переведен на фабричный паттерн обработчиков по ROLE. - Добавлен единый контракт обработчиков COM-контроллеров: - IComControllerHandler - DefaultComControllerHandler - Ответственность за оркестрацию перенесена в ComSystemManager, чтобы инициализация COM и жизненный цикл контроллеров управлялись централизованно. - ComWrapper сохранен как слой совместимости на время миграции логики в новую схему. - PrizeStandController отрефакторен под новый role-handler pipeline вместо старой прямой привязки. - Добавлен удобный доступ к PrizeStandController из менеджера системы там, где это требуется игровому коду. Handshake/авторизация платы (ComDeviceClient) - Авторизация платы переработана в строгую поэтапную последовательность: 1) ПК отправляет 07 2) ожидает CHAL 3) отправляет 08 в ответ на CHAL 4) ожидает RAES - Парсер стадий теперь принимает только ожидаемые токены на каждой стадии: - WaitingChal -> только CHAL - WaitingRaes -> только RAES - Любой посторонний вывод из serial во время handshake игнорируется (без немедленного падения), что устраняет ложные ошибки на нерелевантных COM-устройствах. - Если ожидаемый ответ после 07 не приходит, порт быстро пропускается как нерелевантный, без эскалации в критическую ошибку. - Для случаев с неподходящими/сторонними устройствами уровень логирования снижен с ошибок до предупреждений там, где это уместно. - Добавлена подробная диагностика авторизации (переходы стадий, принятые значимые кадры, контекст таймаутов) для упрощения отладки. Тестовая утилита handshake - Добавлен Assets/Temp/Handshake_Test.cs как изолированный диагностический скрипт. - Включены очень подробные логи по каждому шагу handshake и по каждому решению парсера. - Позволяет отдельно гонять тестовый сценарий и сравнивать поведение платы, не ломая production-поток. Стабильность COM и поведение reconnect - Подправлена логика keep-alive, чтобы исключить агрессивные реконнекты. - Reconnect теперь срабатывает только после нескольких последовательных пропусков INPUT-ответа, что снижает случайные обрывы COM и прерывания подсветки. Поведение подсветки во время выполнения - Циклы подсветки сделаны независимыми от Time.timeScale: - LightController: WaitForSeconds -> WaitForSecondsRealtime - LightSequence: WaitForSeconds -> WaitForSecondsRealtime - Это устраняет случайные паузы/замедления световых циклов при изменении игрового времени (пауза, slow-mo, временные изменения scale). Итоговый эффект - COM-стек стал расширяемым по ролям и проще в сопровождении. - Handshake стал детерминированным, устойчивым к serial-шуму и безопасным при работе со сторонними устройствами. - Циклы подсветки стабилизированы для длительных сессий и больше не зависят от масштабирования игрового времени.
95 lines
3.5 KiB
C#
95 lines
3.5 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace RR.Common.Controls
|
||
{
|
||
/// <summary>
|
||
/// Строитель последовательности световых эффектов.
|
||
/// Создаётся через <see cref="LightController.SetLight(LightMode,int)"/>.
|
||
/// <example><code>
|
||
/// SetLight(LightMode.VerticalWave, 3).Then(LightMode.DiagonalWave, 3).Loop();
|
||
/// SetLight(LightMode.VerticalWave, 3).Then(LightMode.Off);
|
||
/// SetLight(LightMode.Rainbow, 5);
|
||
/// </code></example>
|
||
/// </summary>
|
||
public class LightSequence
|
||
{
|
||
private readonly struct Step
|
||
{
|
||
public readonly LightMode Mode;
|
||
public readonly int DurationSeconds;
|
||
public Step(LightMode mode, int durationSeconds) { Mode = mode; DurationSeconds = durationSeconds; }
|
||
}
|
||
|
||
private readonly List<Step> _steps = new();
|
||
private readonly LightController _controller;
|
||
private bool _looping;
|
||
private Coroutine _coroutine;
|
||
|
||
internal LightSequence(LightController controller, LightMode firstMode, int firstDuration)
|
||
{
|
||
_controller = controller;
|
||
_steps.Add(new Step(firstMode, firstDuration));
|
||
}
|
||
|
||
/// <summary>Добавляет следующий эффект с временем по умолчанию.</summary>
|
||
public LightSequence Then(LightMode mode) => Then(mode, _controller.DefaultDuration);
|
||
|
||
/// <summary>Добавляет следующий эффект с указанным временем.</summary>
|
||
public LightSequence Then(LightMode mode, int durationSeconds)
|
||
{
|
||
_steps.Add(new Step(mode, durationSeconds));
|
||
return this;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Зацикливает последовательность. Для остановки — <see cref="LightController.StopSequence"/>.
|
||
/// </summary>
|
||
public LightSequence Loop()
|
||
{
|
||
_looping = true;
|
||
Play();
|
||
return this;
|
||
}
|
||
|
||
/// <summary>Запускает последовательность один раз.</summary>
|
||
public LightSequence Play()
|
||
{
|
||
Stop();
|
||
_coroutine = _controller.StartCoroutine(RunSequence());
|
||
return this;
|
||
}
|
||
|
||
/// <summary>Останавливает выполнение последовательности.</summary>
|
||
public void Stop()
|
||
{
|
||
if (_coroutine == null) return;
|
||
_controller.StopCoroutine(_coroutine);
|
||
_coroutine = null;
|
||
}
|
||
|
||
/// <summary>Возвращает <c>true</c>, если последовательность сейчас выполняется.</summary>
|
||
public bool IsPlaying => _coroutine != null;
|
||
|
||
// ReSharper disable once IteratorNeverReturns — цикл намеренно бесконечный при _looping=true
|
||
private IEnumerator RunSequence()
|
||
{
|
||
do
|
||
{
|
||
foreach (Step step in _steps)
|
||
{
|
||
_controller.SendCommand(step.Mode, step.DurationSeconds);
|
||
|
||
if (step.Mode != LightMode.Off && step.DurationSeconds > 0)
|
||
yield return new WaitForSecondsRealtime(step.DurationSeconds);
|
||
}
|
||
}
|
||
while (_looping);
|
||
|
||
_coroutine = null;
|
||
}
|
||
}
|
||
}
|
||
|