Files
Tornado3_2026Election/Tornado3_2026Election/Services/MockTornado3Adapter.cs
2026-05-14 09:38:45 +09:00

161 lines
5.5 KiB
C#

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tornado3_2026Election.Domain;
namespace Tornado3_2026Election.Services;
public sealed class MockTornado3Adapter : ITornado3Adapter
{
private readonly LogService _logService;
private TornadoConnectionState _state = TornadoConnectionState.Idle;
public MockTornado3Adapter(LogService logService)
{
_logService = logService;
}
public string BackendName => "Mock Adapter";
public bool IsLiveCg => false;
public bool IsConnected => false;
public string ConnectionTarget => "실CG 미연동";
public TornadoConnectionState State
{
get => _state;
private set
{
if (_state == value)
{
return;
}
_state = value;
StateChanged?.Invoke(this, value);
}
}
public event EventHandler<TornadoConnectionState>? StateChanged;
public event EventHandler? ConnectionChanged;
public async Task EnsureConnectedAsync(CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
await Task.Delay(120, cancellationToken).ConfigureAwait(false);
State = TornadoConnectionState.Ready;
_logService.Info("Tornado3 mock adapter connected.");
}, cancellationToken).ConfigureAwait(false);
}
public async Task ApplyCutAsync(
BroadcastChannel channel,
FormatTemplateDefinition template,
FormatCutDefinition cut,
ElectionDataSnapshot snapshot,
BroadcastStationProfile station,
string imageRootPath,
CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.Sending;
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
var summary = snapshot.BroadcastPhase == BroadcastPhase.PreElection
? $"turnout={snapshot.TurnoutRate:0.0}% voters={snapshot.TurnoutVotes:N0}"
: $"leader={snapshot.Candidates.OrderByDescending(candidate => candidate.VoteCount).FirstOrDefault()?.Name ?? ""} counted={snapshot.CountedVotes:N0}";
_logService.Info($"[{channel}] Apply {template.Name}/{cut.Name} station={station.Name} {summary} imageRoot={imageRootPath}");
State = TornadoConnectionState.Ready;
}, cancellationToken).ConfigureAwait(false);
}
public Task<bool> TryCapturePendingCutPreviewAsync(
BroadcastChannel channel,
string fileName,
int width,
int height,
int frame,
CancellationToken cancellationToken)
{
return Task.FromResult(false);
}
public Task<bool> TryCaptureCutPreviewAsync(
BroadcastChannel channel,
FormatTemplateDefinition template,
FormatCutDefinition cut,
ElectionDataSnapshot snapshot,
BroadcastStationProfile station,
string imageRootPath,
string fileName,
int width,
int height,
int frame,
CancellationToken cancellationToken)
{
return Task.FromResult(false);
}
public async Task PrepareAsync(BroadcastChannel channel, CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.Ready;
await Task.Delay(60, cancellationToken).ConfigureAwait(false);
_logService.Info($"[{channel}] Prepare");
}, cancellationToken).ConfigureAwait(false);
}
public async Task ShowPreparedFirstFrameAsync(BroadcastChannel channel, CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.Ready;
await Task.Delay(40, cancellationToken).ConfigureAwait(false);
_logService.Info($"[{channel}] Show prepared first frame on PGM");
}, cancellationToken).ConfigureAwait(false);
}
public async Task TakeAsync(BroadcastChannel channel, CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.OnAir;
await Task.Delay(60, cancellationToken).ConfigureAwait(false);
_logService.Info($"[{channel}] Take");
}, cancellationToken).ConfigureAwait(false);
}
public async Task ClearOutputAsync(BroadcastChannel channel, CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.Idle;
await Task.Delay(30, cancellationToken).ConfigureAwait(false);
_logService.Info($"[{channel}] Clear output layer");
}, cancellationToken).ConfigureAwait(false);
}
public async Task OutAsync(BroadcastChannel channel, CancellationToken cancellationToken)
{
await ExecuteWithTimeoutAsync(async () =>
{
State = TornadoConnectionState.Idle;
await Task.Delay(60, cancellationToken).ConfigureAwait(false);
_logService.Info($"[{channel}] Layer Out");
}, cancellationToken).ConfigureAwait(false);
}
private static async Task ExecuteWithTimeoutAsync(Func<Task> action, CancellationToken cancellationToken)
{
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeoutCts.CancelAfter(TimeSpan.FromSeconds(5));
await action().WaitAsync(timeoutCts.Token).ConfigureAwait(false);
}
}