Complete legacy operator UI and playout migration

This commit is contained in:
2026-07-12 02:05:49 +09:00
parent d2e16bf0c2
commit ffb8f43c19
131 changed files with 48473 additions and 228 deletions

View File

@@ -14,7 +14,10 @@ internal static class LegacySceneRuntimeFactory
public static LegacyPlayoutWorkflow Create(
IPlayoutEngine engine,
IDataQueryExecutor executor,
LegacySceneCueCompositionOptions? compositionOptions = null)
LegacySceneCueCompositionOptions? compositionOptions = null,
ILegacySceneCueCompositionOptionsSource? compositionOptionsSource = null,
IS5025TrustedManualDataSource? trustedManualSource = null,
ITrustedViStockNameSnapshotSource? trustedViSource = null)
{
ArgumentNullException.ThrowIfNull(engine);
ArgumentNullException.ThrowIfNull(executor);
@@ -50,12 +53,14 @@ internal static class LegacySceneRuntimeFactory
var salesLoader = new S5080SceneDataLoader(executor);
var participantTrendLoader = new S5083SceneDataLoader(executor);
var indexTrendLoader = new S5084SceneDataLoader(executor);
var manualDataDirectory = Environment.GetEnvironmentVariable(
S5025TrustedManualFileDataSource.DirectoryEnvironmentVariable);
IS5025TrustedManualDataSource? trustedManualSource =
string.IsNullOrWhiteSpace(manualDataDirectory)
if (trustedManualSource is null)
{
var manualDataDirectory = Environment.GetEnvironmentVariable(
S5025TrustedManualFileDataSource.DirectoryEnvironmentVariable);
trustedManualSource = string.IsNullOrWhiteSpace(manualDataDirectory)
? null
: new S5025TrustedManualFileDataSource(manualDataDirectory);
}
var gridMarketRequestResolver = new LegacyGridMarketSceneRequestResolver(
executor,
trustedManualSource);
@@ -334,6 +339,10 @@ internal static class LegacySceneRuntimeFactory
RequireFirstPage(page);
var selection = RequireSelection(entry);
var target = ParseCandleMarket(selection.GroupCode);
var foreignIdentity = target is
CandleMarketTarget.OverseasIndex or CandleMarketTarget.OverseasStock
? ParseForeignCandleIdentity(selection.DataCode, target)
: default;
var request = new S8010SceneDataRequest(
ParseCandleAlias(entry.CutCode),
target,
@@ -342,7 +351,9 @@ internal static class LegacySceneRuntimeFactory
? RequireLookupValue(selection.Subject, "instrument")
: null,
HasClosedFlag(selection.GraphicType, "MA5"),
HasClosedFlag(selection.GraphicType, "MA20"));
HasClosedFlag(selection.GraphicType, "MA20"),
foreignIdentity.Symbol,
foreignIdentity.NationCode);
var data = await candleLoader.LoadAsync(
request,
cancellationToken).ConfigureAwait(false);
@@ -350,10 +361,30 @@ internal static class LegacySceneRuntimeFactory
}),
new LegacySceneDataSourceRoute(["5074"], async (entry, page, cancellationToken) =>
{
var request = CreatePagedQuoteRequest(RequireSelection(entry), page);
var selection = RequireSelection(entry);
var request = selection.GroupCode == "PAGED_VI"
? await TrustedViManualReference.ResolveAsync(
selection,
page,
trustedViSource,
cancellationToken).ConfigureAwait(false)
: CreatePagedQuoteRequest(selection, page);
var data = await fiveRowLoader.LoadAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5074", data, data.Rows.Count);
}, async (entry, cancellationToken) =>
{
var selection = RequireSelection(entry);
var request = selection.GroupCode == "PAGED_VI"
? await TrustedViManualReference.ResolveAsync(
selection,
pageIndexZeroBased: 0,
trustedSource: trustedViSource,
cancellationToken: cancellationToken).ConfigureAwait(false)
: CreatePagedQuoteRequest(selection, 0);
var data = await fiveRowLoader.LoadPagePlanAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5074", data, data.Rows.Count);
}),
new LegacySceneDataSourceRoute(["5077"], async (entry, page, cancellationToken) =>
{
@@ -361,6 +392,12 @@ internal static class LegacySceneRuntimeFactory
var data = await sixRowLoader.LoadAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5077", data, data.Rows.Count);
}, async (entry, cancellationToken) =>
{
var request = CreatePagedQuoteRequest(RequireSelection(entry), 0);
var data = await sixRowLoader.LoadPagePlanAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5077", data, data.Rows.Count);
}),
new LegacySceneDataSourceRoute(["5088"], async (entry, page, cancellationToken) =>
{
@@ -368,13 +405,25 @@ internal static class LegacySceneRuntimeFactory
var data = await twelveRowLoader.LoadAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5088", data, data.Rows.Count);
}, async (entry, cancellationToken) =>
{
var request = CreatePagedQuoteRequest(RequireSelection(entry), 0);
var data = await twelveRowLoader.LoadPagePlanAsync(request, cancellationToken)
.ConfigureAwait(false);
return new LegacySceneDataPage("s5088", data, data.Rows.Count);
})
]);
LegacySceneRuntimeCoverage.Validate(dataSource);
var cueProvider = new RegisteredLegacySceneCueProvider(
dataSource,
options: compositionOptions);
if (compositionOptions is not null && compositionOptionsSource is not null)
{
throw new ArgumentException(
"Specify either fixed or mutable scene composition options, not both.");
}
var cueProvider = compositionOptionsSource is null
? new RegisteredLegacySceneCueProvider(dataSource, options: compositionOptions)
: new RegisteredLegacySceneCueProvider(dataSource, compositionOptionsSource);
return new LegacyPlayoutWorkflow(engine, cueProvider);
}
@@ -448,6 +497,34 @@ internal static class LegacySceneRuntimeFactory
CandleMarketTarget.KospiStock or
CandleMarketTarget.KosdaqStock;
private static (string? Symbol, string? NationCode) ParseForeignCandleIdentity(
string value,
CandleMarketTarget target)
{
var parts = value.Split('|', StringSplitOptions.None);
var expectedType = target switch
{
CandleMarketTarget.OverseasIndex => "0",
CandleMarketTarget.OverseasStock => "1",
_ => throw new LegacySceneDataException(
"The foreign candle target is invalid.")
};
if (parts.Length != 3 ||
parts[0].Length is < 1 or > 64 ||
parts[0].Any(character => char.IsControl(character) || char.IsSurrogate(character)) ||
parts[1].Length != 2 ||
parts[1].Any(character => character is < 'A' or > 'Z') ||
!string.Equals(parts[2], expectedType, StringComparison.Ordinal) ||
target == CandleMarketTarget.OverseasStock &&
parts[1] is not ("US" or "TW"))
{
throw new LegacySceneDataException(
"The foreign candle identity is invalid.");
}
return (parts[0], parts[1]);
}
private static string RequireLookupValue(string value, string label)
{
if (string.IsNullOrWhiteSpace(value))
@@ -500,6 +577,7 @@ internal static class LegacySceneRuntimeFactory
ParseNxtSession(selection.GraphicType),
page),
"PAGED_EXPERT" => new ExpertPagedQuoteLoadRequest(
RequireLookupValue(selection.DataCode, "expert code"),
RequireLookupValue(selection.Subject, "expert"),
page),
"PAGED_VI" => new ViPagedQuoteLoadRequest(