Complete legacy operator UI and playout migration
This commit is contained in:
@@ -16,10 +16,15 @@ public sealed partial class MainWindow
|
||||
private readonly SemaphoreSlim _playoutCommandGate = new(1, 1);
|
||||
private IPlayoutEngine? _playoutEngine;
|
||||
private LegacyPlayoutWorkflow? _playoutWorkflow;
|
||||
private PlayoutOptions? _playoutOptions;
|
||||
private MutableLegacySceneCueCompositionOptionsSource? _legacyCompositionOptionsSource;
|
||||
private LegacySceneCueCompositionOptions? _lastEnabledLegacyComposition;
|
||||
private string? _playoutInitializationError;
|
||||
private int _playoutCommandInFlight;
|
||||
private int _playoutShutdownStarted;
|
||||
private int _browserCorrelationQuarantined;
|
||||
private int _operatorMutationQuarantined;
|
||||
private string? _operatorMutationQuarantineMessage;
|
||||
private Task? _legacyRefreshTask;
|
||||
private readonly LegacyRefreshEpoch<LegacyRefreshRuntimeStatus> _legacyRefreshEpoch =
|
||||
new(LegacyRefreshRuntimeStatus.Empty);
|
||||
@@ -30,6 +35,13 @@ public sealed partial class MainWindow
|
||||
{
|
||||
var options = PlayoutOptionsLoader.Load();
|
||||
var compositionOptions = PlayoutSceneCompositionFactory.Create(options);
|
||||
_playoutOptions = options;
|
||||
_legacyCompositionOptionsSource =
|
||||
new MutableLegacySceneCueCompositionOptionsSource(compositionOptions);
|
||||
_lastEnabledLegacyComposition = compositionOptions.BackgroundKind ==
|
||||
LegacySceneBackgroundKind.None
|
||||
? null
|
||||
: compositionOptions;
|
||||
_playoutEngine = PlayoutEngineFactory.Create(options);
|
||||
_playoutEngine.StatusChanged += OnPlayoutStatusChanged;
|
||||
if (_databaseRuntime is not null)
|
||||
@@ -37,7 +49,9 @@ public sealed partial class MainWindow
|
||||
_playoutWorkflow = LegacySceneRuntimeFactory.Create(
|
||||
_playoutEngine,
|
||||
_databaseRuntime.Executor,
|
||||
compositionOptions);
|
||||
compositionOptionsSource: _legacyCompositionOptionsSource,
|
||||
trustedManualSource: _manualNetSellStore,
|
||||
trustedViSource: _viManualListStore);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -1027,6 +1041,57 @@ public sealed partial class MainWindow
|
||||
private void ResetLegacyRefreshStatus() =>
|
||||
_legacyRefreshEpoch.Stop(_ => LegacyRefreshRuntimeStatus.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Provides the single fail-closed playout boundary used by every operator-data
|
||||
/// mutation. A missing engine/workflow is not an idle state: it means native
|
||||
/// state cannot be proven, so a write must not start.
|
||||
/// </summary>
|
||||
private bool CanStartOperatorMutation(bool familyWriteQuarantined)
|
||||
{
|
||||
var engine = _playoutEngine;
|
||||
var workflow = _playoutWorkflow;
|
||||
var status = engine?.Status;
|
||||
var workflowState = workflow?.State;
|
||||
var refreshStatus = GetLegacyRefreshStatus();
|
||||
var snapshot = new OperatorMutationGateSnapshot(
|
||||
IsFamilyWriteQuarantined: familyWriteQuarantined,
|
||||
IsGlobalWriteQuarantined: IsOperatorMutationQuarantined(),
|
||||
IsLifetimeCancellationRequested: _lifetimeCancellation.IsCancellationRequested,
|
||||
IsPlayoutCommandInFlight: Volatile.Read(ref _playoutCommandInFlight) != 0,
|
||||
IsPlayoutShutdownStarted: Volatile.Read(ref _playoutShutdownStarted) != 0,
|
||||
IsBrowserCorrelationQuarantined: IsBrowserCorrelationQuarantined(),
|
||||
IsEngineAvailable: engine is not null,
|
||||
IsWorkflowAvailable: workflow is not null,
|
||||
IsCommandAvailable: status?.IsCommandAvailable ?? false,
|
||||
IsPlayCompletionPending: status?.IsPlayCompletionPending ?? true,
|
||||
PreparedSceneName: status?.PreparedSceneName,
|
||||
OnAirSceneName: status?.OnAirSceneName,
|
||||
PreparedCutCode: workflowState?.PreparedCutCode,
|
||||
OnAirCutCode: workflowState?.OnAirCutCode,
|
||||
IsRefreshActive: refreshStatus.IsActive,
|
||||
IsRefreshTaskRunning: _legacyRefreshTask is { IsCompleted: false });
|
||||
|
||||
return OperatorMutationGate.CanStart(snapshot);
|
||||
}
|
||||
|
||||
private bool IsOperatorMutationQuarantined() =>
|
||||
Volatile.Read(ref _operatorMutationQuarantined) != 0;
|
||||
|
||||
private string GetOperatorMutationQuarantineMessage(string fallback) =>
|
||||
Volatile.Read(ref _operatorMutationQuarantineMessage) ?? fallback;
|
||||
|
||||
private void LatchOperatorMutationQuarantine(string message)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _operatorMutationQuarantined, 1, 0) == 0)
|
||||
{
|
||||
Volatile.Write(
|
||||
ref _operatorMutationQuarantineMessage,
|
||||
string.IsNullOrWhiteSpace(message)
|
||||
? "An operator-data mutation has an unknown outcome. Restart and reconcile every operator-data store before writing again."
|
||||
: message);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShutdownPlayoutRuntime()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _playoutShutdownStarted, 1) != 0)
|
||||
|
||||
Reference in New Issue
Block a user