From d3747c9a1d06662f6806e3b0a3f2f74de934e89a Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Fri, 26 Jun 2026 08:57:10 -0400 Subject: [PATCH 01/19] Updated ARES to support the new analysis capabilities. Still some work to do --- Ares.Core.Tests/AnalyzerManagerTests.cs | 4 +- .../Data/Analyzer/TestReplyAnalyzer.cs | 9 +- .../CampaignDatasetGeneratorTests.cs | 26 +- .../Execution/CampaignExecutorTests.cs | 4 +- Ares.Core/Analyzing/AnalysisHelper.cs | 38 +- Ares.Core/Analyzing/AnalysisRepo.cs | 4 +- Ares.Core/Analyzing/AnalyzerBase.cs | 4 +- Ares.Core/Analyzing/IAnalyzer.cs | 4 +- Ares.Core/Analyzing/NoneAnalyzer.cs | 9 +- Ares.Core/Analyzing/RemoteAnalyzer.cs | 4 +- Ares.Core/CoreDatabaseContext.cs | 3 + .../DataMappers/CampaignDatasetGenerator.cs | 41 +- .../Analyzer/AnalysisEntityConfiguration.cs | 8 +- .../Helpers/AnalysisInfoSerializeHelper.cs | 15 +- .../Helpers/SerializerSettingsHelper.cs | 1 + .../Execution/Executors/CampaignExecutor.cs | 33 +- .../StopConditions/DesiredAnalysisResult.cs | 3 +- Ares.Core/Planning/IPlanningHelper.cs | 2 +- Ares.Core/Planning/PlanningHelper.cs | 5 +- ..._AnalysisUpdates_AresDbContext.Designer.cs | 2070 ++++++++++++++++ ...624184742_AnalysisUpdates_AresDbContext.cs | 92 + ...sisUpdates_AresIdentityContext.Designer.cs | 277 +++ ...744_AnalysisUpdates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 9 +- ..._AnalysisUpdates_AresDbContext.Designer.cs | 2082 +++++++++++++++++ ...624184747_AnalysisUpdates_AresDbContext.cs | 89 + ...sisUpdates_AresIdentityContext.Designer.cs | 279 +++ ...750_AnalysisUpdates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 9 +- ..._AnalysisUpdates_AresDbContext.Designer.cs | 2065 ++++++++++++++++ ...624184736_AnalysisUpdates_AresDbContext.cs | 92 + ...sisUpdates_AresIdentityContext.Designer.cs | 268 +++ ...739_AnalysisUpdates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 9 +- UI/Features/Execution/ExecutionViewModel.cs | 16 +- 35 files changed, 7550 insertions(+), 90 deletions(-) create mode 100644 AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.cs diff --git a/Ares.Core.Tests/AnalyzerManagerTests.cs b/Ares.Core.Tests/AnalyzerManagerTests.cs index 05000409..cdead353 100644 --- a/Ares.Core.Tests/AnalyzerManagerTests.cs +++ b/Ares.Core.Tests/AnalyzerManagerTests.cs @@ -25,12 +25,12 @@ public TempAnalyzer(string name, string version) : base(name, "TempAnalyzer", ve { } - public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) { throw new NotImplementedException(); } - public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs b/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs index 28d58301..10a6dfa1 100644 --- a/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs +++ b/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs @@ -2,6 +2,7 @@ using Ares.Datamodel; using Ares.Datamodel.Analyzing; using Ares.Datamodel.Analyzing.Remote; +using Ares.Datamodel.Extensions; namespace Ares.Core.Tests.Data.Analyzer; @@ -11,15 +12,17 @@ public TestReplyAnalyzer() : base("Test Analyzer", "TestAnalyzer", "1.0") { } - public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) { var firstData = request.Inputs.Fields["TestAnalyzerInput"]; - var analysis = new Analysis() { Result = (float)firstData.NumberValue, AnalysisOutcome = Outcome.Success }; + var objective = new Objective() { ObjectiveName = "Objective", ObjectiveValue = firstData }; + var analysis = new AnalysisResponse() { Objectives = { objective }, AnalysisOutcome = Outcome.Success }; + return Task.FromResult(analysis); } - public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken) { return Analyze(request, cancellationToken); } diff --git a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs index 2d8130a2..0084d737 100644 --- a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs +++ b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs @@ -34,8 +34,8 @@ public async Task GenerateAsync_SortsExperimentRowsAndCreatesFixedColumns() var summary = CreateCampaignSummary( summaryId, "Campaign A", - CreateExperiment(secondStart, secondEnd, analysisResult: 2.5), - CreateExperiment(firstStart, firstEnd, analysisResult: 1.5)); + CreateExperiment(secondStart, secondEnd, analysisResult: new List { new Objective { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(2.5) } }), + CreateExperiment(firstStart, firstEnd, analysisResult: new List { new Objective { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(1.5) } })); var generator = CreateGenerator(summary); var dataset = GetDataset(await generator.GenerateAsync(summaryId), "Experiments"); @@ -538,7 +538,11 @@ public async Task GenerateAsync_CreatesPlannerTransactionRows() DateTime.UnixEpoch.AddSeconds(1), DateTime.UnixEpoch.AddSeconds(3), ("Temperature", nestedOutput)); - transaction.PlanningRequest.AnalysisResults.AddRange([1.5, 2.5]); + var objectiveOne = new Objective() { ObjectiveValue = AresValueHelper.CreateNumber(1.5), ObjectiveName = "ObjectiveOne" }; + var objectiveTwo = new Objective() { ObjectiveValue = AresValueHelper.CreateNumber(2.5), ObjectiveName = "ObjectiveTwo" }; + var responseOne = new AnalysisResponse() { AnalysisOutcome = Outcome.Success, Objectives = { objectiveOne } }; + var responseTwo = new AnalysisResponse() { AnalysisOutcome = Outcome.Success, Objectives = { objectiveTwo } }; + transaction.PlanningRequest.AnalysisResults.AddRange([responseOne, responseTwo]); transaction.PlanningResponse.PlanningOutcome = Outcome.Warning; transaction.PlanningResponse.ErrorString = "planner warning"; var generator = CreateGenerator(CreateCampaignSummary(summaryId, "Campaign A", experiment), [transaction]); @@ -583,9 +587,10 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() DateTime.UnixEpoch.AddSeconds(2), DateTime.UnixEpoch.AddSeconds(4), ("Measurement", nestedInput)); - transaction.AnalysisResponse = new Analysis + + transaction.AnalysisResponse = new AnalysisResponse { - Result = 9.5f, + Objectives = { new Objective() { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(9.5) } }, AnalysisOutcome = Outcome.Success, ErrorString = "analysis note" }; @@ -741,7 +746,7 @@ private static CampaignExecutionSummary CreateCampaignSummary( private static ExperimentExecutionSummary CreateExperiment( DateTime timeStarted, DateTime timeFinished, - double? analysisResult = null, + List? analysisResult = null, (string Name, AresValue Value)[] resultFields = null, Parameter[] parameters = null, StepExecutionSummary[] steps = null) @@ -752,7 +757,12 @@ private static ExperimentExecutionSummary CreateExperiment( }; if(analysisResult is not null) - overview.AnalysisOverview = new AnalysisOverview { Result = analysisResult.Value }; + { + overview.AnalysisOverview = new AnalysisOverview(); + overview.AnalysisOverview.Result.AddRange(analysisResult); + + } + foreach(var field in resultFields ?? []) { @@ -937,7 +947,7 @@ private static AnalyzerTransaction CreateAnalyzerTransaction( ExperimentId = experimentId } }, - AnalysisResponse = new Analysis() + AnalysisResponse = new AnalysisResponse() }; foreach(var input in inputs) diff --git a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs index f0f74be2..5948ff7f 100644 --- a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs +++ b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs @@ -57,7 +57,7 @@ public void SetUp() It.IsAny>(), It.IsAny(), It.IsAny>(), - It.IsAny>(), + It.IsAny>(), It.IsAny>(), It.IsAny())) .ReturnsAsync(true); @@ -216,7 +216,7 @@ public async Task Replan_Composes_And_Executes_Experiment_Again() It.IsAny>(), It.IsAny(), It.IsAny>(), - It.IsAny>(), + It.IsAny>(), It.IsAny>(), It.IsAny()), Times.Once); } diff --git a/Ares.Core/Analyzing/AnalysisHelper.cs b/Ares.Core/Analyzing/AnalysisHelper.cs index 11971e6b..e80891be 100644 --- a/Ares.Core/Analyzing/AnalysisHelper.cs +++ b/Ares.Core/Analyzing/AnalysisHelper.cs @@ -28,7 +28,7 @@ public AnalysisHelper(IAnalyzerRepo analyzerRepo, ILogger logger _notificationHandler = notificationHandler; } - public async Task Analyze(ExperimentTemplate template, ExperimentExecutionSummary experimentSummary, ExperimentExecutionSummary startupSummary, RequestMetadata metadata, CancellationToken cancellationToken) + public async Task Analyze(ExperimentTemplate template, ExperimentExecutionSummary experimentSummary, ExperimentExecutionSummary startupSummary, RequestMetadata metadata, CancellationToken cancellationToken) { try { @@ -40,7 +40,13 @@ public async Task Analyze(ExperimentTemplate template, ExperimentExecu var analyzerInputs = ExperimentOutputToAnalyzerInputs(combinedResult, template.AnalyzerMaps); if(analyzerInputs is null) - return new Analysis { Result = float.NaN, AnalysisOutcome = Outcome.Failure, ErrorString = "Analysis Failure: Failed to assign analysis " }; + { + return new AnalysisResponse + { + AnalysisOutcome = Outcome.Failure, + ErrorString = "Analysis Failure: Failed to assign analysis " + }; + } var analysisRequest = new AnalysisRequest { @@ -65,29 +71,43 @@ public async Task Analyze(ExperimentTemplate template, ExperimentExecu experimentSummary.ExperimentOverview.AnalysisOverview = new AnalysisOverview { UniqueId = Guid.NewGuid().ToString(), - Result = analysis.Result, AnalyzerInfo = await analyzer.CreateAnalyzerInfo(), ExperimentOverviewId = experimentSummary.ExperimentOverview.UniqueId }; - _logger.LogInformation("Analysis completed {Result}", analysis.Result); + experimentSummary.ExperimentOverview.AnalysisOverview.Result.AddRange(analysis.Objectives); + + _logger.LogInformation("Analysis completed {Result}", analysis.Objectives); await LogAnalyzerTransaction(transaction); return analysis; } + catch(RpcException e) { if(e.InnerException is OperationCanceledException oce) { - return new Analysis { Result = float.NaN, AnalysisOutcome = Outcome.Canceled, ErrorString = $" Analysis has been canceled" }; + return new AnalysisResponse + { + AnalysisOutcome = Outcome.Canceled, + ErrorString = $" Analysis has been canceled" + }; } - return new Analysis {Result = float.NaN, AnalysisOutcome = Outcome.Failure, ErrorString = $"Call to analyzer has failed: {e}" }; + + return new AnalysisResponse + { + AnalysisOutcome = Outcome.Failure, + ErrorString = $"Call to analyzer has failed: {e}" + }; } + catch(Exception e) { - return new Analysis { Result = float.NaN, AnalysisOutcome = Outcome.Failure, ErrorString = $"Call to analyzer has failed: {e}" }; + return new AnalysisResponse + { + AnalysisOutcome = Outcome.Failure, + ErrorString = $"Call to analyzer has failed: {e}" + }; } - - } private IAnalyzer GetAnalyzer(string? analyzerId) diff --git a/Ares.Core/Analyzing/AnalysisRepo.cs b/Ares.Core/Analyzing/AnalysisRepo.cs index 990fab39..b335e924 100644 --- a/Ares.Core/Analyzing/AnalysisRepo.cs +++ b/Ares.Core/Analyzing/AnalysisRepo.cs @@ -6,9 +6,9 @@ namespace Ares.Core.Analyzing; // This is a non-persistent storage of analyses as they come out of analyzers // mainly used to instanced functionality like tracking the analysis results to // decide when to stop the campaign -public class AnalysisRepo : Collection +public class AnalysisRepo : Collection { - public void StoreAnalysis(Analysis analysis) + public void StoreAnalysis(AnalysisResponse analysis) { Add(analysis); } diff --git a/Ares.Core/Analyzing/AnalyzerBase.cs b/Ares.Core/Analyzing/AnalyzerBase.cs index 847551b6..17af997f 100644 --- a/Ares.Core/Analyzing/AnalyzerBase.cs +++ b/Ares.Core/Analyzing/AnalyzerBase.cs @@ -69,9 +69,9 @@ public void UpdateSettings(AresStruct settings) } } - public abstract Task Analyze(AnalysisRequest request, CancellationToken cancellationToken); + public abstract Task Analyze(AnalysisRequest request, CancellationToken cancellationToken); - public abstract Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken); + public abstract Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken); public abstract Task GetParameters(CancellationToken cancellationToken); diff --git a/Ares.Core/Analyzing/IAnalyzer.cs b/Ares.Core/Analyzing/IAnalyzer.cs index 85d27afb..1d555418 100644 --- a/Ares.Core/Analyzing/IAnalyzer.cs +++ b/Ares.Core/Analyzing/IAnalyzer.cs @@ -100,7 +100,7 @@ public interface IAnalyzer /// /// Optional list of settings to influence the analysis /// which is the outcome of the analysis performed. - Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken = default); + Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken = default); /// /// Does the actual analysis work. @@ -108,7 +108,7 @@ public interface IAnalyzer /// The analysis request to be sent to the analyzer proto message /// /// which is the outcome of the analysis performed. - Task Analyze(AnalysisRequest request, CancellationToken cancellationToken); + Task Analyze(AnalysisRequest request, CancellationToken cancellationToken); /// /// How long do we expect the analyzer to do its analysis before ARES decides that analyzing has failed. diff --git a/Ares.Core/Analyzing/NoneAnalyzer.cs b/Ares.Core/Analyzing/NoneAnalyzer.cs index 9e6200f8..878efa87 100644 --- a/Ares.Core/Analyzing/NoneAnalyzer.cs +++ b/Ares.Core/Analyzing/NoneAnalyzer.cs @@ -19,18 +19,17 @@ public NoneAnalyzer() : base("NONE", "NONE :)", "1.0.0") AnalyzerState = State.Active; } - public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, CancellationToken cancellationToken) { - var analysis = new Analysis + var analysis = new AnalysisResponse { - AnalysisOutcome = Outcome.Success, - Result = 0 + AnalysisOutcome = Outcome.Success }; return Task.FromResult(analysis); } - public override Task Analyze(AnalysisRequest request, AresStruct _settings, CancellationToken cancellationToken) + public override Task Analyze(AnalysisRequest request, AresStruct _settings, CancellationToken cancellationToken) { return Analyze(request, cancellationToken); } diff --git a/Ares.Core/Analyzing/RemoteAnalyzer.cs b/Ares.Core/Analyzing/RemoteAnalyzer.cs index b4c58630..7c68468a 100644 --- a/Ares.Core/Analyzing/RemoteAnalyzer.cs +++ b/Ares.Core/Analyzing/RemoteAnalyzer.cs @@ -29,7 +29,7 @@ public RemoteAnalyzer(string name, Uri address) : base(name, "", "_._._") public Uri Address { get; } - public override async Task Analyze(AnalysisRequest request, CancellationToken cancellationToken = default) + public override async Task Analyze(AnalysisRequest request, CancellationToken cancellationToken = default) { var client = GetClient(); var analysis = await client.AnalyzeAsync(request, cancellationToken: cancellationToken); @@ -44,7 +44,7 @@ public override async Task Analyze(AnalysisRequest request, Cancellati /// These will add-to/override existing settings values if provided. /// /// - public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken = default) + public override Task Analyze(AnalysisRequest request, AresStruct settings, CancellationToken cancellationToken = default) { var client = GetClient(); var mergedSettings = Settings.AppendStruct(settings); diff --git a/Ares.Core/CoreDatabaseContext.cs b/Ares.Core/CoreDatabaseContext.cs index 4d32e44d..d06cbe12 100644 --- a/Ares.Core/CoreDatabaseContext.cs +++ b/Ares.Core/CoreDatabaseContext.cs @@ -67,6 +67,9 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura // Utilities configurationBuilder.Properties().HaveConversion(); + // Ignores + configurationBuilder.IgnoreAny(); + base.ConfigureConventions(configurationBuilder); } } diff --git a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs index c578867a..cbb3a9d3 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -63,16 +63,26 @@ public async ValueTask GenerateAsync(string summaryId, Cancellati var campaignEnd = summary.ExecutionInfo?.TimeFinished; if(campaignStart is not null && campaignEnd is not null) { - var plannerTransactions = await ctx.PlannerTransactions - .Where(transaction => transaction.TimeRequestSent >= campaignStart && transaction.TimeResponseReceived <= campaignEnd) - .ToListAsync(cancellationToken); - foreach(var transaction in plannerTransactions) + try { - cancellationToken.ThrowIfCancellationRequested(); - if(TryCreatePlannerRecord(transaction, summary, experimentNumbers, out var record)) - plannerRecords.Add(record); + var plannerTransactions = await ctx.PlannerTransactions + .Where(transaction => transaction.TimeRequestSent >= campaignStart && transaction.TimeResponseReceived <= campaignEnd) + .ToListAsync(cancellationToken); + + foreach(var transaction in plannerTransactions) + { + cancellationToken.ThrowIfCancellationRequested(); + if(TryCreatePlannerRecord(transaction, summary, experimentNumbers, out var record)) + plannerRecords.Add(record); + } + } + catch(Exception e) + { + throw e; } + + var analyzerTransactions = await ctx.AnalyzerTransactions .Where(transaction => transaction.TimeRequestSent >= campaignStart && transaction.TimeResponseReceived <= campaignEnd) .ToListAsync(cancellationToken); @@ -427,7 +437,10 @@ private static AresDataRow CreateExperimentRow(ExperimentExecutionSummary experi AddExecutionFields(data, experiment.ExecutionInfo); if(experiment.ExperimentOverview?.AnalysisOverview is not null) - data.Fields[AnalysisResultColumnName] = AresValueHelper.CreateNumber(experiment.ExperimentOverview.AnalysisOverview.Result); + { + foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Result) + data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + } foreach(var field in experiment.ExperimentOverview?.Result?.Fields ?? []) { @@ -513,7 +526,13 @@ private static AresDataRow CreatePlannerTransactionRow(PlannerRecord record, Can } if(transaction.PlanningRequest?.AnalysisResults.Count > 0) - data.Fields[AnalysisResultsColumnName] = AresValueHelper.CreateList(transaction.PlanningRequest.AnalysisResults.Select(AresValueHelper.CreateNumber)); + { + foreach(var analysisResponse in transaction.PlanningRequest.AnalysisResults) + { + foreach(var objective in analysisResponse.Objectives) + data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + } + } return new AresDataRow { Data = data }; } @@ -532,7 +551,9 @@ private static AresDataRow CreateAnalyzerTransactionRow(AnalyzerRecord record, C if(transaction.AnalysisResponse is not null) { - data.Fields[ResultColumnName] = AresValueHelper.CreateNumber(transaction.AnalysisResponse.Result); + foreach(var objective in transaction.AnalysisResponse.Objectives) + data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); } diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs index 46a88e9b..94058346 100644 --- a/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs @@ -1,14 +1,16 @@ -using Ares.Datamodel.Analyzing; +using Ares.Core.EntityConfigurations.Helpers; +using Ares.Datamodel.Analyzing; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Ares.Core.EntityConfigurations.Analyzer; -internal class AnalysisEntityConfiguration : AresEntityTypeBaseConfiguration +internal class AnalysisEntityConfiguration : AresEntityTypeBaseConfiguration { - public override void Configure(EntityTypeBuilder builder) + public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); + builder.Property(a => a.Objectives).HasObjectives(); builder.ToTable("Analyses"); } } diff --git a/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs b/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs index 215384c3..5d105a47 100644 --- a/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs +++ b/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs @@ -1,5 +1,6 @@ using Ares.Datamodel.Analyzing; using Ares.Datamodel.Analyzing.Remote; +using Google.Protobuf.Collections; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using System.Text.Json; @@ -28,13 +29,23 @@ public static PropertyBuilder HasAnalysisRequest(this PropertyB .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); } - public static PropertyBuilder HasAnalysis(this PropertyBuilder value) + public static PropertyBuilder HasAnalysis(this PropertyBuilder value) { var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); return value.HasConversion( v => JsonSerializer.Serialize(v, settings), - v => JsonSerializer.Deserialize(v, settings) ?? new Analysis()) + v => JsonSerializer.Deserialize(v, settings) ?? new AnalysisResponse()) + .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); + } + + public static PropertyBuilder> HasObjectives(this PropertyBuilder> value) + { + var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); + + return value.HasConversion( + v => JsonSerializer.Serialize(v, settings), + v => JsonSerializer.Deserialize>(v, settings) ?? new RepeatedField()) .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); } } diff --git a/Ares.Core/EntityConfigurations/Helpers/SerializerSettingsHelper.cs b/Ares.Core/EntityConfigurations/Helpers/SerializerSettingsHelper.cs index ee75b524..288e40a3 100644 --- a/Ares.Core/EntityConfigurations/Helpers/SerializerSettingsHelper.cs +++ b/Ares.Core/EntityConfigurations/Helpers/SerializerSettingsHelper.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using System.Text.Json.Serialization; namespace Ares.Core.EntityConfigurations.Helpers; diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 14f176ed..2ba6653f 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -126,7 +126,7 @@ await _stateLoggerManager.EnableOverrideAsync(new Datamodel.Device.DeviceLogging try { var campaignPath = await InitializeCampaign(CampaignStartTime); - var analyses = new List(); + var analyses = new List(); ResetStatus(token); await NotifyCampaignStart(); @@ -206,7 +206,7 @@ private async Task NotifyCampaignStart() _logger.LogInformation("Campaign started named {CampaignName}", Template.Name); } - private async Task<(bool Success, ExperimentExecutionSummary Summary)> ExecuteStartup(List analyses, List experimentSummaries, ExecutionControlToken token) + private async Task<(bool Success, ExperimentExecutionSummary Summary)> ExecuteStartup(List analyses, List experimentSummaries, ExecutionControlToken token) { _logger.LogDebug("Startup template not null, will run startup script"); var startupExecutorResult = await GenerateExperimentExecutor(Template.StartupTemplate, analyses, experimentSummaries.Select(es => es.ExperimentOverview), token.CancellationToken); @@ -226,7 +226,11 @@ private async Task NotifyCampaignStart() return (true, startupSummary); } - private async Task ExecuteExperimentLoop(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, ExperimentExecutionSummary startupSummary) + private async Task ExecuteExperimentLoop(string campaignPath, + List analyses, + List experimentSummaries, + ExecutionControlToken token, + ExperimentExecutionSummary startupSummary) { var currentPhase = ExperimentPhase.Initialize; var currentExperimentPath = ""; @@ -306,7 +310,7 @@ private async Task ExecuteExperimentLoop(string campaignP } } - private async Task PrepareExperiment(List analyses, List experimentSummaries, ExecutionControlToken token) + private async Task PrepareExperiment(List analyses, List experimentSummaries, ExecutionControlToken token) { if(_currentExperimentTemplate is null) { @@ -402,7 +406,10 @@ private async Task ExecuteCurrentExperiment(string currentExper : await HandleError(failedCommandSummary, token); } - private async Task AnalyzeCurrentExperiment(ExperimentExecutionSummary startupSummary, List analyses, List experimentSummaries, ExecutionControlToken token) + private async Task AnalyzeCurrentExperiment(ExperimentExecutionSummary startupSummary, + List analyses, + List experimentSummaries, + ExecutionControlToken token) { if(token.IsCancelled) return ExperimentPhase.Canceled; @@ -411,6 +418,7 @@ private async Task AnalyzeCurrentExperiment(ExperimentExecution return ExperimentPhase.Failed; var result = await AnalyzeResult(_currentExecutorResult.ExperimentExecutor, _currentSummary, startupSummary, analyses, token); + if(!result.Continue) return result.Success ? ExperimentPhase.Canceled : ExperimentPhase.Failed; @@ -419,7 +427,7 @@ private async Task AnalyzeCurrentExperiment(ExperimentExecution return ExperimentPhase.Complete; } - private async Task ReplanCurrentExperiment(List analyses, List experimentSummaries, ExecutionControlToken token) + private async Task ReplanCurrentExperiment(List analyses, List experimentSummaries, ExecutionControlToken token) { if(_currentExperimentTemplate is null) { @@ -516,7 +524,7 @@ private async Task HandleError(CommandExecutionSummary cmdSumma } } - private async Task PlanExperiment(List analyses, + private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, ExecutionControlToken token) @@ -554,7 +562,7 @@ private async Task PlanExperiment(List analyses, private async Task<(bool Success, bool Continue)> AnalyzeResult(ExperimentExecutor experimentExecutor, ExperimentExecutionSummary experimentSummary, ExperimentExecutionSummary startupSummary, - List analyses, + List analyses, ExecutionControlToken token) { var metadata = new RequestMetadata @@ -579,11 +587,11 @@ private async Task PlanExperiment(List analyses, // The following are top level checks for analysis failure in case the // failure is not properly handled on the Analysis itself // which also has support for "success" and "error" message - if (analysis is null || analysis.Result == float.NaN) + if (analysis is null || !analysis.Objectives.Any()) { Status.AnalysisState = AnalysisState.AnalysisError; await _notifier.Notify("Analysis Failure", $"Analysis was reported as successful, but no actual analysis was provided. {analysis?.ErrorString ?? "No error string provided"}", NotificationSeverityEnum.Error); - _logger.LogError("Failed to analyze. The analysis result came back as {Result}", analysis?.Result); + _logger.LogError("Failed to analyze. The analysis result came back as {Result}", analysis?.Objectives); return (false, false); } @@ -614,6 +622,7 @@ private async Task PlanExperiment(List analyses, analyses.Add(analysis); _analysisRepo.Add(analysis); + // Here the analysis has failed, but the analyzer properly reported why // it failed via the analysis result. if(analysis.ErrorString != string.Empty && string.IsNullOrEmpty(analysis.ErrorString)) @@ -625,7 +634,7 @@ private async Task PlanExperiment(List analyses, return (true, true); } - private async Task<(bool Success, ExperimentExecutionSummary Summary)> ExecuteCloseout(List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess) + private async Task<(bool Success, ExperimentExecutionSummary Summary)> ExecuteCloseout(List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess) { _logger.LogInformation("Closeout template is set. Running the closeout script."); var closeoutExecutorResult = await GenerateExperimentExecutor(Template.CloseoutTemplate, analyses, experimentSummaries.Select(es => es.ExperimentOverview), token.CancellationToken); @@ -726,7 +735,7 @@ private static bool IsAwaitingResponse(ExperimentExecutionStatus status) .Any(cmd => cmd.State == ExecutionState.AwaitingUser)); private async Task GenerateExperimentExecutor(ExperimentTemplate template, - IEnumerable analyses, + IEnumerable analyses, IEnumerable previousExperiments, CancellationToken cancellationToken) { diff --git a/Ares.Core/Execution/StopConditions/DesiredAnalysisResult.cs b/Ares.Core/Execution/StopConditions/DesiredAnalysisResult.cs index 49e63b62..b68a3fc7 100644 --- a/Ares.Core/Execution/StopConditions/DesiredAnalysisResult.cs +++ b/Ares.Core/Execution/StopConditions/DesiredAnalysisResult.cs @@ -24,7 +24,8 @@ public bool ShouldStop() if (latestAnalysis is null) return false; - var analysisVal = latestAnalysis.Result; + //TODO: Fix to check things properly + var analysisVal = latestAnalysis.Objectives.FirstOrDefault()?.ObjectiveValue.FloatValue; var resultAchieved = analysisVal >= _desiredResult - _leeway && analysisVal <= _desiredResult + _leeway; if (resultAchieved) Message = $"Achieved result {analysisVal} which is within {_leeway} of {_desiredResult}"; diff --git a/Ares.Core/Planning/IPlanningHelper.cs b/Ares.Core/Planning/IPlanningHelper.cs index 7ac1c5e9..9cf22aba 100644 --- a/Ares.Core/Planning/IPlanningHelper.cs +++ b/Ares.Core/Planning/IPlanningHelper.cs @@ -21,7 +21,7 @@ public interface IPlanningHelper Task TryResolveParameters(IEnumerable plannerAllocations, RequestMetadata metadata, IEnumerable parameters, - IEnumerable seedAnalyses, + IEnumerable seedAnalyses, IEnumerable seedExperiments, CancellationToken cancellationToken); diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 1f154544..7442f6eb 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -32,7 +32,7 @@ public PlanningHelper(IPlannerServiceRepo plannerManager, public async Task TryResolveParameters(IEnumerable plannerAllocations, RequestMetadata metadata, IEnumerable parameters, - IEnumerable seedAnalyses, + IEnumerable seedAnalyses, IEnumerable seedExperiments, CancellationToken cancellationToken) { @@ -50,6 +50,7 @@ public async Task TryResolveParameters(IEnumerable plan var planGroup = plannerToMetadataMaps.GroupBy(pair => pair.Planner); var seedAnalysesArr = seedAnalyses.ToArray(); + foreach(var grouping in planGroup) { var planner = grouping.Key; @@ -71,7 +72,7 @@ public async Task TryResolveParameters(IEnumerable plan //Create the plan request. Store it in the transaction. var planRequest = new PlanningRequest(); planRequest.PlanningParameters.AddRange(plannableParameters.Select(parameter => ConvertToPlanningParameter(parameter, seedExperiments))); - planRequest.AnalysisResults.AddRange(seedAnalysesArr.Select(a => (double)a.Result)); + planRequest.AnalysisResults.AddRange(seedAnalysesArr); planRequest.Metadata = metadata; planTransaction.PlanningRequest = planRequest; diff --git a/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.Designer.cs new file mode 100644 index 00000000..76528d0b --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.Designer.cs @@ -0,0 +1,2070 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260624184742_AnalysisUpdates_AresDbContext")] + partial class AnalysisUpdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfo") + .HasColumnType("jsonb"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique(); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisOutcome") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ErrorString") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Objectives") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisRequest") + .HasColumnType("jsonb"); + + b.Property("AnalysisResponse") + .HasColumnType("jsonb"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("AnalyzerName") + .HasColumnType("text"); + + b.Property("AnalyzerType") + .HasColumnType("text"); + + b.Property("AnalyzerVersion") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TagName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandLatency") + .HasColumnType("jsonb"); + + b.Property("CommandRetryLimit") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("RetryCooldown") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignId") + .HasColumnType("text"); + + b.Property("CampaignName") + .HasColumnType("text"); + + b.Property("CampaignNotes") + .HasColumnType("text"); + + b.Property("CampaignTags") + .HasColumnType("text"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandId") + .HasColumnType("text"); + + b.Property("CommandName") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceName") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusMessage") + .HasColumnType("text"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uuid"); + + b.Property("VariableName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandDescription") + .HasColumnType("text"); + + b.Property("CommandId") + .HasColumnType("text"); + + b.Property("CommandName") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StatusCode") + .HasColumnType("integer"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("TemplateId") + .HasColumnType("text"); + + b.Property("VarName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AwaitUserInput") + .HasColumnType("boolean"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Error") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("StatusCode") + .HasColumnType("integer"); + + b.Property("Success") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeviceInfoId") + .HasColumnType("uuid"); + + b.Property("InputSchema") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OutputSchema") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceName") + .HasColumnType("text"); + + b.Property("DeviceSettings") + .HasColumnType("text"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.Property("IsSimulated") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Deltas") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IntervalMs") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LoggingEnabled") + .HasColumnType("boolean"); + + b.Property("LoggingType") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BaudRate") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PortName") + .HasColumnType("text"); + + b.Property("SerialId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Handling") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LocaltimeOffset") + .HasColumnType("text"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("TimeFinished") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeStarted") + .HasColumnType("timestamp with time zone"); + + b.Property("Timezone") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ResultOutputPath") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("TemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Maximum") + .HasColumnType("double precision"); + + b.Property("Minimum") + .HasColumnType("double precision"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uuid"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uuid"); + + b.Property("PlannerId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerInfoId") + .HasColumnType("uuid"); + + b.Property("ServiceName") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerType") + .HasColumnType("text"); + + b.Property("PlannerVersion") + .HasColumnType("text"); + + b.Property("PlanningRequest") + .HasColumnType("jsonb"); + + b.Property("PlanningResponse") + .HasColumnType("jsonb"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.Property("StepName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceType") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("OutputVarName") + .HasColumnType("text"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uuid"); + + b.Property("CampaignExperimentId") + .HasColumnType("uuid"); + + b.Property("CampaignStartupId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Resolved") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataSchema") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SourceJson") + .HasColumnType("jsonb") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NotPlannable") + .HasColumnType("boolean"); + + b.Property("OutputName") + .HasColumnType("text"); + + b.Property("ParameterId") + .HasColumnType("uuid"); + + b.Property("PlannerDescription") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("Schema") + .HasColumnType("text"); + + b.Property("Unit") + .HasColumnType("text"); + + b.Property("UseDefault") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChartTitle") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("GridH") + .HasColumnType("integer"); + + b.Property("GridW") + .HasColumnType("integer"); + + b.Property("GridX") + .HasColumnType("integer"); + + b.Property("GridY") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("integer"); + + b.Property("Paths") + .HasColumnType("jsonb"); + + b.Property("PollingRate") + .HasColumnType("integer"); + + b.Property("ShowDataLabels") + .HasColumnType("boolean"); + + b.Property("ShowMarkers") + .HasColumnType("boolean"); + + b.Property("Style") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AssociatedDeviceId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataType") + .HasColumnType("integer"); + + b.Property("IsPlottable") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Path") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.cs b/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.cs new file mode 100644 index 00000000..4a9fefc3 --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260624184742_AnalysisUpdates_AresDbContext.cs @@ -0,0 +1,92 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresDb +{ + /// + public partial class AnalysisUpdates_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "jsonb", + nullable: true); + + migrationBuilder.DropColumn( + name: "Result", + table: "AnalysisOverview"); + + migrationBuilder.DropColumn( + name: "Result", + table: "Analyses"); + + migrationBuilder.Sql(@" + UPDATE ""AnalyzerTransactions"" + SET ""AnalysisResponse"" = ( + '{""objectives"":[{""objectiveName"":""Legacy Result"",""objectiveValue"":{""numberValue"":' + || COALESCE(""AnalysisResponse""->>'result', ""AnalysisResponse""->>'Result') + || '}}],""analysisOutcome"":' + || COALESCE(""AnalysisResponse""->>'analysisOutcome', ""AnalysisResponse""->>'analysis_outcome', ""AnalysisResponse""->>'AnalysisOutcome', '0') + || CASE + WHEN ""AnalysisResponse""->>'errorString' IS NOT NULL THEN ',""errorString"":""' || (""AnalysisResponse""->>'errorString') || '""' + WHEN ""AnalysisResponse""->>'error_string' IS NOT NULL THEN ',""errorString"":""' || (""AnalysisResponse""->>'error_string') || '""' + WHEN ""AnalysisResponse""->>'ErrorString' IS NOT NULL THEN ',""errorString"":""' || (""AnalysisResponse""->>'ErrorString') || '""' + ELSE '' + END + || '}' + )::jsonb + WHERE COALESCE(""AnalysisResponse""->>'result', ""AnalysisResponse""->>'Result') IS NOT NULL; + "); + + // PlannerTransactions: Wipe the array if legacy data is detected + // Note: We check if it's an array first, then use path traversal '#>' to check for objectives + migrationBuilder.Sql(@" + UPDATE ""PlannerTransactions"" + SET ""PlanningRequest"" = jsonb_set(""PlanningRequest"", '{analysisResults}', '[]'::jsonb) + WHERE jsonb_typeof(""PlanningRequest""->'analysisResults') = 'array' + AND jsonb_array_length(""PlanningRequest""->'analysisResults') > 0 + AND ""PlanningRequest""#>'{analysisResults,0,objectives}' IS NULL; + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@" + UPDATE ""AnalyzerTransactions"" + SET ""AnalysisResponse"" = ( + '{""result"":' || (""AnalysisResponse""#>>'{objectives,0,objectiveValue,numberValue}') + || ',""analysisOutcome"":' || COALESCE(""AnalysisResponse""->>'analysisOutcome', '0') + || CASE + WHEN ""AnalysisResponse""->>'errorString' IS NOT NULL + THEN ',""errorString"":""' || (""AnalysisResponse""->>'errorString') || '""' + ELSE '' + END + || '}' + )::jsonb + WHERE ""AnalysisResponse""->'objectives' IS NOT NULL; + "); + + migrationBuilder.AddColumn( + name: "Result", + table: "AnalysisOverview", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "Result", + table: "Analyses", + type: "real", + nullable: true); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "Analyses"); + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..b056b588 --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.Designer.cs @@ -0,0 +1,277 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260624184744_AnalysisUpdates_AresIdentityContext")] + partial class AnalysisUpdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.cs b/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.cs new file mode 100644 index 00000000..c1afb0a0 --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260624184744_AnalysisUpdates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresIdentity +{ + /// + public partial class AnalysisUpdates_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs index 0d19c09f..f69da048 100644 --- a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs @@ -44,9 +44,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); - b.Property("Result") - .HasColumnType("double precision"); - b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -55,7 +52,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); - modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") .ValueGeneratedOnAdd() @@ -77,8 +74,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); - b.Property("Result") - .HasColumnType("real"); + b.Property("Objectives") + .HasColumnType("jsonb"); b.HasKey("UniqueId"); diff --git a/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.Designer.cs new file mode 100644 index 00000000..5922e4a8 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.Designer.cs @@ -0,0 +1,2082 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260624184747_AnalysisUpdates_AresDbContext")] + partial class AnalysisUpdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfo") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique() + .HasFilter("[ExperimentOverviewId] IS NOT NULL"); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisOutcome") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ErrorString") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalysisResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerName") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerType") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TagName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandLatency") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandRetryLimit") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("RetryCooldown") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignId") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignName") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignNotes") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignTags") + .HasColumnType("nvarchar(max)"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandId") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StatusMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("VariableName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandId") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("TemplateId") + .HasColumnType("nvarchar(max)"); + + b.Property("VarName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AwaitUserInput") + .HasColumnType("bit"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Error") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique() + .HasFilter("[CommandExecutionSummaryId] IS NOT NULL"); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("InputSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OutputSchema") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceName") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceSettings") + .HasColumnType("nvarchar(max)"); + + b.Property("DriverId") + .HasColumnType("nvarchar(max)"); + + b.Property("IsSimulated") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Deltas") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("IntervalMs") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LoggingEnabled") + .HasColumnType("bit"); + + b.Property("LoggingType") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BaudRate") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PortName") + .HasColumnType("nvarchar(max)"); + + b.Property("SerialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Handling") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LocaltimeOffset") + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("TimeFinished") + .HasColumnType("datetime2"); + + b.Property("TimeStarted") + .HasColumnType("datetime2"); + + b.Property("Timezone") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique() + .HasFilter("[CampaignExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique() + .HasFilter("[CommandExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique() + .HasFilter("[StepExecutionSummaryId] IS NOT NULL"); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ResultOutputPath") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("TemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Maximum") + .HasColumnType("float"); + + b.Property("Minimum") + .HasColumnType("float"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("ServiceName") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerType") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.Property("StepName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceType") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("OutputVarName") + .HasColumnType("nvarchar(max)"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExperimentId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignStartupId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Resolved") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique() + .HasFilter("[CampaignCloseoutId] IS NOT NULL"); + + b.HasIndex("CampaignExperimentId") + .IsUnique() + .HasFilter("[CampaignExperimentId] IS NOT NULL"); + + b.HasIndex("CampaignStartupId") + .IsUnique() + .HasFilter("[CampaignStartupId] IS NOT NULL"); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SourceJson") + .HasColumnType("nvarchar(max)") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NotPlannable") + .HasColumnType("bit"); + + b.Property("OutputName") + .HasColumnType("nvarchar(max)"); + + b.Property("ParameterId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Schema") + .HasColumnType("nvarchar(max)"); + + b.Property("Unit") + .HasColumnType("nvarchar(max)"); + + b.Property("UseDefault") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique() + .HasFilter("[ParameterId] IS NOT NULL"); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ChartTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("GridH") + .HasColumnType("int"); + + b.Property("GridW") + .HasColumnType("int"); + + b.Property("GridX") + .HasColumnType("int"); + + b.Property("GridY") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("int"); + + b.Property("Paths") + .HasColumnType("nvarchar(max)"); + + b.Property("PollingRate") + .HasColumnType("int"); + + b.Property("ShowDataLabels") + .HasColumnType("bit"); + + b.Property("ShowMarkers") + .HasColumnType("bit"); + + b.Property("Style") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssociatedDeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataType") + .HasColumnType("int"); + + b.Property("IsPlottable") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Path") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DriverId") + .HasColumnType("nvarchar(max)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.cs b/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.cs new file mode 100644 index 00000000..94149453 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260624184747_AnalysisUpdates_AresDbContext.cs @@ -0,0 +1,89 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresDb +{ + /// + public partial class AnalysisUpdates_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.DropColumn( + name: "Result", + table: "AnalysisOverview"); + + migrationBuilder.DropColumn( + name: "Result", + table: "Analyses"); + + migrationBuilder.Sql(@" + UPDATE [AnalyzerTransactions] + SET [AnalysisResponse] = + '{""objectives"":[{""objectiveName"":""Legacy Result"",""objectiveValue"":{""numberValue"":' + + COALESCE(JSON_VALUE([AnalysisResponse], '$.result'), JSON_VALUE([AnalysisResponse], '$.Result')) + + '}}],""analysisOutcome"":' + + COALESCE(JSON_VALUE([AnalysisResponse], '$.analysisOutcome'), JSON_VALUE([AnalysisResponse], '$.analysis_outcome'), JSON_VALUE([AnalysisResponse], '$.AnalysisOutcome'), '0') + + CASE + WHEN JSON_VALUE([AnalysisResponse], '$.errorString') IS NOT NULL THEN ',""errorString"":""' + JSON_VALUE([AnalysisResponse], '$.errorString') + '""' + WHEN JSON_VALUE([AnalysisResponse], '$.error_string') IS NOT NULL THEN ',""errorString"":""' + JSON_VALUE([AnalysisResponse], '$.error_string') + '""' + WHEN JSON_VALUE([AnalysisResponse], '$.ErrorString') IS NOT NULL THEN ',""errorString"":""' + JSON_VALUE([AnalysisResponse], '$.ErrorString') + '""' + ELSE '' + END + + '}' + WHERE COALESCE(JSON_VALUE([AnalysisResponse], '$.result'), JSON_VALUE([AnalysisResponse], '$.Result')) IS NOT NULL; + "); + + migrationBuilder.Sql(@" + UPDATE [PlannerTransactions] + SET [PlanningRequest] = JSON_MODIFY([PlanningRequest], '$.analysisResults', JSON_QUERY('[]')) + WHERE JSON_QUERY([PlanningRequest], '$.analysisResults') IS NOT NULL + AND JSON_QUERY([PlanningRequest], '$.analysisResults') <> '[]' + AND JSON_QUERY([PlanningRequest], '$.analysisResults[0].objectives') IS NULL; + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // AnalyzerTransactions: Revert back to the legacy JSON structure + migrationBuilder.Sql(@" + UPDATE [AnalyzerTransactions] + SET [AnalysisResponse] = + '{""result"":' + JSON_VALUE([AnalysisResponse], '$.objectives[0].objectiveValue.numberValue') + + ',""analysisOutcome"":' + COALESCE(JSON_VALUE([AnalysisResponse], '$.analysisOutcome'), '0') + + CASE + WHEN JSON_VALUE([AnalysisResponse], '$.errorString') IS NOT NULL + THEN ',""errorString"":""' + JSON_VALUE([AnalysisResponse], '$.errorString') + '""' + ELSE '' + END + + '}' + WHERE JSON_QUERY([AnalysisResponse], '$.objectives') IS NOT NULL; + "); + + migrationBuilder.AddColumn( + name: "Result", + table: "AnalysisOverview", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "Result", + table: "Analyses", + type: "real", + nullable: true); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "Analyses"); + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..094f2371 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.Designer.cs @@ -0,0 +1,279 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260624184750_AnalysisUpdates_AresIdentityContext")] + partial class AnalysisUpdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.cs b/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.cs new file mode 100644 index 00000000..2409666c --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260624184750_AnalysisUpdates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresIdentity +{ + /// + public partial class AnalysisUpdates_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs index 10fbf9df..90151e73 100644 --- a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs @@ -44,9 +44,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); - b.Property("Result") - .HasColumnType("float"); - b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -56,7 +53,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); - modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") .ValueGeneratedOnAdd() @@ -78,8 +75,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); - b.Property("Result") - .HasColumnType("real"); + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); b.HasKey("UniqueId"); diff --git a/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.Designer.cs new file mode 100644 index 00000000..09425843 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.Designer.cs @@ -0,0 +1,2065 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260624184736_AnalysisUpdates_AresDbContext")] + partial class AnalysisUpdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfo") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique(); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisOutcome") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ErrorString") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Objectives") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfoId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisRequest") + .HasColumnType("TEXT"); + + b.Property("AnalysisResponse") + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("AnalyzerName") + .HasColumnType("TEXT"); + + b.Property("AnalyzerType") + .HasColumnType("TEXT"); + + b.Property("AnalyzerVersion") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TagName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLatency") + .HasColumnType("TEXT"); + + b.Property("CommandRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("RetryCooldown") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignId") + .HasColumnType("TEXT"); + + b.Property("CampaignName") + .HasColumnType("TEXT"); + + b.Property("CampaignNotes") + .HasColumnType("TEXT"); + + b.Property("CampaignTags") + .HasColumnType("TEXT"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandId") + .HasColumnType("TEXT"); + + b.Property("CommandName") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("State") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StatusMessage") + .HasColumnType("TEXT"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("TEXT"); + + b.Property("VariableName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandDescription") + .HasColumnType("TEXT"); + + b.Property("CommandId") + .HasColumnType("TEXT"); + + b.Property("CommandName") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StatusCode") + .HasColumnType("INTEGER"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("TemplateId") + .HasColumnType("TEXT"); + + b.Property("VarName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AwaitUserInput") + .HasColumnType("INTEGER"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("StatusCode") + .HasColumnType("INTEGER"); + + b.Property("Success") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DeviceInfoId") + .HasColumnType("TEXT"); + + b.Property("InputSchema") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OutputSchema") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("DeviceSettings") + .HasColumnType("TEXT"); + + b.Property("DriverId") + .HasColumnType("TEXT"); + + b.Property("IsSimulated") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Deltas") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("IntervalMs") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LoggingEnabled") + .HasColumnType("INTEGER"); + + b.Property("LoggingType") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BaudRate") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PortName") + .HasColumnType("TEXT"); + + b.Property("SerialId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Handling") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LocaltimeOffset") + .HasColumnType("TEXT"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("TimeFinished") + .HasColumnType("TEXT"); + + b.Property("TimeStarted") + .HasColumnType("TEXT"); + + b.Property("Timezone") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ResultOutputPath") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("TemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Maximum") + .HasColumnType("REAL"); + + b.Property("Minimum") + .HasColumnType("REAL"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ParameterUniqueId") + .HasColumnType("TEXT"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerInfoId") + .HasColumnType("TEXT"); + + b.Property("ServiceName") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerType") + .HasColumnType("TEXT"); + + b.Property("PlannerVersion") + .HasColumnType("TEXT"); + + b.Property("PlanningRequest") + .HasColumnType("TEXT"); + + b.Property("PlanningResponse") + .HasColumnType("TEXT"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.Property("StepName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("OutputVarName") + .HasColumnType("TEXT"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("CampaignCloseoutId") + .HasColumnType("TEXT"); + + b.Property("CampaignExperimentId") + .HasColumnType("TEXT"); + + b.Property("CampaignStartupId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Resolved") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataSchema") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SourceJson") + .HasColumnType("TEXT") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("InitialValue") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NotPlannable") + .HasColumnType("INTEGER"); + + b.Property("OutputName") + .HasColumnType("TEXT"); + + b.Property("ParameterId") + .HasColumnType("TEXT"); + + b.Property("PlannerDescription") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("Schema") + .HasColumnType("TEXT"); + + b.Property("Unit") + .HasColumnType("TEXT"); + + b.Property("UseDefault") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsParallel") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ChartTitle") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("GridH") + .HasColumnType("INTEGER"); + + b.Property("GridW") + .HasColumnType("INTEGER"); + + b.Property("GridX") + .HasColumnType("INTEGER"); + + b.Property("GridY") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("NumberDisplayPoints") + .HasColumnType("INTEGER"); + + b.Property("Paths") + .HasColumnType("TEXT"); + + b.Property("PollingRate") + .HasColumnType("INTEGER"); + + b.Property("ShowDataLabels") + .HasColumnType("INTEGER"); + + b.Property("ShowMarkers") + .HasColumnType("INTEGER"); + + b.Property("Style") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AssociatedDeviceId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataType") + .HasColumnType("INTEGER"); + + b.Property("IsPlottable") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DisplayName") + .HasColumnType("TEXT"); + + b.Property("DriverId") + .HasColumnType("TEXT"); + + b.Property("FileSizeBytes") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.cs b/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.cs new file mode 100644 index 00000000..5c789426 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260624184736_AnalysisUpdates_AresDbContext.cs @@ -0,0 +1,92 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresDb; + +/// +public partial class AnalysisUpdates_AresDbContext : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "TEXT", + nullable: true); + + migrationBuilder.DropColumn( + name: "Result", + table: "AnalysisOverview"); + + migrationBuilder.DropColumn( + name: "Result", + table: "Analyses"); + + migrationBuilder.Sql(@" + UPDATE ""AnalyzerTransactions"" + SET ""AnalysisResponse"" = + '{""objectives"":[{""objectiveName"":""Legacy Result"",""objectiveValue"":{""numberValue"":' + || COALESCE(json_extract(""AnalysisResponse"", '$.result'), json_extract(""AnalysisResponse"", '$.Result')) + || '}}],""analysisOutcome"":' + || COALESCE(json_extract(""AnalysisResponse"", '$.analysisOutcome'), json_extract(""AnalysisResponse"", '$.analysis_outcome'), json_extract(""AnalysisResponse"", '$.AnalysisOutcome'), 0) + || CASE + WHEN json_extract(""AnalysisResponse"", '$.errorString') IS NOT NULL THEN ',""errorString"":""' || json_extract(""AnalysisResponse"", '$.errorString') || '""' + WHEN json_extract(""AnalysisResponse"", '$.error_string') IS NOT NULL THEN ',""errorString"":""' || json_extract(""AnalysisResponse"", '$.error_string') || '""' + WHEN json_extract(""AnalysisResponse"", '$.ErrorString') IS NOT NULL THEN ',""errorString"":""' || json_extract(""AnalysisResponse"", '$.ErrorString') || '""' + ELSE '' + END + || '}' + WHERE COALESCE(json_extract(""AnalysisResponse"", '$.result'), json_extract(""AnalysisResponse"", '$.Result')) IS NOT NULL; + "); + + migrationBuilder.Sql(@" + UPDATE ""PlannerTransactions"" + SET ""PlanningRequest"" = json_set(""PlanningRequest"", '$.analysisResults', json('[]')) + WHERE json_array_length(""PlanningRequest"", '$.analysisResults') > 0 + AND json_extract(""PlanningRequest"", '$.analysisResults[0].objectives') IS NULL; + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@" + UPDATE ""AnalyzerTransactions"" + SET ""AnalysisResponse"" = + '{""result"":' || cast(json_extract(""AnalysisResponse"", '$.objectives[0].objectiveValue.numberValue') as REAL) + || ',""analysisOutcome"":' || COALESCE(json_extract(""AnalysisResponse"", '$.analysisOutcome'), 0) + || CASE + WHEN json_extract(""AnalysisResponse"", '$.errorString') IS NOT NULL + THEN ',""errorString"":""' || json_extract(""AnalysisResponse"", '$.errorString') || '""' + ELSE '' + END + || '}' + WHERE json_extract(""AnalysisResponse"", '$.objectives') IS NOT NULL; + "); + + migrationBuilder.AddColumn( + name: "Result", + table: "AnalysisOverview", + type: "REAL", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "Result", + table: "Analyses", + type: "REAL", + nullable: true); + + migrationBuilder.Sql(@" + UPDATE ""Analyses"" + SET ""Result"" = cast(json_extract(""Objectives"", '$[0].objectiveValue.numberValue') as REAL) + WHERE ""Objectives"" IS NOT NULL AND ""Objectives"" <> '[]'; + "); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "Analyses"); + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..e982a780 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.Designer.cs @@ -0,0 +1,268 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260624184739_AnalysisUpdates_AresIdentityContext")] + partial class AnalysisUpdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.cs b/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.cs new file mode 100644 index 00000000..5ec6da60 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260624184739_AnalysisUpdates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresIdentity +{ + /// + public partial class AnalysisUpdates_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs index 9c9b63cc..d8a7b6b1 100644 --- a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs @@ -39,9 +39,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); - b.Property("Result") - .HasColumnType("REAL"); - b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -50,7 +47,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); - modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") .ValueGeneratedOnAdd() @@ -72,8 +69,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); - b.Property("Result") - .HasColumnType("REAL"); + b.Property("Objectives") + .HasColumnType("TEXT"); b.HasKey("UniqueId"); diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index ca594511..176de723 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -404,11 +404,19 @@ public void OnPlannerTransactionReceived(PlannerTransaction transaction, int cur public void OnAnalyzerTransactionReceived(AnalyzerTransaction transaction, int currentTurn) { - AnalyzerMetrics.Add(new ChartMetricPoint + foreach(var objective in transaction.AnalysisResponse.Objectives) { - ExecutionIndex = currentTurn, - RawValue = transaction.AnalysisResponse.Result - }); + var found = objective.ObjectiveValue.TryGetNumericValue(out var numericValue); + + if(found) + { + AnalyzerMetrics.Add(new ChartMetricPoint + { + ExecutionIndex = currentTurn, + RawValue = numericValue + }); + } + } } public bool TryGetChartableValue(AresValue aresValue, out double result) From 76768d56313beaa07edd3350f4390ea928df030a Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 29 Jun 2026 11:04:48 -0400 Subject: [PATCH 02/19] Fixed the display charts for analysis to support displaying multiple objectives --- .../Execution/Executors/CampaignExecutor.cs | 16 ++++++------ UI/Features/Execution/Execution.razor | 25 +++++++++++++------ UI/Features/Execution/ExecutionViewModel.cs | 21 +++++++++------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 2ba6653f..1229832c 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -587,14 +587,6 @@ private async Task PlanExperiment(List analyses, // The following are top level checks for analysis failure in case the // failure is not properly handled on the Analysis itself // which also has support for "success" and "error" message - if (analysis is null || !analysis.Objectives.Any()) - { - Status.AnalysisState = AnalysisState.AnalysisError; - await _notifier.Notify("Analysis Failure", $"Analysis was reported as successful, but no actual analysis was provided. {analysis?.ErrorString ?? "No error string provided"}", NotificationSeverityEnum.Error); - _logger.LogError("Failed to analyze. The analysis result came back as {Result}", analysis?.Objectives); - return (false, false); - } - if(analysis.AnalysisOutcome == Outcome.Failure) { Status.AnalysisState = AnalysisState.AnalysisError; @@ -617,6 +609,14 @@ private async Task PlanExperiment(List analyses, _logger.LogWarning("Analysis completed successfully, but the analyzer emitted a warning! {Warning}", analysis.ErrorString); } + if(analysis is null || !analysis.Objectives.Any()) + { + Status.AnalysisState = AnalysisState.AnalysisError; + await _notifier.Notify("Analysis Failure", $"Analysis was reported as successful, but no actual analysis was provided. {analysis?.ErrorString ?? "No error string provided"}", NotificationSeverityEnum.Error); + _logger.LogError("Failed to analyze. The analysis result came back as {Result}", analysis?.Objectives); + return (false, false); + } + Status.AnalysisState = AnalysisState.AnalysisComplete; ReportCampaignStatus(); analyses.Add(analysis); diff --git a/UI/Features/Execution/Execution.razor b/UI/Features/Execution/Execution.razor index d70f46f5..3b2d0dd6 100644 --- a/UI/Features/Execution/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -439,15 +439,26 @@ { - - + @foreach(var analyzerSeries in ViewModel!.AnalyzerMetrics) + { + + + + + + +
+ @analyzerSeries.Key: + @data.RawValue +
+
+ } + + + +
} else diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 176de723..0edf7a1f 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -407,15 +407,18 @@ public void OnAnalyzerTransactionReceived(AnalyzerTransaction transaction, int c foreach(var objective in transaction.AnalysisResponse.Objectives) { var found = objective.ObjectiveValue.TryGetNumericValue(out var numericValue); - - if(found) + if(!found) + return; + + if(!AnalyzerMetrics.ContainsKey(objective.ObjectiveName)) + AnalyzerMetrics[objective.ObjectiveName] = new List(); + + AnalyzerMetrics[objective.ObjectiveName].Add(new ChartMetricPoint { - AnalyzerMetrics.Add(new ChartMetricPoint - { - ExecutionIndex = currentTurn, - RawValue = numericValue - }); - } + ExecutionIndex = currentTurn, + RawValue = numericValue, + PlotValue = numericValue + }); } } @@ -755,7 +758,7 @@ public int ActiveExperimentNumber [Reactive] public partial Dictionary> PlannerMetricsMap { get; private set; } [Reactive] - public partial List AnalyzerMetrics { get; private set; } + public partial Dictionary> AnalyzerMetrics { get; private set; } [Reactive] public partial IList ExperimentExecutionStatuses { get; private set; } [Reactive] From f5711416bba41d6faea617115694178ba1d84732 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 22 Jul 2026 08:57:06 -0400 Subject: [PATCH 03/19] Updated tests and fixed build errors associated with the new changes --- .../CampaignDatasetGeneratorTests.cs | 124 +++++++++++++----- Ares.Core.Tests/ManualPlannerTests.cs | 2 +- Ares.Core/Analyzing/AnalysisHelper.cs | 2 +- .../DataMappers/CampaignDatasetGenerator.cs | 92 +++++++++---- .../AnalysisOverviewEntityConfiguration.cs | 1 + Ares.Core/Planning/ManualPlanner.cs | 13 +- Ares.Core/Planning/PlanningHelper.cs | 60 +-------- UI/Features/Execution/ExecutionViewModel.cs | 14 +- 8 files changed, 171 insertions(+), 137 deletions(-) diff --git a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs index f0143c98..3974c9dd 100644 --- a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs +++ b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs @@ -43,30 +43,28 @@ public async Task GenerateAsync_SortsExperimentRowsAndCreatesFixedColumns() using(Assert.EnterMultipleScope()) { Assert.That(dataset.Name, Is.EqualTo("Experiments")); - Assert.That(dataset.Columns.Take(6).Select(column => column.Name), Is.EqualTo([ + Assert.That(dataset.Columns.Take(5).Select(column => column.Name), Is.EqualTo([ "Experiment Number", "Experiment Template", "Time Started", "Time Finished", - "Duration Seconds", - "Analysis Result" - ])); + "Duration Seconds" + ])); Assert.That(ColumnSchema(dataset, "Experiment Number").Type, Is.EqualTo(AresDataType.Int)); Assert.That(ColumnSchema(dataset, "Time Started").Type, Is.EqualTo(AresDataType.Timestamp)); Assert.That(ColumnSchema(dataset, "Time Finished").Type, Is.EqualTo(AresDataType.Timestamp)); Assert.That(ColumnSchema(dataset, "Duration Seconds").Type, Is.EqualTo(AresDataType.Number)); - Assert.That(ColumnSchema(dataset, "Analysis Result").Type, Is.EqualTo(AresDataType.Number)); - Assert.That(ColumnSchema(dataset, "Analysis Result").Optional, Is.True); + Assert.That(ColumnSchema(dataset, "Objective.Result").Optional, Is.True); Assert.That(dataset.Rows[0].Data.Fields["Experiment Number"].IntValue, Is.EqualTo(1)); Assert.That(dataset.Rows[0].Data.Fields["Time Started"].TimestampValue, Is.EqualTo(Timestamp.FromDateTime(firstStart))); Assert.That(dataset.Rows[0].Data.Fields["Time Finished"].TimestampValue, Is.EqualTo(Timestamp.FromDateTime(firstEnd))); Assert.That(dataset.Rows[0].Data.Fields["Duration Seconds"].NumberValue, Is.EqualTo(1)); - Assert.That(dataset.Rows[0].Data.Fields["Analysis Result"].NumberValue, Is.EqualTo(1.5)); + Assert.That(dataset.Rows[0].Data.Fields["Objective.Result"].NumberValue, Is.EqualTo(1.5)); Assert.That(dataset.Rows[1].Data.Fields["Experiment Number"].IntValue, Is.EqualTo(2)); Assert.That(dataset.Rows[1].Data.Fields["Time Started"].TimestampValue, Is.EqualTo(Timestamp.FromDateTime(secondStart))); Assert.That(dataset.Rows[1].Data.Fields["Time Finished"].TimestampValue, Is.EqualTo(Timestamp.FromDateTime(secondEnd))); Assert.That(dataset.Rows[1].Data.Fields["Duration Seconds"].NumberValue, Is.EqualTo(1)); - Assert.That(dataset.Rows[1].Data.Fields["Analysis Result"].NumberValue, Is.EqualTo(2.5)); + Assert.That(dataset.Rows[1].Data.Fields["Objective.Result"].NumberValue, Is.EqualTo(2.5)); } } @@ -532,47 +530,98 @@ public async Task GenerateAsync_CreatesPlannerTransactionRows() var summaryId = Guid.NewGuid().ToString(); var experiment = CreateExperiment(DateTime.UnixEpoch, DateTime.UnixEpoch.AddSeconds(5)); var nestedOutput = AresValueHelper.CreateStruct(); - nestedOutput.StructValue.Fields["Value"] = AresValueHelper.CreateNumber(12.5); + nestedOutput.StructValue.Fields["Value"] = AresValueHelper.CreateInt(100); + + var nestConcentration = AresValueHelper.CreateStruct(); + nestConcentration.StructValue.Fields["Value"] = AresValueHelper.CreateFloat(1.6); + + // This helper creates Plan 1 with the Temperature output var transaction = CreatePlannerTransaction( experiment.ExperimentId, DateTime.UnixEpoch.AddSeconds(1), DateTime.UnixEpoch.AddSeconds(3), - ("Temperature", nestedOutput)); - var objectiveOne = new Objective() { ObjectiveValue = AresValueHelper.CreateNumber(1.5), ObjectiveName = "ObjectiveOne" }; - var objectiveTwo = new Objective() { ObjectiveValue = AresValueHelper.CreateNumber(2.5), ObjectiveName = "ObjectiveTwo" }; + ("Temperature", nestedOutput), + ("Concentration", nestConcentration)); + + var objectiveOne = new Objective() + { + ObjectiveValue = AresValueHelper.CreateNumber(1.5), + ObjectiveName = "ObjectiveOne" + }; + + var objectiveTwo = new Objective() + { + ObjectiveValue = AresValueHelper.CreateNumber(2.5), + ObjectiveName = "ObjectiveTwo" + }; + var responseOne = new AnalysisData() { AnalysisObjectives = { objectiveOne } }; var responseTwo = new AnalysisData() { AnalysisObjectives = { objectiveTwo } }; + var planOne = new Plan() { ErrorString = "planner warning", PlanningOutcome = Outcome.Warning }; - var planTwo = new Plan() { ErrorString = "planner warning", PlanningOutcome = Outcome.Warning }; + planOne.PlannedParameters.Add(new PlannedParameter() { ParameterName = "Temperature", ParameterValue = AresValueHelper.CreateInt(200) }); + planOne.PlannedParameters.Add(new PlannedParameter() { ParameterName = "Concentration", ParameterValue = AresValueHelper.CreateFloat(2.6) }); + + var planTwo = new Plan() { ErrorString = "planner error", PlanningOutcome = Outcome.Failure }; + planTwo.PlannedParameters.Add(new PlannedParameter() { ParameterName = "Temperature", ParameterValue = AresValueHelper.CreateInt(300) }); + planTwo.PlannedParameters.Add(new PlannedParameter() { ParameterName = "Concentration", ParameterValue = AresValueHelper.CreateFloat(3.6) }); + transaction.PlanningRequest.AnalysisData.AddRange([responseOne, responseTwo]); transaction.PlanningResponse.Plans.AddRange([planOne, planTwo]); + var generator = CreateGenerator(CreateCampaignSummary(summaryId, "Campaign A", experiment), [transaction]); var dataset = GetDataset(await generator.GenerateAsync(summaryId), "Planner Transactions"); - var row = dataset.Rows.Single(); + + // We now expect 3 distinct rows + var rows = dataset.Rows.ToList(); using(Assert.EnterMultipleScope()) { + Assert.That(rows, Has.Count.EqualTo(3)); + Assert.That(dataset.Columns.Take(11).Select(column => column.Name), Is.EqualTo([ - "Experiment Number", - "Planner Name", - "Planner Type", - "Planner Version", - "Time Request Sent", - "Time Response Received", - "Duration Seconds", - "Outcome", - "Error", - "Analysis Results", - "Output.Temperature.Value" + "Experiment Number", + "Plan Number", + "Planner Name", + "Planner Type", + "Planner Version", + "Time Request Sent", + "Time Response Received", + "Duration Seconds", + "Outcome", + "Error", + "Output.Temperature.Value" ])); - Assert.That(row.Data.Fields["Experiment Number"].IntValue, Is.EqualTo(1)); - Assert.That(row.Data.Fields["Planner Name"].StringValue, Is.EqualTo("Planner A")); - Assert.That(row.Data.Fields["Duration Seconds"].NumberValue, Is.EqualTo(2)); - Assert.That(row.Data.Fields["Outcome"].StringValue, Is.EqualTo(Outcome.Warning.ToString())); - Assert.That(row.Data.Fields["Error"].StringValue, Is.EqualTo("planner warning")); - Assert.That(row.Data.Fields["Analysis Results"].ListValue.Values.Select(value => value.NumberValue), Is.EqualTo([1.5, 2.5])); - Assert.That(row.Data.Fields["Output.Temperature.Value"].NumberValue, Is.EqualTo(12.5)); + + // Transaction-level fields should be identical across all 3 rows + foreach(var row in rows) + { + Assert.That(row.Data.Fields["Experiment Number"].IntValue, Is.EqualTo(1)); + Assert.That(row.Data.Fields["Planner Name"].StringValue, Is.EqualTo("Planner A")); + Assert.That(row.Data.Fields["Duration Seconds"].NumberValue, Is.EqualTo(2)); + Assert.That(row.Data.Fields["Objective.ObjectiveOne"].NumberValue, Is.EqualTo(1.5)); + Assert.That(row.Data.Fields["Objective.ObjectiveTwo"].NumberValue, Is.EqualTo(2.5)); + } + + var rowOne = rows[0]; + Assert.That(rowOne.Data.Fields["Plan Number"].IntValue, Is.EqualTo(1)); + Assert.That(rowOne.Data.Fields["Output.Temperature.Value"].IntValue, Is.EqualTo(100)); + Assert.That(rowOne.Data.Fields["Output.Concentration.Value"].FloatValue, Is.EqualTo(1.6)); + + var rowTwo = rows[1]; + Assert.That(rowTwo.Data.Fields["Plan Number"].IntValue, Is.EqualTo(2)); + Assert.That(rowTwo.Data.Fields["Outcome"].StringValue, Is.EqualTo(Outcome.Warning.ToString())); + Assert.That(rowTwo.Data.Fields["Error"].StringValue, Is.EqualTo("planner warning")); + Assert.That(rowTwo.Data.Fields["Output.Temperature"].IntValue, Is.EqualTo(200)); + Assert.That(rowTwo.Data.Fields["Output.Concentration"].FloatValue, Is.EqualTo(2.6)); + + var rowThree = rows[2]; + Assert.That(rowThree.Data.Fields["Plan Number"].IntValue, Is.EqualTo(3)); + Assert.That(rowThree.Data.Fields["Outcome"].StringValue, Is.EqualTo(Outcome.Failure.ToString())); + Assert.That(rowThree.Data.Fields["Error"].StringValue, Is.EqualTo("planner error")); + Assert.That(rowThree.Data.Fields["Output.Temperature"].IntValue, Is.EqualTo(300)); + Assert.That(rowThree.Data.Fields["Output.Concentration"].FloatValue, Is.EqualTo(3.6)); } } @@ -617,7 +666,7 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() ])); Assert.That(row.Data.Fields["Analyzer Version"].StringValue, Is.EqualTo("2.0")); Assert.That(row.Data.Fields["Duration Seconds"].NumberValue, Is.EqualTo(2)); - Assert.That(row.Data.Fields["Result"].NumberValue, Is.EqualTo(9.5)); + Assert.That(row.Data.Fields["Objective.Result"].NumberValue, Is.EqualTo(9.5)); Assert.That(row.Data.Fields["Outcome"].StringValue, Is.EqualTo(Outcome.Success.ToString())); Assert.That(row.Data.Fields["Input.Measurement.Mass"].QuantityValue.Scalar, Is.EqualTo(4.5)); } @@ -760,7 +809,7 @@ private static ExperimentExecutionSummary CreateExperiment( if(analysisResult is not null) { overview.AnalysisOverview = new AnalysisOverview(); - overview.AnalysisOverview.Result.AddRange(analysisResult); + overview.AnalysisOverview.Objectives.AddRange(analysisResult); } @@ -917,14 +966,17 @@ private static PlannerTransaction CreatePlannerTransaction( ExperimentId = experimentId } }, - PlanningResponse = new PlanningResponse() + PlanningResponse = new PlanningResponse() { ObjectiveStatus = ObjectiveStatus.ObjectiveUnachieved } }; - transaction.PlanningResponse.PlannedParameters.AddRange(outputs.Select(output => new PlannedParameter + var plan = new Plan(); + plan.PlannedParameters.AddRange(outputs.Select(output => new PlannedParameter { ParameterName = output.Name, ParameterValue = output.Value })); + + transaction.PlanningResponse.Plans.Add(plan); return transaction; } diff --git a/Ares.Core.Tests/ManualPlannerTests.cs b/Ares.Core.Tests/ManualPlannerTests.cs index c6e43aaa..47a1cb86 100644 --- a/Ares.Core.Tests/ManualPlannerTests.cs +++ b/Ares.Core.Tests/ManualPlannerTests.cs @@ -58,6 +58,6 @@ public async Task CorrectNumberOfResults_FileLines() var request = new PlanningRequest(); request.PlanningParameters.AddRange(_parameterMetadatas.Select(m => new PlanningParameter { ParameterName = m.Name })); var planResponse = await _manualPlanner.Plan(request, CancellationToken.None); - Assert.That(planResponse.PlannedParameters, Has.Exactly(3).Items); + Assert.That(planResponse.Plans.First().PlannedParameters, Has.Exactly(3).Items); } } diff --git a/Ares.Core/Analyzing/AnalysisHelper.cs b/Ares.Core/Analyzing/AnalysisHelper.cs index e80891be..f6fa788f 100644 --- a/Ares.Core/Analyzing/AnalysisHelper.cs +++ b/Ares.Core/Analyzing/AnalysisHelper.cs @@ -75,7 +75,7 @@ public async Task Analyze(ExperimentTemplate template, Experim ExperimentOverviewId = experimentSummary.ExperimentOverview.UniqueId }; - experimentSummary.ExperimentOverview.AnalysisOverview.Result.AddRange(analysis.Objectives); + experimentSummary.ExperimentOverview.AnalysisOverview.Objectives.AddRange(analysis.Objectives); _logger.LogInformation("Analysis completed {Result}", analysis.Objectives); await LogAnalyzerTransaction(transaction); diff --git a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs index f36663a8..be6e11d1 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -1,12 +1,10 @@ using Ares.Datamodel; using Ares.Datamodel.Analyzing; -using Ares.Datamodel.Analyzing.Remote; using Ares.Datamodel.Extensions; using Ares.Datamodel.Planning; using Ares.Datamodel.Templates; using Google.Protobuf.WellKnownTypes; using Microsoft.EntityFrameworkCore; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Ares.Core.DataManagement.DataMappers; @@ -22,10 +20,10 @@ public class CampaignDatasetGenerator(IDbContextFactory _db private const string TimeStartedColumnName = "Time Started"; private const string TimeFinishedColumnName = "Time Finished"; private const string DurationSecondsColumnName = "Duration Seconds"; - private const string AnalysisResultColumnName = "Analysis Result"; private const string StatusColumnName = "Status"; private const string SuccessColumnName = "Success"; private const string ErrorColumnName = "Error"; + private const string PlanNumberColumnName = "Plan Number"; private const string PlannerNameColumnName = "Planner Name"; private const string PlannerTypeColumnName = "Planner Type"; private const string PlannerVersionColumnName = "Planner Version"; @@ -35,7 +33,7 @@ public class CampaignDatasetGenerator(IDbContextFactory _db private const string TimeRequestSentColumnName = "Time Request Sent"; private const string TimeResponseReceivedColumnName = "Time Response Received"; private const string OutcomeColumnName = "Outcome"; - private const string AnalysisResultsColumnName = "Analysis Results"; + private const string ObjectiveStatusColumnName = "Objective Status"; private const string ResultColumnName = "Result"; private const string InputColumnPrefix = "Input."; private const string OutputColumnPrefix = "Output."; @@ -137,7 +135,8 @@ private static AresDataset CreatePlannerTransactionsDataset(PlannerRecord[] reco }; dataset.Columns.AddRange(CreatePlannerTransactionColumns(records, cancellationToken)); - dataset.Rows.AddRange(records.Select(record => CreatePlannerTransactionRow(record, cancellationToken))); + dataset.Rows.AddRange(records.SelectMany(record => CreatePlannerTransactionRows(record, cancellationToken))); + return dataset; } @@ -161,8 +160,7 @@ private static IEnumerable CreateExperimentColumns(IEnumerable CreatePlannerTransactionColumns(IEnum return [ CreateColumn(ExperimentNumberColumnName, AresDataType.Int), + CreateColumn(PlanNumberColumnName, AresDataType.Int, optional: true), CreateColumn(PlannerNameColumnName, AresDataType.String, optional: true), CreateColumn(PlannerTypeColumnName, AresDataType.String, optional: true), CreateColumn(PlannerVersionColumnName, AresDataType.String, optional: true), .. CreateTransactionTimingColumns(), CreateColumn(OutcomeColumnName, AresDataType.String, optional: true), CreateColumn(ErrorColumnName, AresDataType.String, optional: true), - CreateColumn(AnalysisResultsColumnName, AresDataType.List, optional: true), .. CreatePlannerDynamicColumns(records, cancellationToken) ]; } @@ -241,6 +239,15 @@ private static IEnumerable CreateExperimentDynamicColumns(IEnume { cancellationToken.ThrowIfCancellationRequested(); + if(experiment.ExperimentOverview?.AnalysisOverview is not null) + { + foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) + { + cancellationToken.ThrowIfCancellationRequested(); + TryAddDynamicColumn(columns, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue); + } + } + var resultFields = experiment.ExperimentOverview?.Result?.Fields.OrderBy(field => field.Key) ?? Enumerable.Empty>(); foreach(var field in resultFields) @@ -324,11 +331,15 @@ private static IEnumerable CreatePlannerDynamicColumns(IEnumerab foreach(var record in records) { - foreach(var parameter in record.Transaction.PlanningResponse?.PlannedParameters ?? []) + var plans = record.Transaction.PlanningResponse?.Plans ?? Enumerable.Empty(); + foreach(var plan in plans) { - cancellationToken.ThrowIfCancellationRequested(); - if(parameter.ParameterValue is not null) - AddDynamicColumns(columns, $"{OutputColumnPrefix}{parameter.ParameterName}", parameter.ParameterValue); + foreach(var parameter in plan.PlannedParameters ?? []) + { + cancellationToken.ThrowIfCancellationRequested(); + if(parameter.ParameterValue is not null) + AddDynamicColumns(columns, $"{OutputColumnPrefix}{parameter.ParameterName}", parameter.ParameterValue); + } } } @@ -443,8 +454,8 @@ private static AresDataRow CreateExperimentRow(ExperimentExecutionSummary experi if(experiment.ExperimentOverview?.AnalysisOverview is not null) { - foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Result) - data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) + data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; } foreach(var field in experiment.ExperimentOverview?.Result?.Fields ?? []) @@ -510,12 +521,33 @@ private static AresDataRow CreateCommandRow(CommandRecord record, CancellationTo }; } - private static AresDataRow CreatePlannerTransactionRow(PlannerRecord record, CancellationToken cancellationToken) + private static IEnumerable CreatePlannerTransactionRows(PlannerRecord record, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + var transaction = record.Transaction; + var plans = transaction.PlanningResponse?.Plans; + + // If there are no plans, we still output the transaction metadata + if(plans is null || plans.Count == 0) + { + yield return BuildPlanRow(record, null, 0, cancellationToken); + yield break; + } + + // Yield one row per plan + int planIndex = 1; + foreach(var plan in plans) + { + yield return BuildPlanRow(record, plan, planIndex++, cancellationToken); + } + } + + private static AresDataRow BuildPlanRow(PlannerRecord record, dynamic? plan, int planIndex, CancellationToken cancellationToken) + { var transaction = record.Transaction; var data = new AresStruct(); + data.Fields[ExperimentNumberColumnName] = AresValueHelper.CreateInt(record.ExperimentNumber); AddString(data, PlannerNameColumnName, transaction.PlannerName); AddString(data, PlannerTypeColumnName, transaction.PlannerType); @@ -524,23 +556,31 @@ private static AresDataRow CreatePlannerTransactionRow(PlannerRecord record, Can if(transaction.PlanningResponse is not null) { - AddString(data, OutcomeColumnName, transaction.PlanningResponse.PlanningOutcome.ToString()); - AddString(data, ErrorColumnName, transaction.PlanningResponse.ErrorString); - - foreach(var parameter in transaction.PlanningResponse.PlannedParameters) - { - cancellationToken.ThrowIfCancellationRequested(); - if(parameter.ParameterValue is not null) - AddFlattenedValue(data, $"{OutputColumnPrefix}{parameter.ParameterName}", parameter.ParameterValue, cancellationToken); - } + AddString(data, ObjectiveStatusColumnName, transaction.PlanningResponse.ObjectiveStatus.ToString()); } + // Base AnalysisData if(transaction.PlanningRequest?.AnalysisData.Count > 0) { foreach(var analysisDataEntry in transaction.PlanningRequest.AnalysisData) { foreach(var objective in analysisDataEntry.AnalysisObjectives) - data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; + } + } + + // Plan-specific data + if(plan is not null) + { + data.Fields["Plan Number"] = AresValueHelper.CreateInt(planIndex); + AddString(data, OutcomeColumnName, plan.PlanningOutcome.ToString()); + AddString(data, ErrorColumnName, plan.ErrorString); + + foreach(var parameter in plan.PlannedParameters) + { + cancellationToken.ThrowIfCancellationRequested(); + if(parameter.ParameterValue is not null) + AddFlattenedValue(data, $"{OutputColumnPrefix}{parameter.ParameterName}", parameter.ParameterValue, cancellationToken); } } @@ -562,7 +602,7 @@ private static AresDataRow CreateAnalyzerTransactionRow(AnalyzerRecord record, C if(transaction.AnalysisResponse is not null) { foreach(var objective in transaction.AnalysisResponse.Objectives) - data.Fields[objective.ObjectiveName] = objective.ObjectiveValue; + data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalysisOverviewEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalysisOverviewEntityConfiguration.cs index d2445b28..48d5aa6f 100644 --- a/Ares.Core/EntityConfigurations/Analyzer/AnalysisOverviewEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalysisOverviewEntityConfiguration.cs @@ -10,5 +10,6 @@ public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); builder.Property(overview => overview.AnalyzerInfo).HasAnalyzerInfo(); + builder.Property(overview => overview.Objectives).HasObjectives(); } } diff --git a/Ares.Core/Planning/ManualPlanner.cs b/Ares.Core/Planning/ManualPlanner.cs index b3b117fe..921678e9 100644 --- a/Ares.Core/Planning/ManualPlanner.cs +++ b/Ares.Core/Planning/ManualPlanner.cs @@ -32,14 +32,19 @@ public Task Plan(PlanningRequest request, CancellationToken __ var currentParameterSet = _planResultsQueue.Dequeue().ToList(); var returnList = request.PlanningParameters.Select(param => currentParameterSet.First(result => result.Name == param.ParameterName).ToPlannedParameter(param)).ToList(); var response = new PlanningResponse(); - - response.PlannedParameters.AddRange(returnList); - response.PlanningOutcome = Outcome.Success; + + var plan = new Plan() { PlanningOutcome = Outcome.Success }; + plan.PlannedParameters.AddRange(returnList); + response.Plans.Add(plan); return Task.FromResult(response); } catch(InvalidOperationException e) { - return Task.FromResult(new PlanningResponse() { ErrorString = $"Exception Occured in Manual Planner: {e.Message}", PlanningOutcome = Outcome.Failure}); + var errorResponse = new PlanningResponse(); + var errorPlan = new Plan() { ErrorString = $"Exception Occured in Manual Planner: {e.Message}", PlanningOutcome = Outcome.Failure }; + errorResponse.Plans.Add(errorPlan); + + return Task.FromResult(errorResponse); } } diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 3c407081..fe058971 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -130,10 +130,9 @@ private async Task RequestNewPlans(IPlannerService planner, else { - var legacyResponseProcessed = await HandleLegacyPlanResponse(planResponse, parameterArray); - - if(!legacyResponseProcessed) - return false; + _logger.LogError("Received plan response, but no plans were returned."); + await _notifier.Notify("Planner Error!", "Plan response received, but no plans included in response!", NotificationSeverityEnum.Error); + return false; } } @@ -209,59 +208,6 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam return plannerToMetadataMaps; } - /// - /// Handles legacy plan responses - /// - /// - /// - /// A bool indicating whether or not resolving variables was a success - private async Task HandleLegacyPlanResponse(PlanningResponse planResponse, Parameter[] parameterArray) - { -#pragma warning disable CS0612 // Type or member is obsolete - - if(planResponse.PlanningOutcome == Outcome.Failure) - { - if(string.IsNullOrWhiteSpace(planResponse.ErrorString)) - await _notifier.Notify("Planner Error!", "Planner reported that planning failed, but did not provide any specific error as to why.", NotificationSeverityEnum.Error); - - else - await _notifier.Notify($"Planner Reported Error: {planResponse.ErrorString}", "Planner Error!", NotificationSeverityEnum.Error); - - return false; - } - - if(planResponse.PlanningOutcome == Outcome.Warning) - { - if(string.IsNullOrWhiteSpace(planResponse.ErrorString)) - await _notifier.Notify("Planner Warning", "Planner reported a warning, but did not provide specific context for that warning.", NotificationSeverityEnum.Warning); - - else - await _notifier.Notify("Planner Warning", $"Planner successfully planned, but reported a warning: {planResponse.ErrorString}", NotificationSeverityEnum.Warning); - } - - if(planResponse.PlanningOutcome == Outcome.Canceled) - await _notifier.Notify("Planning was canceled.", "Planning was canceled.", NotificationSeverityEnum.Info); - - if(!planResponse.PlannedParameters.Any()) - { - await _notifier.Notify("Planning Error!", "Tried to plan for experiment, but planning returned no plan results! Campaign will stop.", NotificationSeverityEnum.Error); - return false; - } - - foreach(var result in planResponse.PlannedParameters) - { - var parameterPlanTarget = parameterArray.FirstOrDefault(parameter => parameter.GetPlanningMetadata()?.Name == result.ParameterName); - - if(parameterPlanTarget is null) - continue; - - parameterPlanTarget.SetResolvedValue(result.ParameterValue); - } - - return true; -#pragma warning restore CS0612 // Type or member is obsolete - } - /// /// Takes in a plan and a parameter array and resolves those parameters using the provided plan /// diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 663b3de5..7ba94902 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -402,11 +402,7 @@ public async Task UpdatePlannerTransactions() public void OnPlannerTransactionReceived(PlannerTransaction transaction, int currentTurn) { - if(transaction.PlanningResponse.PlannedParameters.Any()) - foreach(var field in transaction.PlanningResponse.PlannedParameters) - ProcessTransactionParameterData(field, transaction, currentTurn); - - else if(transaction.PlanningResponse.Plans.Any()) + if(transaction.PlanningResponse.Plans.Any()) { foreach(var plan in transaction.PlanningResponse.Plans) { @@ -423,13 +419,7 @@ private int DeterminePlanCount(IEnumerable transactionList) var count = 0; foreach(var transaction in transactionList) - { - if(transaction.PlanningResponse.PlannedParameters.Any()) - count++; - - else - count += transaction.PlanningResponse.Plans.Count(); - } + count += transaction.PlanningResponse.Plans.Count(); return count; } From ce4466b518b7dd7886fec26ad359107bd012323e Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 22 Jul 2026 14:21:38 -0400 Subject: [PATCH 04/19] Working towards full functionality. restoring an old field for backwards compatability --- .../CampaignDatasetGeneratorTests.cs | 4 +- Ares.Core/Analyzing/AnalysisHelper.cs | 2 +- .../DataMappers/CampaignDatasetGenerator.cs | 52 +++++++++++++++---- .../AnalyzerTransactionEntityConfiguration.cs | 2 +- UI/Features/Execution/ExecutionViewModel.cs | 2 +- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs index 3974c9dd..fa72b5c2 100644 --- a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs +++ b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs @@ -638,7 +638,7 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() DateTime.UnixEpoch.AddSeconds(4), ("Measurement", nestedInput)); - transaction.AnalysisResponse = new AnalysisResponse + transaction.AnalyzerResponse = new AnalysisResponse { Objectives = { new Objective() { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(9.5) } }, AnalysisOutcome = Outcome.Success, @@ -1003,7 +1003,7 @@ private static AnalyzerTransaction CreateAnalyzerTransaction( ExperimentId = experimentId } }, - AnalysisResponse = new AnalysisResponse() + AnalyzerResponse = new AnalysisResponse() }; foreach(var input in inputs) diff --git a/Ares.Core/Analyzing/AnalysisHelper.cs b/Ares.Core/Analyzing/AnalysisHelper.cs index f6fa788f..e9341170 100644 --- a/Ares.Core/Analyzing/AnalysisHelper.cs +++ b/Ares.Core/Analyzing/AnalysisHelper.cs @@ -66,7 +66,7 @@ public async Task Analyze(ExperimentTemplate template, Experim var analysis = await analyzer.Analyze(analysisRequest, cancellationToken); transaction.TimeResponseReceived = DateTime.UtcNow.ToTimestamp(); - transaction.AnalysisResponse = analysis; + transaction.AnalyzerResponse = analysis; experimentSummary.ExperimentOverview.AnalysisOverview = new AnalysisOverview { diff --git a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs index be6e11d1..d4931515 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -241,11 +241,22 @@ private static IEnumerable CreateExperimentDynamicColumns(IEnume if(experiment.ExperimentOverview?.AnalysisOverview is not null) { - foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) + if(experiment.ExperimentOverview.AnalysisOverview.Objectives.Any()) { - cancellationToken.ThrowIfCancellationRequested(); - TryAddDynamicColumn(columns, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue); + foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) + { + cancellationToken.ThrowIfCancellationRequested(); + TryAddDynamicColumn(columns, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue); + } + } + + // Handle the presence of deprecated result values gracefully + else if(experiment.ExperimentOverview.AnalysisOverview.AnalyzerInfo.Name != "NONE") + { + var aresValueResult = AresValueHelper.CreateNumber(experiment.ExperimentOverview.AnalysisOverview.Result); + TryAddDynamicColumn(columns, $"Objective.Result", aresValueResult); } + } var resultFields = experiment.ExperimentOverview?.Result?.Fields.OrderBy(field => field.Key) @@ -454,8 +465,19 @@ private static AresDataRow CreateExperimentRow(ExperimentExecutionSummary experi if(experiment.ExperimentOverview?.AnalysisOverview is not null) { - foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) - data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; + if(experiment.ExperimentOverview.AnalysisOverview.Objectives.Any()) + { + foreach(var objective in experiment.ExperimentOverview.AnalysisOverview.Objectives) + AddFlattenedValue(data, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue, cancellationToken); + } + + //If we have no listed objectives AD + else if(experiment.ExperimentOverview.AnalysisOverview.AnalyzerInfo.Name != "NONE") + { + var aresValueResult = AresValueHelper.CreateNumber(experiment.ExperimentOverview.AnalysisOverview.Result); + AddFlattenedValue(data, $"Objective.Result", aresValueResult, cancellationToken); + } + } foreach(var field in experiment.ExperimentOverview?.Result?.Fields ?? []) @@ -599,13 +621,25 @@ private static AresDataRow CreateAnalyzerTransactionRow(AnalyzerRecord record, C AddString(data, AnalyzerVersionColumnName, transaction.AnalyzerVersion); AddTransactionTimingFields(data, transaction.TimeRequestSent, transaction.TimeResponseReceived); + //if(transaction.AnalysisResponse is not null) + //{ + // foreach(var objective in transaction.AnalysisResponse.Objectives) + // data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; + + // AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); + // AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); + //} + if(transaction.AnalysisResponse is not null) { - foreach(var objective in transaction.AnalysisResponse.Objectives) - data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; + var aresValResult = AresValueHelper.CreateNumber(transaction.AnalysisResponse.Result); + AddFlattenedValue(data, "Objective.Result", aresValResult, cancellationToken); + } - AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); - AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); + else if(transaction.AnalyzerResponse is not null) + { + foreach(var objective in transaction.AnalyzerResponse.Objectives) + AddFlattenedValue(data, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue, cancellationToken); } foreach(var input in transaction.AnalysisRequest?.Inputs?.Fields ?? []) diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs index a19a5988..b8cf9f44 100644 --- a/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs @@ -11,6 +11,6 @@ public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); builder.Property(transaction => transaction.AnalysisRequest).HasAnalysisRequest(); - builder.Property(transaction => transaction.AnalysisResponse).HasAnalysis(); + builder.Property(transaction => transaction.AnalyzerResponse).HasAnalysis(); } } diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 7ba94902..f12c6cbd 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -454,7 +454,7 @@ private void ProcessTransactionParameterData(PlannedParameter field, PlannerTran public void OnAnalyzerTransactionReceived(AnalyzerTransaction transaction, int currentTurn) { - foreach(var objective in transaction.AnalysisResponse.Objectives) + foreach(var objective in transaction.AnalyzerResponse.Objectives) { var found = objective.ObjectiveValue.TryGetNumericValue(out var numericValue); if(!found) From 4dd99bebc59cb18eeadc30ccf58eeeddc64cc268 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 23 Jul 2026 08:26:04 -0400 Subject: [PATCH 05/19] More work. Might change directions on this. --- .../DataMappers/CampaignDatasetGenerator.cs | 29 +++++++++++-------- .../Analyzer/AnalysisEntityConfiguration.cs | 8 ++--- .../AnalyzerTransactionEntityConfiguration.cs | 4 +-- .../Helpers/AnalysisInfoSerializeHelper.cs | 17 ++++++++++- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs index d4931515..79f1c4e1 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -34,7 +34,7 @@ public class CampaignDatasetGenerator(IDbContextFactory _db private const string TimeResponseReceivedColumnName = "Time Response Received"; private const string OutcomeColumnName = "Outcome"; private const string ObjectiveStatusColumnName = "Objective Status"; - private const string ResultColumnName = "Result"; + private const string ResultColumnName = "Objective.Result"; private const string InputColumnPrefix = "Input."; private const string OutputColumnPrefix = "Output."; private const string OutputColumnName = "Output"; @@ -565,7 +565,7 @@ private static IEnumerable CreatePlannerTransactionRows(PlannerReco } } - private static AresDataRow BuildPlanRow(PlannerRecord record, dynamic? plan, int planIndex, CancellationToken cancellationToken) + private static AresDataRow BuildPlanRow(PlannerRecord record, Plan? plan, int planIndex, CancellationToken cancellationToken) { var transaction = record.Transaction; var data = new AresStruct(); @@ -574,8 +574,16 @@ private static AresDataRow BuildPlanRow(PlannerRecord record, dynamic? plan, int AddString(data, PlannerNameColumnName, transaction.PlannerName); AddString(data, PlannerTypeColumnName, transaction.PlannerType); AddString(data, PlannerVersionColumnName, transaction.PlannerVersion); + + AddTransactionTimingFields(data, transaction.TimeRequestSent, transaction.TimeResponseReceived); + if(plan is not null) + { + AddString(data, OutcomeColumnName, plan.PlanningOutcome.ToString()); + AddString(data, ErrorColumnName, plan.ErrorString); + } + if(transaction.PlanningResponse is not null) { AddString(data, ObjectiveStatusColumnName, transaction.PlanningResponse.ObjectiveStatus.ToString()); @@ -621,25 +629,22 @@ private static AresDataRow CreateAnalyzerTransactionRow(AnalyzerRecord record, C AddString(data, AnalyzerVersionColumnName, transaction.AnalyzerVersion); AddTransactionTimingFields(data, transaction.TimeRequestSent, transaction.TimeResponseReceived); - //if(transaction.AnalysisResponse is not null) - //{ - // foreach(var objective in transaction.AnalysisResponse.Objectives) - // data.Fields[$"Objective.{objective.ObjectiveName}"] = objective.ObjectiveValue; - - // AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); - // AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); - //} - if(transaction.AnalysisResponse is not null) { var aresValResult = AresValueHelper.CreateNumber(transaction.AnalysisResponse.Result); AddFlattenedValue(data, "Objective.Result", aresValResult, cancellationToken); + + AddString(data, OutcomeColumnName, transaction.AnalysisResponse.AnalysisOutcome.ToString()); + AddString(data, ErrorColumnName, transaction.AnalysisResponse.ErrorString); } - else if(transaction.AnalyzerResponse is not null) + else if(transaction.AnalyzerResponse.Objectives.Any()) { foreach(var objective in transaction.AnalyzerResponse.Objectives) AddFlattenedValue(data, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue, cancellationToken); + + AddString(data, OutcomeColumnName, transaction.AnalyzerResponse.AnalysisOutcome.ToString()); + AddString(data, ErrorColumnName, transaction.AnalyzerResponse.ErrorString); } foreach(var input in transaction.AnalysisRequest?.Inputs?.Fields ?? []) diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs index 94058346..46a88e9b 100644 --- a/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalysisEntityConfiguration.cs @@ -1,16 +1,14 @@ -using Ares.Core.EntityConfigurations.Helpers; -using Ares.Datamodel.Analyzing; +using Ares.Datamodel.Analyzing; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Ares.Core.EntityConfigurations.Analyzer; -internal class AnalysisEntityConfiguration : AresEntityTypeBaseConfiguration +internal class AnalysisEntityConfiguration : AresEntityTypeBaseConfiguration { - public override void Configure(EntityTypeBuilder builder) + public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); - builder.Property(a => a.Objectives).HasObjectives(); builder.ToTable("Analyses"); } } diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs index b8cf9f44..4df9569d 100644 --- a/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs @@ -1,6 +1,5 @@ using Ares.Core.EntityConfigurations.Helpers; using Ares.Datamodel.Analyzing; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Ares.Core.EntityConfigurations.Analyzer; @@ -11,6 +10,7 @@ public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); builder.Property(transaction => transaction.AnalysisRequest).HasAnalysisRequest(); - builder.Property(transaction => transaction.AnalyzerResponse).HasAnalysis(); + builder.Property(transaction => transaction.AnalysisResponse).HasAnalysis(); //Known depricated field, kept for backwards compatability + builder.Property(transaction => transaction.AnalyzerResponse).HasAnalyzerResponse(); } } diff --git a/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs b/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs index 5d105a47..a0526886 100644 --- a/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs +++ b/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs @@ -29,7 +29,22 @@ public static PropertyBuilder HasAnalysisRequest(this PropertyB .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); } - public static PropertyBuilder HasAnalysis(this PropertyBuilder value) + /// + /// A deprecated method used to keep compatability with older versions of ARES for now. Likely to be removed in future major version release. + /// + /// + /// + public static PropertyBuilder HasAnalysis(this PropertyBuilder value) + { + var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); + + return value.HasConversion( + v => JsonSerializer.Serialize(v, settings), + v => JsonSerializer.Deserialize(v, settings) ?? new Analysis()) + .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); + } + + public static PropertyBuilder HasAnalyzerResponse(this PropertyBuilder value) { var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); From c2d04e245568d563353f8251e0a246c0d87f51f3 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 23 Jul 2026 08:26:57 -0400 Subject: [PATCH 06/19] Forgot an entity config --- .../AnalysisResponseEntityConfiguration.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Ares.Core/EntityConfigurations/Analyzer/AnalysisResponseEntityConfiguration.cs diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalysisResponseEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalysisResponseEntityConfiguration.cs new file mode 100644 index 00000000..d59ff3fa --- /dev/null +++ b/Ares.Core/EntityConfigurations/Analyzer/AnalysisResponseEntityConfiguration.cs @@ -0,0 +1,16 @@ +using Ares.Core.EntityConfigurations.Helpers; +using Ares.Datamodel.Analyzing; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Ares.Core.EntityConfigurations.Analyzer; + +internal class AnalysisResponseEntityConfiguration : AresEntityTypeBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.Property(a => a.Objectives).HasObjectives(); + builder.ToTable("AnalysisResponses"); + } +} From 316eb6d6090f8a66e7db4f482b045a794e963dd2 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 28 Jul 2026 16:58:55 -0400 Subject: [PATCH 07/19] Saving place --- Ares.Core/Planning/PlanningHelper.cs | 8 ++++++++ UI/Infrastructure/Devices/RemoteDeviceAdapter.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index fe058971..2b74601d 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -176,6 +176,14 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam if(currentAnalysis is null || currentExp is null) continue; + // Add values to the deprecated field for now to ensure backwards compatability + // TODO: REMOVE IN NEXT MAJOR VERSION OF ARES/PYARES + var defaultObjective = currentAnalysis.Objectives.FirstOrDefault().ObjectiveValue; + + //If the objective doesn't have a number value, then the analyzer was build for running the newest system + if(defaultObjective.HasNumberValue) + planRequest.AnalysisResults.Add(defaultObjective.NumberValue); + var analysisData = CreateAnalysisData(currentAnalysis, currentExp); planRequest.AnalysisData.Add(analysisData); } diff --git a/UI/Infrastructure/Devices/RemoteDeviceAdapter.cs b/UI/Infrastructure/Devices/RemoteDeviceAdapter.cs index a8c53856..353edcbf 100644 --- a/UI/Infrastructure/Devices/RemoteDeviceAdapter.cs +++ b/UI/Infrastructure/Devices/RemoteDeviceAdapter.cs @@ -95,7 +95,7 @@ public async Task StartStateStream() return Task.CompletedTask; }); - _logger.LogInformation("Started device state stream for device {}.", Name); + _logger.LogInformation("Started device state stream for device {name}.", Name); UpdateStatusIfChanged(ConnectionStatus.ConnectedToService); await _devicesClient.GetDeviceStateStream(new DeviceStateStreamRequest { DeviceId = Id, PollingSettings = _pollingSettings }, streamWriter, null); From 8c54fa8902cbf7fb70128f59048c787f5173a70c Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 17 Aug 2026 16:45:02 -0400 Subject: [PATCH 08/19] Built a basic UI addition to handle selecting what objectives are passed to the planner during campaigns. Still need to round out the logic for populating objective info for planners, but currently am successfully seeing analyzers report exactly what objectives they return and then successfully marking what objectives to use for planning and saving that off to experiment templates. --- Ares.Core.Tests/AnalyzerManagerTests.cs | 5 ++ .../Data/Analyzer/TestReplyAnalyzer.cs | 10 +++ Ares.Core/Analyzing/AnalyzerBase.cs | 2 + Ares.Core/Analyzing/IAnalyzer.cs | 7 ++ Ares.Core/Analyzing/NoneAnalyzer.cs | 5 ++ Ares.Core/Analyzing/RemoteAnalyzer.cs | 6 ++ Ares.Core/Device/Managers/DeviceManager.cs | 15 ++-- .../ExperimentTemplateEntityConfiguration.cs | 2 + .../Resources/IResourceConnectionArbiter.cs | 2 +- .../Resources/ResourceConnectionArbiter.cs | 60 +++++++++---- .../Factories/PlanningDesignerFactory.cs | 10 ++- .../ViewModels/PlanningViewModel.cs | 28 ++++++- .../CampaignEdit/Views/CampaignDesigner.razor | 8 +- UI/Features/CampaignEdit/Views/Planning.razor | 84 +++++++++++++++++-- .../CampaignEdit/Views/Planning.razor.css | 28 +++++++ 15 files changed, 228 insertions(+), 44 deletions(-) create mode 100644 UI/Features/CampaignEdit/Views/Planning.razor.css diff --git a/Ares.Core.Tests/AnalyzerManagerTests.cs b/Ares.Core.Tests/AnalyzerManagerTests.cs index cdead353..1299d799 100644 --- a/Ares.Core.Tests/AnalyzerManagerTests.cs +++ b/Ares.Core.Tests/AnalyzerManagerTests.cs @@ -44,5 +44,10 @@ public override Task GetParameters(CancellationToken cancellat { throw new NotImplementedException(); } + + public override Task GetObjectiveOutputs(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } } diff --git a/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs b/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs index 10a6dfa1..a472b710 100644 --- a/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs +++ b/Ares.Core.Tests/Data/Analyzer/TestReplyAnalyzer.cs @@ -41,4 +41,14 @@ public override Task GetParameters(CancellationToken cancellat return Task.FromResult(schema); } + + public override Task GetObjectiveOutputs(CancellationToken cancellationToken) + { + var schema = new AresStructSchema(); + var testReplySchema = new AresValueSchema() { Optional = false, Type = AresDataType.Number }; + + schema.Fields["TestObjective"] = testReplySchema; + + return Task.FromResult(schema); + } } diff --git a/Ares.Core/Analyzing/AnalyzerBase.cs b/Ares.Core/Analyzing/AnalyzerBase.cs index 17af997f..23347c8c 100644 --- a/Ares.Core/Analyzing/AnalyzerBase.cs +++ b/Ares.Core/Analyzing/AnalyzerBase.cs @@ -75,6 +75,8 @@ public void UpdateSettings(AresStruct settings) public abstract Task GetParameters(CancellationToken cancellationToken); + public abstract Task GetObjectiveOutputs(CancellationToken cancellationToken); + private static ParameterValidationResult ValidateParameterTypes(KeyValuePair analyzerField, AresStructSchema parameters) { var result = new ParameterValidationResult(); diff --git a/Ares.Core/Analyzing/IAnalyzer.cs b/Ares.Core/Analyzing/IAnalyzer.cs index 1d555418..070780a1 100644 --- a/Ares.Core/Analyzing/IAnalyzer.cs +++ b/Ares.Core/Analyzing/IAnalyzer.cs @@ -93,6 +93,13 @@ public interface IAnalyzer /// Task GetCapabilities(CancellationToken cancellationToken = default); + /// + /// This will return the schema describing the analyzers objective outputs. + /// + /// + /// + Task GetObjectiveOutputs(CancellationToken cancellationToken = default); + /// /// Does the actual analysis work. /// diff --git a/Ares.Core/Analyzing/NoneAnalyzer.cs b/Ares.Core/Analyzing/NoneAnalyzer.cs index 878efa87..529cfef9 100644 --- a/Ares.Core/Analyzing/NoneAnalyzer.cs +++ b/Ares.Core/Analyzing/NoneAnalyzer.cs @@ -45,4 +45,9 @@ public override Task GetParameters(CancellationToken cancellat { return Task.FromResult(new AresStructSchema()); } + + public override Task GetObjectiveOutputs(CancellationToken cancellationToken) + { + return Task.FromResult(new AresStructSchema()); + } } diff --git a/Ares.Core/Analyzing/RemoteAnalyzer.cs b/Ares.Core/Analyzing/RemoteAnalyzer.cs index 7b207293..0764acb2 100644 --- a/Ares.Core/Analyzing/RemoteAnalyzer.cs +++ b/Ares.Core/Analyzing/RemoteAnalyzer.cs @@ -57,6 +57,12 @@ public override Task Analyze(AnalysisRequest request, AresStru return client.AnalyzeAsync(request, cancellationToken: cancellationToken).ResponseAsync; } + public override async Task GetObjectiveOutputs(CancellationToken cancellationToken) + { + var capabilities = await GetCapabilities(); + return capabilities.ObjectiveOutputSchema; + } + public override async Task GetCapabilities(CancellationToken cancellationToken = default) { var client = GetClient(); diff --git a/Ares.Core/Device/Managers/DeviceManager.cs b/Ares.Core/Device/Managers/DeviceManager.cs index 75b5de05..17115540 100644 --- a/Ares.Core/Device/Managers/DeviceManager.cs +++ b/Ares.Core/Device/Managers/DeviceManager.cs @@ -26,8 +26,7 @@ public class DeviceManager : IDeviceManager private readonly IResourceConnectionArbiter _resourceConnectionArbiter; private readonly CompositeDisposable _cleanup = new(); - public DeviceManager( - IDeviceDriverProvider driverProvider, + public DeviceManager(IDeviceDriverProvider driverProvider, IAresDeviceRepo deviceRepository, INotificationHandler notificationHandler, IDeviceConfigProvider configProvider, @@ -113,14 +112,10 @@ private async Task HandleChangeAsync(Change change) if(!success) { - var owner = _resourceConnectionArbiter.GetResourceOwner(serialConnectionResource); - if(owner?.UniqueId == device.UniqueId) - { - var message = $"Failed to add device {device.Name} as the resource it tried to use ({serialConnectionResource.ResourceName}) was already in use by another device."; - _logger.LogError(message); - await _notificationHandler.HandleNotification("Failed to Add Device", message, NotificationSeverityEnum.Error); - return null; - } + var message = $"Failed to add device {device.Name} as the resource it tried to use ({serialConnectionResource.ResourceName}) was already in use by another device."; + _logger.LogError(message); + await _notificationHandler.HandleNotification("Failed to Add Device", message, NotificationSeverityEnum.Error); + return null; } } diff --git a/Ares.Core/EntityConfigurations/Execution/Experiments/ExperimentTemplateEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Execution/Experiments/ExperimentTemplateEntityConfiguration.cs index 02ae6ed2..bc086379 100644 --- a/Ares.Core/EntityConfigurations/Execution/Experiments/ExperimentTemplateEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Execution/Experiments/ExperimentTemplateEntityConfiguration.cs @@ -20,5 +20,7 @@ public override void Configure(EntityTypeBuilder builder) builder.Navigation(experimentTemplate => experimentTemplate.StepTemplates) .AutoInclude(); + + builder.Property(template => template.PlanObjectives).HasSerializedRepeatedField(); } } diff --git a/Ares.Core/Resources/IResourceConnectionArbiter.cs b/Ares.Core/Resources/IResourceConnectionArbiter.cs index b132f569..5c7fc9c9 100644 --- a/Ares.Core/Resources/IResourceConnectionArbiter.cs +++ b/Ares.Core/Resources/IResourceConnectionArbiter.cs @@ -34,5 +34,5 @@ public interface IResourceConnectionArbiter ///
/// The resource to get the owner of. /// The ResourceOwner, or null if the resource is not in use. - IAresDevice? GetResourceOwner(ConnectionResource resource); + List GetResourceOwners(ConnectionResource resource); } \ No newline at end of file diff --git a/Ares.Core/Resources/ResourceConnectionArbiter.cs b/Ares.Core/Resources/ResourceConnectionArbiter.cs index 24dccaec..8f2d171e 100644 --- a/Ares.Core/Resources/ResourceConnectionArbiter.cs +++ b/Ares.Core/Resources/ResourceConnectionArbiter.cs @@ -8,38 +8,68 @@ namespace Ares.Core.Resources; ///
public class ResourceConnectionArbiter : IResourceConnectionArbiter { - private readonly ConcurrentDictionary _resourceLocks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _resourceLocks = new ConcurrentDictionary>(); /// - public bool TryAcquireResource(ConnectionResource resource, IAresDevice owner) + public bool TryAcquireResource(ConnectionResource resource, IAresDevice requester) { - if(string.IsNullOrEmpty(owner?.UniqueId) || resource == null) - return false; + if(string.IsNullOrEmpty(requester?.UniqueId) || resource == null) + return false; - return _resourceLocks.TryAdd(resource, owner); + if(!IsResourceInUse(resource)) + return _resourceLocks.TryAdd(resource, [requester]); + + else + { + var resourceOwners = GetResourceOwners(resource); + + if(resourceOwners is null) + return _resourceLocks.TryAdd(resource, [requester]); + + var requestingDeviceType = requester.GetType(); + var resourceOwnerType = resourceOwners?.First().GetType(); + + // If the device is of the exact same type, allow the resource allocation to pass. + if(requestingDeviceType.Name == resourceOwnerType?.Name) + return true; + + return false; + } } /// public void ReleaseResource(ConnectionResource resource, IAresDevice owner) { - if(resource == null || owner == null) - return; - - _resourceLocks.TryRemove(new KeyValuePair(resource, owner)); + if(resource == null || owner == null) + return; + + var ownerList = GetResourceOwners(resource); + + if(ownerList.Count == 0 || ownerList.Count == 1) + _resourceLocks.TryRemove(new KeyValuePair>(resource, ownerList)); + + + var updatedOwnerList = ownerList.ToList(); + updatedOwnerList.Remove(owner); + _resourceLocks.TryUpdate(resource, updatedOwnerList, ownerList); } /// public bool IsResourceInUse(ConnectionResource resource) { - if(resource == null) return false; - return _resourceLocks.ContainsKey(resource); + if(resource == null) + return false; + + return _resourceLocks.ContainsKey(resource); } /// - public IAresDevice? GetResourceOwner(ConnectionResource resource) + public List GetResourceOwners(ConnectionResource resource) { - if(resource == null) return null; - _resourceLocks.TryGetValue(resource, out var owner); - return owner; + if(resource == null) + return []; + + _resourceLocks.TryGetValue(resource, out var owner); + return owner ?? []; } } diff --git a/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs index c886bdaa..a4f0f505 100644 --- a/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs @@ -1,9 +1,9 @@ using Ares.Datamodel.Templates; -using Ares.Services; using Ares.Core.Grpc.Services; using Google.Protobuf.WellKnownTypes; using UI.Features.CampaignEdit.ViewModels; using UI.Application.Notifications; +using Ares.Core.Analyzing; namespace UI.Features.CampaignEdit.Factories; @@ -12,17 +12,21 @@ public class PlanningDesignerFactory { private readonly PlannerService _client; private readonly IUiNotificationService _notificationService; + private readonly IAnalyzerRepo _analyzerRepo; - public PlanningDesignerFactory(PlannerService client, IUiNotificationService notificationService) + public PlanningDesignerFactory(PlannerService client, IUiNotificationService notificationService, IAnalyzerRepo analyzerRepo) { _client = client; _notificationService = notificationService; + _analyzerRepo = analyzerRepo; } public async Task Create(CampaignTemplate template) { + var matchingAnalyzer = _analyzerRepo.GetAnalyzerById(template.ExperimentTemplate.AnalyzerId); + var planners = await _client.GetAllPlanners(new Empty(), null); - return new PlanningViewModel(template, planners.Planners, _client, _notificationService); + return new PlanningViewModel(template, planners.Planners, _client, _notificationService, matchingAnalyzer); } } diff --git a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index 60c23c75..67e89f5b 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -1,10 +1,11 @@ using ReactiveUI; using System.Collections.ObjectModel; using Ares.Datamodel.Templates; -using Ares.Services; using Ares.Datamodel.Planning; using Ares.Core.Grpc.Services; using UI.Application.Notifications; +using Ares.Core.Analyzing; +using Ares.Datamodel; namespace UI.Features.CampaignEdit.ViewModels; @@ -13,22 +14,39 @@ public class PlanningViewModel : ReactiveObject private readonly CampaignTemplate _template; private readonly PlannerService _client; private readonly IUiNotificationService _notificationService; + private readonly IAnalyzer? _selectedAnalyzer; public PlanningViewModel(CampaignTemplate template, IEnumerable plannerAdapters, PlannerService client, - IUiNotificationService notificationService) + IUiNotificationService notificationService, + IAnalyzer? selectedAnalyzer) { _template = template; _client = client; _notificationService = notificationService; + _selectedAnalyzer = selectedAnalyzer; PlannerAdapters = new ReadOnlyCollection(plannerAdapters.ToList()); PlannerAllocationEditors = template.PlannableParameters.Select(metadata => new PlannerAllocationEditorViewModel(metadata, template.PlannerAllocations.FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, client, notificationService)).ToArray(); } - public IEnumerable PlannerAllocationEditors { get; private set; } + public async Task UpdateAnalyzerObjectives() + { + if(_selectedAnalyzer is not null) + AvailableObjectives = await _selectedAnalyzer.GetObjectiveOutputs(); + + SelectedObjectives = AvailableObjectives?.Fields.Where(obj => _template.ExperimentTemplate.PlanObjectives.Contains(obj.Key)).ToList() ?? []; + } + + public IEnumerable PlannerAllocationEditors { get; private set; } = []; - public IEnumerable PlannerAdapters { get; } + public IEnumerable PlannerAdapters { get; } = []; + + public AresStructSchema? AvailableObjectives { get; private set; } + + public List> SelectedObjectives { get; private set; } = []; + + public List IncludedObjectives { get; private set; } public void Save() { @@ -40,6 +58,8 @@ public void Save() .Where(allocation => allocation is not null) .Where(allocation => _template.PlannableParameters.Any(meta => meta.UniqueId == allocation!.Parameter.UniqueId))); + _template.ExperimentTemplate.PlanObjectives.AddRange(SelectedObjectives.Select(obj => obj.Key)); + PlannerAllocationEditors = _template.PlannableParameters .Select(metadata => new PlannerAllocationEditorViewModel(metadata, _template.PlannerAllocations .FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, _client, _notificationService)) diff --git a/UI/Features/CampaignEdit/Views/CampaignDesigner.razor b/UI/Features/CampaignEdit/Views/CampaignDesigner.razor index e2fe9eb5..51644550 100644 --- a/UI/Features/CampaignEdit/Views/CampaignDesigner.razor +++ b/UI/Features/CampaignEdit/Views/CampaignDesigner.razor @@ -37,19 +37,19 @@ } @if (SelectedSubOption2) { - + } @if (SelectedSubOption3) { - + } @if (SelectedSubOption4) { - + } @if (SelectedSubOption5) { - + } @if (SelectedSubOption6) { diff --git a/UI/Features/CampaignEdit/Views/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor index 46213ad3..2074379e 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -1,16 +1,86 @@ -@using UI.Features.CampaignEdit.ViewModels +@using Ares.Datamodel +@using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase
-

Planning

- @foreach (var editor in ViewModel!.PlannerAllocationEditors) - { - - } +
+
+

Planner Assignment

+ @if(ViewModel?.PlannerAllocationEditors is null) + { +

Loading allocations...

+ } + else + { + @foreach(var editor in ViewModel.PlannerAllocationEditors) + { + + } + } +
+ +
+

Analyzer Objectives

+ @if(ViewModel!.AvailableObjectives is not null) + { + @foreach(var item in ViewModel.AvailableObjectives.Fields) + { + var isSelected = ViewModel!.SelectedObjectives.Contains(item); + +
+ + + + Objective Name: @item.Key + + + + Objective Description: @item.Value.Description + + + + + + + + +
+ } + } + + else + { +

Select an Analyzer to See Objectives

+ } +
+
+ +
@code { - + protected override async Task OnInitializedAsync() + { + if(ViewModel is not null) + await ViewModel!.UpdateAnalyzerObjectives(); + + await base.OnInitializedAsync(); + } + + private void ToggleSelection(KeyValuePair item) + { + if(ViewModel!.SelectedObjectives.Contains(item)) + ViewModel!.SelectedObjectives.Remove(item); + + else + ViewModel!.SelectedObjectives.Add(item); + + StateHasChanged(); + } } \ No newline at end of file diff --git a/UI/Features/CampaignEdit/Views/Planning.razor.css b/UI/Features/CampaignEdit/Views/Planning.razor.css new file mode 100644 index 00000000..a8ee85bb --- /dev/null +++ b/UI/Features/CampaignEdit/Views/Planning.razor.css @@ -0,0 +1,28 @@ +.two-column-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; + width: 100%; + min-height: calc(100vh - 4rem); + padding: 0.5rem; + box-sizing: border-box; +} + +.column { + border-radius: 1px; + overflow-y: auto; +} + +.rz-switch .rz-switch-circle { + background: green !important; +} + +.rz-switch.rz-switch-checked .rz-switch-circle { + background: red !important; +} + +@media (max-width: 768px) { + .two-column-container { + grid-template-columns: 1fr; + } +} From ebc8ca92343af689d2ba2a2b3bce6ce510fc4e6b Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 17 Aug 2026 16:45:44 -0400 Subject: [PATCH 09/19] Setting default empty list value to handle warning --- UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index 67e89f5b..b5fe890f 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -46,7 +46,7 @@ public async Task UpdateAnalyzerObjectives() public List> SelectedObjectives { get; private set; } = []; - public List IncludedObjectives { get; private set; } + public List IncludedObjectives { get; private set; } = []; public void Save() { From d057a094c6693f4361bf0f27e7800c981799cb22 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 18 Aug 2026 10:29:57 -0400 Subject: [PATCH 10/19] Updates to support new objectives in planning. Successfully saw a full closed loop run, where the analyzer provided a schema defining it's expected objective outputs as well as the objective outputs themselves, and the planner was able to successfully receive a list of objective outputs. --- .../Execution/CampaignExecutorTests.cs | 4 +-- .../Execution/Executors/CampaignExecutor.cs | 4 +-- Ares.Core/Planning/IPlanningHelper.cs | 2 +- Ares.Core/Planning/PlanningHelper.cs | 25 +++++++++++-------- UI/Features/CampaignEdit/Views/Planning.razor | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs index 45f1f4cb..dcc8547f 100644 --- a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs +++ b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs @@ -56,7 +56,7 @@ public void SetUp() .Setup(helper => helper.TryResolveParameters( It.IsAny>(), It.IsAny(), - It.IsAny>(), + It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny(), @@ -220,7 +220,7 @@ public async Task Replan_Composes_And_Executes_Experiment_Again() _planningHelper.Verify(helper => helper.TryResolveParameters( It.IsAny>(), It.IsAny(), - It.IsAny>(), + It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny(), diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 71e477db..ac98acbf 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -551,7 +551,7 @@ private async Task PlanExperiment(List analyses, var resolveSuccess = await _planningHelper.TryResolveParameters(Template.PlannerAllocations, metadata, - currentExperimentTemplate.GetAllPlannedParameters(), + currentExperimentTemplate, analyses, experimentSummaries.Select(es => es.ExperimentOverview), BatchPlanningSize, @@ -772,7 +772,7 @@ private async Task GenerateExperimentExecutor(Experime var resolveSuccess = await _planningHelper.TryResolveParameters(Template.PlannerAllocations, metadata, - experimentTemplate.GetAllPlannedParameters(), + experimentTemplate, analyses, previousExperiments, BatchPlanningSize, diff --git a/Ares.Core/Planning/IPlanningHelper.cs b/Ares.Core/Planning/IPlanningHelper.cs index c9924cb4..b0f8ac6e 100644 --- a/Ares.Core/Planning/IPlanningHelper.cs +++ b/Ares.Core/Planning/IPlanningHelper.cs @@ -20,7 +20,7 @@ public interface IPlanningHelper /// True if planning succeeded, false otherwise Task TryResolveParameters(IEnumerable plannerAllocations, RequestMetadata metadata, - IEnumerable parameters, + ExperimentTemplate currentTemplate, IEnumerable seedAnalyses, IEnumerable seedExperiments, int batchSize, diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 2b74601d..47a843d5 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -35,16 +35,19 @@ public PlanningHelper(IPlannerServiceRepo plannerManager, public async Task TryResolveParameters(IEnumerable plannerAllocations, RequestMetadata metadata, - IEnumerable parameters, + ExperimentTemplate currentTemplate, IEnumerable seedAnalyses, IEnumerable seedExperiments, int batchSize, List codes, CancellationToken cancellationToken) { + var parameters = currentTemplate.GetAllPlannedParameters(); var parameterArray = parameters.ToArray(); var plannerToMetadataMaps = MapParameterMetadataToPlanners(plannerAllocations); + var includedObjectives = currentTemplate.PlanObjectives.ToList(); + if(plannerToMetadataMaps is null) return false; @@ -63,7 +66,7 @@ public async Task TryResolveParameters(IEnumerable plan return true; } - return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, codes, metadata, planQueue, parameterArray, batchSize, cancellationToken); + return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, codes, metadata, planQueue, parameterArray, batchSize, includedObjectives, cancellationToken); }); var results = await Task.WhenAll(planningTasks); @@ -92,6 +95,7 @@ private async Task RequestNewPlans(IPlannerService planner, ConcurrentQueue planQueue, Parameter[] parameterArray, int batchSize, + List includedObjectives, CancellationToken cancellationToken) { var planTransaction = new PlannerTransaction() @@ -109,7 +113,7 @@ private async Task RequestNewPlans(IPlannerService planner, planTransaction.TimeRequestSent = DateTime.UtcNow.ToTimestamp(); //Create the plan request. Store it in the transaction. - var planRequest = CreatePlanningRequest(plannableParameters, seedExperiments, seedAnalysesArr, statusCodes, batchSize, metadata); + var planRequest = CreatePlanningRequest(plannableParameters, seedExperiments, seedAnalysesArr, statusCodes, batchSize, includedObjectives, metadata); planTransaction.PlanningRequest = planRequest; var planResponse = await planner.Plan(planRequest, cancellationToken); @@ -161,6 +165,7 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam AnalysisResponse[] seedAnalysesArr, List statusCodes, int batchSize, + List includedObjectives, RequestMetadata metadata) { //Create the plan request. Store it in the transaction. @@ -171,20 +176,20 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam for(int i = 0; i < seedAnalysesArr.Length; i++) { var currentAnalysis = seedAnalysesArr.ElementAtOrDefault(i); - var currentExp = relevantExperiments.ElementAtOrDefault(i + 1); + var currentExp = relevantExperiments.ElementAtOrDefault(i); if(currentAnalysis is null || currentExp is null) continue; // Add values to the deprecated field for now to ensure backwards compatability // TODO: REMOVE IN NEXT MAJOR VERSION OF ARES/PYARES - var defaultObjective = currentAnalysis.Objectives.FirstOrDefault().ObjectiveValue; + var defaultObjective = currentAnalysis.Objectives.FirstOrDefault()?.ObjectiveValue; - //If the objective doesn't have a number value, then the analyzer was build for running the newest system - if(defaultObjective.HasNumberValue) + //If the objective doesn't have a number value, then the analyzer was built for running the newest system + if(defaultObjective is not null && defaultObjective.HasNumberValue) planRequest.AnalysisResults.Add(defaultObjective.NumberValue); - var analysisData = CreateAnalysisData(currentAnalysis, currentExp); + var analysisData = CreateAnalysisData(currentAnalysis, currentExp, includedObjectives); planRequest.AnalysisData.Add(analysisData); } planRequest.PreviousPlanStatusCodes.AddRange(statusCodes); @@ -281,13 +286,13 @@ private static PlanningParameter ConvertToPlanningParameter(ParameterMetadata me return parameter; } - private AnalysisData CreateAnalysisData(AnalysisResponse analysis, ExperimentOverview experiment) + private AnalysisData CreateAnalysisData(AnalysisResponse analysis, ExperimentOverview experiment, List includedObjectives) { var newData = new AnalysisData(); foreach(var objective in analysis.Objectives) { - if(experiment.Template.PlanObjectives.Any(obj => obj == objective.ObjectiveName)) + if(experiment.Template.PlanObjectives.Any(obj => obj == objective.ObjectiveName) && includedObjectives.Contains(objective.ObjectiveName)) newData.AnalysisObjectives.Add(objective); } diff --git a/UI/Features/CampaignEdit/Views/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor index 2074379e..f009ce40 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -22,7 +22,7 @@

Analyzer Objectives

- @if(ViewModel!.AvailableObjectives is not null) + @if(ViewModel?.AvailableObjectives is not null) { @foreach(var item in ViewModel.AvailableObjectives.Fields) { From 53e49406511534ff453b5a162abce44aa8d812ac Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 18 Aug 2026 10:39:04 -0400 Subject: [PATCH 11/19] Fixed test --- .../DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs index fa72b5c2..2a0ae0de 100644 --- a/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs +++ b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs @@ -659,7 +659,7 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() "Time Request Sent", "Time Response Received", "Duration Seconds", - "Result", + "Objective.Result", "Outcome", "Error", "Input.Measurement.Mass" From d5a101fe666a4c0c6c3a576185859f8be0226847 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 18 Aug 2026 16:41:58 -0400 Subject: [PATCH 12/19] Fixed a problem in the plan request construction. Now when no objective outputs are selected (either because the analyzer doesn't support advertising it's objective outputs or the user simply didn't select any) all objectives are passed to the planner for use. --- Ares.Core/Planning/PlanningHelper.cs | 24 +++++++++++-------- .../ViewModels/AnalyzerDesignerViewModel.cs | 3 +++ .../ViewModels/PlanningViewModel.cs | 3 +++ UI/Features/CampaignEdit/Views/Planning.razor | 11 ++++++++- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 47a843d5..243c23b0 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -46,8 +46,6 @@ public async Task TryResolveParameters(IEnumerable plan var parameterArray = parameters.ToArray(); var plannerToMetadataMaps = MapParameterMetadataToPlanners(plannerAllocations); - var includedObjectives = currentTemplate.PlanObjectives.ToList(); - if(plannerToMetadataMaps is null) return false; @@ -66,7 +64,7 @@ public async Task TryResolveParameters(IEnumerable plan return true; } - return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, codes, metadata, planQueue, parameterArray, batchSize, includedObjectives, cancellationToken); + return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, codes, metadata, planQueue, parameterArray, batchSize, cancellationToken); }); var results = await Task.WhenAll(planningTasks); @@ -95,7 +93,6 @@ private async Task RequestNewPlans(IPlannerService planner, ConcurrentQueue planQueue, Parameter[] parameterArray, int batchSize, - List includedObjectives, CancellationToken cancellationToken) { var planTransaction = new PlannerTransaction() @@ -113,7 +110,7 @@ private async Task RequestNewPlans(IPlannerService planner, planTransaction.TimeRequestSent = DateTime.UtcNow.ToTimestamp(); //Create the plan request. Store it in the transaction. - var planRequest = CreatePlanningRequest(plannableParameters, seedExperiments, seedAnalysesArr, statusCodes, batchSize, includedObjectives, metadata); + var planRequest = CreatePlanningRequest(plannableParameters, seedExperiments, seedAnalysesArr, statusCodes, batchSize, metadata); planTransaction.PlanningRequest = planRequest; var planResponse = await planner.Plan(planRequest, cancellationToken); @@ -165,7 +162,6 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam AnalysisResponse[] seedAnalysesArr, List statusCodes, int batchSize, - List includedObjectives, RequestMetadata metadata) { //Create the plan request. Store it in the transaction. @@ -189,7 +185,7 @@ private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParam if(defaultObjective is not null && defaultObjective.HasNumberValue) planRequest.AnalysisResults.Add(defaultObjective.NumberValue); - var analysisData = CreateAnalysisData(currentAnalysis, currentExp, includedObjectives); + var analysisData = CreateAnalysisData(currentAnalysis, currentExp); planRequest.AnalysisData.Add(analysisData); } planRequest.PreviousPlanStatusCodes.AddRange(statusCodes); @@ -286,16 +282,24 @@ private static PlanningParameter ConvertToPlanningParameter(ParameterMetadata me return parameter; } - private AnalysisData CreateAnalysisData(AnalysisResponse analysis, ExperimentOverview experiment, List includedObjectives) + private AnalysisData CreateAnalysisData(AnalysisResponse analysis, ExperimentOverview experiment) { var newData = new AnalysisData(); - foreach(var objective in analysis.Objectives) + // If no objectives were specified, include all of them. + if(experiment.Template.PlanObjectives.Count == 0) { - if(experiment.Template.PlanObjectives.Any(obj => obj == objective.ObjectiveName) && includedObjectives.Contains(objective.ObjectiveName)) + foreach(var objective in analysis.Objectives) newData.AnalysisObjectives.Add(objective); } + else + { + foreach(var objective in analysis.Objectives) + if(experiment.Template.PlanObjectives.Any(obj => obj == objective.ObjectiveName)) + newData.AnalysisObjectives.Add(objective); + } + return newData; } diff --git a/UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs index 602e1b1f..77c9e0c5 100644 --- a/UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs @@ -143,6 +143,9 @@ public void Save() _experimentTemplate.AnalyzerMaps.Clear(); _experimentTemplate.AnalyzerMaps.AddRange(analyzerMappings); + if(_experimentTemplate.AnalyzerId != AnalyzerId) + _experimentTemplate.PlanObjectives.Clear(); + _experimentTemplate.AnalyzerId = AnalyzerId; } diff --git a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index b5fe890f..17c53ead 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -28,6 +28,7 @@ public PlanningViewModel(CampaignTemplate template, _selectedAnalyzer = selectedAnalyzer; PlannerAdapters = new ReadOnlyCollection(plannerAdapters.ToList()); PlannerAllocationEditors = template.PlannableParameters.Select(metadata => new PlannerAllocationEditorViewModel(metadata, template.PlannerAllocations.FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, client, notificationService)).ToArray(); + AnalyzerName = _selectedAnalyzer?.Name ?? "NONE"; } public async Task UpdateAnalyzerObjectives() @@ -48,6 +49,8 @@ public async Task UpdateAnalyzerObjectives() public List IncludedObjectives { get; private set; } = []; + public string AnalyzerName { get; private set; } = "NONE"; + public void Save() { //We're not updating everything here maybe? Or maybe I'll just update the ARES core stuff. diff --git a/UI/Features/CampaignEdit/Views/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor index f009ce40..cbfaf081 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -55,7 +55,16 @@ else { -

Select an Analyzer to See Objectives

+ if(ViewModel!.AnalyzerName == "NONE") + { +

Select an Analyzer to See Objectives

+ } + + else + { +

Your Selected Analyer (@ViewModel!.AnalyzerName) Doesn't Report Any Objective Outputs

+

If your Analyzer was built prior to the multi-objective updates this is normal. Consider updating your Analyzer.

+ } }
From 1996b3d792acd1c68ff633f55094230428d59456 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 18 Aug 2026 17:24:49 -0400 Subject: [PATCH 13/19] Fixed a notification stream not being disposed properly, which could sometimes caused errors. --- UI/Components/Layouts/MainLayout.razor | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UI/Components/Layouts/MainLayout.razor b/UI/Components/Layouts/MainLayout.razor index 4986085c..79678a93 100644 --- a/UI/Components/Layouts/MainLayout.razor +++ b/UI/Components/Layouts/MainLayout.razor @@ -124,6 +124,8 @@ private CancellationTokenSource _cts = new(); private string _loadingMessage = "Initializing ARES OS..."; private IDisposable? _driverSubscription; + private IDisposable? _notificationSubscription; + private bool _isDisposed; private List _approvedCssPaths = new(); public bool HasUnreadNotifications = false; @@ -153,7 +155,7 @@ notificationService.OnNotificationReceived += HandleBackgroundNotification; HasUnreadNotifications = notificationProvider.GetAllNotifications().Any(n => !n.IsRead); - notificationProvider.Connect() + _notificationSubscription = notificationProvider.Connect() .Throttle(TimeSpan.FromMilliseconds(500)) .Subscribe(_ => InvokeAsync(NotificationsUpdated)); @@ -168,6 +170,9 @@ private Task NotificationsUpdated() { + if(_isDisposed) + return Task.CompletedTask; + HasUnreadNotifications = notificationProvider.GetAllNotifications().Any(n => !n.IsRead); StateHasChanged(); return Task.CompletedTask; @@ -274,8 +279,12 @@ public void Dispose() { + _isDisposed = true; + Tracker.OnSystemReady -= HandleSystemReady; notificationService.OnNotificationReceived -= HandleBackgroundNotification; + + _notificationSubscription?.Dispose(); _driverSubscription?.Dispose(); _cts.Cancel(); From 4ed9973475e9c807665bbdc33c8eb302fc85280a Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 18 Aug 2026 17:29:51 -0400 Subject: [PATCH 14/19] Fixed some stability issues with the campaign designer, smoothed out a few loading quirks that could result in erradic behavior. --- .../ViewModels/CampaignDesignerViewModel.cs | 91 +++++-- .../Views/AnalyzerInputDesigner.razor | 32 ++- .../CampaignEdit/Views/CampaignDesigner.razor | 247 +++++++++--------- UI/Features/CampaignEdit/Views/Planning.razor | 108 ++++---- 4 files changed, 265 insertions(+), 213 deletions(-) diff --git a/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs index a2ba24c5..b78352b6 100644 --- a/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs @@ -50,41 +50,86 @@ public CampaignDesignerViewModel( }; } - [Reactive] public partial bool IsCreatingCampaign { get; set; } + [Reactive] + public partial bool IsCreatingCampaign { get; set; } - [Reactive] public partial bool IsNotCreatingCampaign { get; set; } + [Reactive] + public partial bool IsNotCreatingCampaign { get; set; } - [Reactive] public partial string Placeholder { get; set; } + [Reactive] + public partial string Placeholder { get; set; } - public PlannableParameterDesignerViewModel? PlannableParameterDesigner { get; private set; } + [Reactive] + public partial PlannableParameterDesignerViewModel? PlannableParameterDesigner { get; private set; } - public ExperimentDesignerViewModel? ExperimentDesigner { get; private set; } + [Reactive] + public partial ExperimentDesignerViewModel? ExperimentDesigner { get; private set; } - public StartupDesignerViewModel? StartupDesigner { get; private set; } + [Reactive] + public partial StartupDesignerViewModel? StartupDesigner { get; private set; } - public CloseoutDesignerViewModel? CloseoutDesigner { get; private set; } + [Reactive] + public partial CloseoutDesignerViewModel? CloseoutDesigner { get; private set; } - public PlanningViewModel? PlanningDesigner { get; private set; } + [Reactive] + public partial PlanningViewModel? PlanningDesigner { get; private set; } - public AnalyzerDesignerViewModel? AnalyzerDesignerViewModel { get; private set; } + [Reactive] + public partial AnalyzerDesignerViewModel? AnalyzerDesignerViewModel { get; private set; } public string CampaignName { get; set; } = "Unnamed Campaign"; - public CampaignTemplate CampaignTemplate + public CampaignTemplate CampaignTemplate { get; private set; } = null!; + + [Reactive] + public partial bool CreationIsErrorFree { get; set; } + + [Reactive] + public partial string? CreationErrorText { get; set; } + + public async Task InitializeNewCampaignAsync() { - private get => _campaignTemplate; + var template = new CampaignTemplate + { + Name = Placeholder, + UniqueId = Guid.NewGuid().ToString(), + ExperimentTemplate = new ExperimentTemplate { UniqueId = Guid.NewGuid().ToString(), Name = "New Experiment" } + }; - set + await LoadCampaignTemplateAsync(template); + } + + public async Task SelectCampaignById(Guid campaignId) + { + var request = new CampaignRequest { - _campaignTemplate = value; - _editContext.CurrentlyEditingCampaign = value; - _ = Init(value); - } + UniqueId = campaignId.ToString(), + }; + + var template = await _automationClient.GetSingleCampaign(request, null); + await LoadCampaignTemplateAsync(template); } - [Reactive] public partial bool CreationIsErrorFree { get; set; } + public async Task LoadCampaignTemplateAsync(CampaignTemplate campaignTemplate) + { + CampaignTemplate = campaignTemplate; + _editContext.CurrentlyEditingCampaign = campaignTemplate; + CampaignName = campaignTemplate.Name; + + PlannableParameterDesigner = _plannableParameterDesignerFactory.Create(campaignTemplate.PlannableParameters, campaignTemplate.ExperimentTemplate); + ExperimentDesigner = _experimentDesignerFactory.Create(campaignTemplate.ExperimentTemplate); + StartupDesigner = _startupDesignerFactory.Create(campaignTemplate.StartupTemplate); + CloseoutDesigner = _closeoutDesignerFactory.Create(campaignTemplate.CloseoutTemplate); + PlanningDesigner = await _planningDesignerFactory.Create(campaignTemplate); + + var commandDesigners = ExperimentDesigner?.StepDesigners?.SelectMany(sd => sd.CommandDesigners) ?? []; + var startupDesigners = StartupDesigner?.StartupStepDesigners?.SelectMany(ssd => ssd.CommandDesigners) ?? []; - [Reactive] public partial string? CreationErrorText { get; set; } + if(campaignTemplate.ExperimentTemplate is not null) + { + AnalyzerDesignerViewModel = _analyzerInputDesignerFactory.Create(campaignTemplate.ExperimentTemplate, commandDesigners, startupDesigners); + } + } private async Task Init(CampaignTemplate campaignTemplate) { @@ -126,16 +171,6 @@ public CampaignTemplate Save() return CampaignTemplate; } - public async Task SelectCampaignById(Guid campaignId) - { - var request = new CampaignRequest - { - UniqueId = campaignId.ToString(), - }; - - CampaignTemplate = await _automationClient.GetSingleCampaign(request, null); - } - public async Task Update() { if(string.IsNullOrEmpty(CampaignName)) diff --git a/UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor b/UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor index 1400352a..0e8f44ff 100644 --- a/UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor +++ b/UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor @@ -2,10 +2,11 @@ @inherits ReactiveUI.Blazor.ReactiveComponentBase -@if(DesignerLoading) +@if(_designerLoading) { -
Loading...
+ } + else {
@@ -37,14 +38,27 @@ else } @code { - protected bool DesignerLoading { get; set; } = false; + private bool _designerLoading = true; + private AnalyzerDesignerViewModel? _lastViewModel; - protected override async Task OnInitializedAsync() + protected override async Task OnParametersSetAsync() { - DesignerLoading = true; - await ViewModel!.UpdateAvailableAnalyzers(); - await ViewModel!.UpdateMappings(); - DesignerLoading = false; - StateHasChanged(); + // Only run if ViewModel is present and has actually changed + if(ViewModel is not null && ViewModel != _lastViewModel) + { + _lastViewModel = ViewModel; + _designerLoading = true; + + try + { + await ViewModel.UpdateAvailableAnalyzers(); + await ViewModel.UpdateMappings(); + } + finally + { + _designerLoading = false; + StateHasChanged(); + } + } } } diff --git a/UI/Features/CampaignEdit/Views/CampaignDesigner.razor b/UI/Features/CampaignEdit/Views/CampaignDesigner.razor index 51644550..a8b3221e 100644 --- a/UI/Features/CampaignEdit/Views/CampaignDesigner.razor +++ b/UI/Features/CampaignEdit/Views/CampaignDesigner.razor @@ -14,47 +14,39 @@
-
- - - - - - - - - - - - -
-
- @if (SelectedSubOption1) - { -
- -
- } - @if (SelectedSubOption2) - { - - } - @if (SelectedSubOption3) - { - - } - @if (SelectedSubOption4) - { - - } - @if (SelectedSubOption5) - { - - } - @if (SelectedSubOption6) - { - - } +
+ + + + + + +
+ +
+ @switch(ActiveTab) + { + case DesignerTab.Parameters: +
+ +
+ break; + case DesignerTab.Experiment: + + break; + case DesignerTab.Planning: + + break; + case DesignerTab.Startup: + + break; + case DesignerTab.Closeout: + + break; + case DesignerTab.Analyzer: + + break; + }
@@ -70,8 +62,19 @@ protected override async Task OnInitializedAsync() { - if (CampaignId.HasValue) + if(CampaignId.HasValue) + await ViewModel!.SelectCampaignById(CampaignId.Value); + } + + private bool _isLoading = true; + + protected override async Task OnParametersSetAsync() + { + if(CampaignId.HasValue) await ViewModel!.SelectCampaignById(CampaignId.Value); + + else + await ViewModel!.InitializeNewCampaignAsync(); } public bool SelectedSubOption1 { get; set; } = true; @@ -81,8 +84,6 @@ public bool SelectedSubOption5 { get; set; } public bool SelectedSubOption6 { get; set; } - - public void SelectParam() { SelectedSubOption1 = true; @@ -93,55 +94,55 @@ SelectedSubOption6 = false; } - public void SelectExp() - { - SelectedSubOption2 = true; - SelectedSubOption1 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } + public void SelectExp() + { + SelectedSubOption2 = true; + SelectedSubOption1 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } - public void SelectPlan() - { - SelectedSubOption2 = false; - SelectedSubOption1 = false; - SelectedSubOption3 = true; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } + public void SelectPlan() + { + SelectedSubOption2 = false; + SelectedSubOption1 = false; + SelectedSubOption3 = true; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } - public void SelectStartup() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = true; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } + public void SelectStartup() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = true; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } - public void SelectCloseout() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = true; - SelectedSubOption6 = false; - } + public void SelectCloseout() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = true; + SelectedSubOption6 = false; + } - public void SelectAnalyzer() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = true; - } + public void SelectAnalyzer() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = true; + } private async Task SaveClick() { @@ -156,44 +157,40 @@ } } - private async void TitleClicked(MouseEventArgs obj) - { - string newName = ViewModel!.CampaignName; - var result = await DialogService.OpenAsync("Campaign Name Edit", ds => - @
-
-
- Campaign Name -
- @if(newName == "New Campaign 1") - { - newName = ""; - - } - - else - { - - } - -
-
- - -
+ private async Task TitleClicked(MouseEventArgs obj) + { + string currentName = ViewModel!.CampaignName; + string newName = currentName == "New Campaign 1" ? "" : currentName; + + var result = await DialogService.OpenAsync("Campaign Name Edit", ds => + @
+
+ Campaign Name +
- ); - - if(newName == "") - { - ViewModel!.CampaignName = "New Campaign 1"; - } - - else if(result ?? false) - { - ViewModel!.CampaignName = newName; - } +
+ + +
+
+ ); + + if(result == true) + { + ViewModel!.CampaignName = string.IsNullOrWhiteSpace(newName) ? "New Campaign 1" : newName; } + } + + public enum DesignerTab + { + Parameters, + Startup, + Experiment, + Closeout, + Analyzer, + Planning + } + public DesignerTab ActiveTab { get; set; } = DesignerTab.Parameters; } diff --git a/UI/Features/CampaignEdit/Views/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor index cbfaf081..841ad5e4 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -4,72 +4,78 @@
-
-
-

Planner Assignment

- @if(ViewModel?.PlannerAllocationEditors is null) - { -

Loading allocations...

- } - else - { - @foreach(var editor in ViewModel.PlannerAllocationEditors) + @if(ViewModel is null) + { + + } + + else + { +
+
+

Planner Assignment

+ @if(ViewModel?.PlannerAllocationEditors is null) { - +

Loading allocations...

} - } -
- -
-

Analyzer Objectives

- @if(ViewModel?.AvailableObjectives is not null) - { - @foreach(var item in ViewModel.AvailableObjectives.Fields) + else { - var isSelected = ViewModel!.SelectedObjectives.Contains(item); - -
- - - - Objective Name: @item.Key - - - - Objective Description: @item.Value.Description - + @foreach(var editor in ViewModel.PlannerAllocationEditors) + { + + } + } +
- - +
+

Analyzer Objectives

+ @if(ViewModel?.AvailableObjectives is not null) + { + @foreach(var item in ViewModel.AvailableObjectives.Fields) + { + var isSelected = ViewModel!.SelectedObjectives.Contains(item); + +
+ + + + Objective Name: @item.Key + + + + Objective Description: @item.Value.Description + + + + - - - -
- } - } - - else - { - if(ViewModel!.AnalyzerName == "NONE") - { -

Select an Analyzer to See Objectives

+ + + +
+ } } else { -

Your Selected Analyer (@ViewModel!.AnalyzerName) Doesn't Report Any Objective Outputs

-

If your Analyzer was built prior to the multi-objective updates this is normal. Consider updating your Analyzer.

+ if(ViewModel!.AnalyzerName == "NONE") + { +

Select an Analyzer to See Objectives

+ } + + else + { +

Your Selected Analyer (@ViewModel!.AnalyzerName) Doesn't Report Any Objective Outputs

+

If your Analyzer was built prior to the multi-objective updates this is normal. Consider updating your Analyzer.

+ } } - } +
-
- - + }
From b3c4bc1bab060865672e4851d9fc99754c976872 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 19 Aug 2026 12:16:18 -0400 Subject: [PATCH 15/19] Fixed a small bug that caused issues displaying analyzer transaction data properly --- .../DataMappers/CampaignDatasetGenerator.cs | 2 +- .../DataExplorer/AresDatasetTable.razor | 54 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs index fd24b5ae..3d6367d5 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -471,7 +471,7 @@ private static AresDataRow CreateExperimentRow(ExperimentExecutionSummary experi AddFlattenedValue(data, $"Objective.{objective.ObjectiveName}", objective.ObjectiveValue, cancellationToken); } - //If we have no listed objectives AD + //If we have no listed objectives AND there is an Analyzer assigned, assume it's using the legacy standard else if(experiment.ExperimentOverview.AnalysisOverview.AnalyzerInfo.Name != "NONE") { var aresValueResult = AresValueHelper.CreateNumber(experiment.ExperimentOverview.AnalysisOverview.Result); diff --git a/UI/Features/DataExplorer/AresDatasetTable.razor b/UI/Features/DataExplorer/AresDatasetTable.razor index 1125295e..e92997ac 100644 --- a/UI/Features/DataExplorer/AresDatasetTable.razor +++ b/UI/Features/DataExplorer/AresDatasetTable.razor @@ -101,13 +101,59 @@ else : column.Name; } - private static string GetCellDisplayValue(AresDataRow row, AresDataColumn column) + private static string GetCellDisplayValue(AresDataRow row, AresDataColumn column) + { + if(row.Data?.Fields is null || string.IsNullOrEmpty(column.Name)) + return string.Empty; + + if(row.Data.Fields.TryGetValue(column.Name, out var directValue)) + return directValue.Stringify(); + + if(column.Name.Contains('.')) { - if (row.Data?.Fields.TryGetValue(column.Name, out var value) != true) + var parts = column.Name.Split('.'); + var currentStruct = row.Data; + AresValue? targetValue = null; + + for(var i = 0; i < parts.Length; i++) + { + var part = parts[i]; + if(currentStruct is null) + break; + + if(!currentStruct.Fields.TryGetValue(part, out var childValue)) { - return string.Empty; + var matchedKey = currentStruct.Fields.Keys + .FirstOrDefault(k => string.Equals(k, part, StringComparison.OrdinalIgnoreCase)); + + if(matchedKey is null || !currentStruct.Fields.TryGetValue(matchedKey, out childValue)) + break; } - return value.Stringify(); + if(i == parts.Length - 1) + { + targetValue = childValue; + } + else if(childValue.KindCase == AresValue.KindOneofCase.StructValue) + { + currentStruct = childValue.StructValue; + } + else + { + break; + } + } + + if(targetValue is not null) + return targetValue.Stringify(); } + + var fallbackKey = row.Data.Fields.Keys + .FirstOrDefault(k => string.Equals(k, column.Name, StringComparison.OrdinalIgnoreCase)); + + if(fallbackKey is not null && row.Data.Fields.TryGetValue(fallbackKey, out var fallbackValue)) + return fallbackValue.Stringify(); + + return string.Empty; + } } From 8d9c47e9d93c8046e435b4a245dbbab3fb9cdb3a Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 19 Aug 2026 12:32:17 -0400 Subject: [PATCH 16/19] Updated migration files --- ...nalyzer-upgrades_AresDbContext.Designer.cs | 2327 ++++++++++++++++ ...7202202_analyzer-upgrades_AresDbContext.cs | 96 + ...r-upgrades_AresIdentityContext.Designer.cs | 277 ++ ...5_analyzer-upgrades_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 50 +- ...nalyzer-upgrades_AresDbContext.Designer.cs | 2341 +++++++++++++++++ ...7202208_analyzer-upgrades_AresDbContext.cs | 96 + ...r-upgrades_AresIdentityContext.Designer.cs | 279 ++ ...1_analyzer-upgrades_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 50 +- ...nalyzer-upgrades_AresDbContext.Designer.cs | 2322 ++++++++++++++++ ...7202156_analyzer-upgrades_AresDbContext.cs | 96 + ...r-upgrades_AresIdentityContext.Designer.cs | 268 ++ ...9_analyzer-upgrades_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 50 +- 15 files changed, 8315 insertions(+), 3 deletions(-) create mode 100644 AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.cs diff --git a/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.Designer.cs new file mode 100644 index 00000000..a53e8b0f --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.Designer.cs @@ -0,0 +1,2327 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260817202202_analyzer-upgrades_AresDbContext")] + partial class analyzerupgrades_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfo") + .HasColumnType("jsonb"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Objectives") + .HasColumnType("jsonb"); + + b.Property("Result") + .HasColumnType("double precision"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique(); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisOutcome") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ErrorString") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("real"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisOutcome") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ErrorString") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Objectives") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalysisResponses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ObjectiveOutputSchema") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisRequest") + .HasColumnType("jsonb"); + + b.Property("AnalysisResponse") + .HasColumnType("jsonb"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("AnalyzerName") + .HasColumnType("text"); + + b.Property("AnalyzerResponse") + .HasColumnType("jsonb"); + + b.Property("AnalyzerType") + .HasColumnType("text"); + + b.Property("AnalyzerVersion") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TagName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandLatency") + .HasColumnType("jsonb"); + + b.Property("CommandRetryLimit") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DisplayCompatabilityWarnings") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("DisplayDataCollectionWidget") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ExperimentRetryLimit") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("RetryCooldown") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("CurrentVersionId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("CustomCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("CustomCommandVersionId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Schema") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandVersionId"); + + b.ToTable("CustomCommandParameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("CustomCommandId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OutputSchema") + .HasColumnType("text"); + + b.Property("ScriptBody") + .HasColumnType("text"); + + b.Property("VersionNumber") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandId", "VersionNumber") + .IsUnique(); + + b.ToTable("CustomCommandVersions", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignId") + .HasColumnType("text"); + + b.Property("CampaignName") + .HasColumnType("text"); + + b.Property("CampaignNotes") + .HasColumnType("text"); + + b.Property("CampaignTags") + .HasColumnType("text"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandId") + .HasColumnType("text"); + + b.Property("CommandName") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceName") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusMessage") + .HasColumnType("text"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uuid"); + + b.Property("VariableName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandDescription") + .HasColumnType("text"); + + b.Property("CommandId") + .HasColumnType("text"); + + b.Property("CommandName") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StatusCode") + .HasColumnType("integer"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("TemplateId") + .HasColumnType("text"); + + b.Property("VarName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AwaitUserInput") + .HasColumnType("boolean"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Error") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("StatusCode") + .HasColumnType("integer"); + + b.Property("Success") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeviceInfoId") + .HasColumnType("uuid"); + + b.Property("InputSchema") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OutputSchema") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceName") + .HasColumnType("text"); + + b.Property("DeviceSettings") + .HasColumnType("text"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.Property("IsSimulated") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Deltas") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IntervalMs") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LoggingEnabled") + .HasColumnType("boolean"); + + b.Property("LoggingType") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BaudRate") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PortName") + .HasColumnType("text"); + + b.Property("SerialId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Handling") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LocaltimeOffset") + .HasColumnType("text"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("TimeFinished") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeStarted") + .HasColumnType("timestamp with time zone"); + + b.Property("Timezone") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ResultOutputPath") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("TemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Maximum") + .HasColumnType("double precision"); + + b.Property("Minimum") + .HasColumnType("double precision"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uuid"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uuid"); + + b.Property("PlannerId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("MultiObjectiveCapable") + .HasColumnType("boolean"); + + b.Property("PlannerInfoId") + .HasColumnType("uuid"); + + b.Property("ServiceName") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerType") + .HasColumnType("text"); + + b.Property("PlannerVersion") + .HasColumnType("text"); + + b.Property("PlanningRequest") + .HasColumnType("jsonb"); + + b.Property("PlanningResponse") + .HasColumnType("jsonb"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.Property("StepName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeviceCommandId") + .HasColumnType("uuid"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceType") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceCommandId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("OutputVarName") + .HasColumnType("text"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("DeviceCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("AnalyzerMaps") + .HasColumnType("text"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uuid"); + + b.Property("CampaignExperimentId") + .HasColumnType("uuid"); + + b.Property("CampaignStartupId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PlanObjectives") + .HasColumnType("text"); + + b.Property("Resolved") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataSchema") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SourceJson") + .HasColumnType("jsonb") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NotPlannable") + .HasColumnType("boolean"); + + b.Property("OutputName") + .HasColumnType("text"); + + b.Property("ParameterId") + .HasColumnType("uuid"); + + b.Property("PlannerDescription") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("Schema") + .HasColumnType("text"); + + b.Property("Unit") + .HasColumnType("text"); + + b.Property("UseDefault") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChartTitle") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("GridH") + .HasColumnType("integer"); + + b.Property("GridW") + .HasColumnType("integer"); + + b.Property("GridX") + .HasColumnType("integer"); + + b.Property("GridY") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("integer"); + + b.Property("Paths") + .HasColumnType("jsonb"); + + b.Property("PollingRate") + .HasColumnType("integer"); + + b.Property("ShowDataLabels") + .HasColumnType("boolean"); + + b.Property("ShowMarkers") + .HasColumnType("boolean"); + + b.Property("Style") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AssociatedDeviceId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataType") + .HasColumnType("integer"); + + b.Property("IsPlottable") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Path") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommandVersion", null) + .WithMany("InputParameters") + .HasForeignKey("CustomCommandVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommand", null) + .WithMany() + .HasForeignKey("CustomCommandId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.DeviceCommand", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "DeviceCommandId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Ares.Datamodel.Templates.CustomCommandInvocation", "CustomCommandInvocation", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("uuid"); + + b1.Property("CustomCommandId") + .HasColumnType("text"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.OwnsOne("Ares.Datamodel.Templates.SystemCommand", "SystemCommand", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("uuid"); + + b1.Property("Operation") + .HasColumnType("integer"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.Navigation("CustomCommandInvocation"); + + b.Navigation("SystemCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("DeviceCommand") + .HasForeignKey("Ares.Datamodel.Templates.DeviceCommand", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("ArgumentBindings") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Navigation("InputParameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("ArgumentBindings"); + + b.Navigation("DeviceCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.cs b/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.cs new file mode 100644 index 00000000..b82d1d3e --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260817202202_analyzer-upgrades_AresDbContext.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresDb +{ + /// + public partial class analyzerupgrades_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PlanObjectives", + table: "ExperimentTemplates", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions", + type: "text", + nullable: false, + defaultValue: "{}"); + + migrationBuilder.AddColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "AnalysisOverview", + type: "text", + nullable: false, + defaultValue: "[]"); + + migrationBuilder.CreateTable( + name: "AnalysisResponses", + columns: table => new + { + UniqueId = table.Column(type: "uuid", nullable: false), + Objectives = table.Column(type: "text", nullable: true), + AnalysisOutcome = table.Column(type: "integer", nullable: false), + ErrorString = table.Column(type: "text", nullable: true), + CreationTime = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + LastModified = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("PK_AnalysisResponses", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AnalysisResponses"); + + migrationBuilder.DropColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities"); + + migrationBuilder.DropColumn( + name: "PlanObjectives", + table: "ExperimentTemplates"); + + migrationBuilder.DropColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions"); + + migrationBuilder.DropColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities"); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "AnalysisOverview"); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "text", + nullable: true); + } + } +} \ No newline at end of file diff --git a/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..c0e629ac --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.Designer.cs @@ -0,0 +1,277 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260817202205_analyzer-upgrades_AresIdentityContext")] + partial class analyzerupgrades_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.cs b/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.cs new file mode 100644 index 00000000..24f28aef --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260817202205_analyzer-upgrades_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresIdentity +{ + /// + public partial class analyzerupgrades_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs index 71c94b90..1f8b1ebb 100644 --- a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs @@ -44,6 +44,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); + b.Property("Objectives") + .HasColumnType("jsonb"); + + b.Property("Result") + .HasColumnType("double precision"); + b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -52,6 +58,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisOutcome") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ErrorString") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("real"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") @@ -79,7 +115,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UniqueId"); - b.ToTable("Analyses", (string)null); + b.ToTable("AnalysisResponses", (string)null); }); modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => @@ -101,6 +137,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); + b.Property("ObjectiveOutputSchema") + .HasColumnType("text"); + b.Property("SettingsSchema") .HasColumnType("text"); @@ -223,6 +262,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AnalyzerName") .HasColumnType("text"); + b.Property("AnalyzerResponse") + .HasColumnType("jsonb"); + b.Property("AnalyzerType") .HasColumnType("text"); @@ -1157,6 +1199,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); + b.Property("MultiObjectiveCapable") + .HasColumnType("boolean"); + b.Property("PlannerInfoId") .HasColumnType("uuid"); @@ -1527,6 +1572,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("PlanObjectives") + .HasColumnType("text"); + b.Property("Resolved") .HasColumnType("boolean"); diff --git a/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.Designer.cs new file mode 100644 index 00000000..dea33aee --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.Designer.cs @@ -0,0 +1,2341 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260817202208_analyzer-upgrades_AresDbContext")] + partial class analyzerupgrades_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfo") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); + + b.Property("Result") + .HasColumnType("float"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique() + .HasFilter("[ExperimentOverviewId] IS NOT NULL"); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisOutcome") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ErrorString") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("real"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisOutcome") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ErrorString") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalysisResponses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ObjectiveOutputSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalysisResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerName") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerType") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TagName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandLatency") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandRetryLimit") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DisplayCompatabilityWarnings") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("DisplayDataCollectionWidget") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("ExperimentRetryLimit") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("RetryCooldown") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("CurrentVersionId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("CustomCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("CustomCommandVersionId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Schema") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandVersionId"); + + b.ToTable("CustomCommandParameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("CustomCommandId") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OutputSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("ScriptBody") + .HasColumnType("nvarchar(max)"); + + b.Property("VersionNumber") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandId", "VersionNumber") + .IsUnique(); + + b.ToTable("CustomCommandVersions", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignId") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignName") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignNotes") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignTags") + .HasColumnType("nvarchar(max)"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandId") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StatusMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("VariableName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandId") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("TemplateId") + .HasColumnType("nvarchar(max)"); + + b.Property("VarName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AwaitUserInput") + .HasColumnType("bit"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Error") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique() + .HasFilter("[CommandExecutionSummaryId] IS NOT NULL"); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("InputSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OutputSchema") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceName") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceSettings") + .HasColumnType("nvarchar(max)"); + + b.Property("DriverId") + .HasColumnType("nvarchar(max)"); + + b.Property("IsSimulated") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Deltas") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("IntervalMs") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LoggingEnabled") + .HasColumnType("bit"); + + b.Property("LoggingType") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BaudRate") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PortName") + .HasColumnType("nvarchar(max)"); + + b.Property("SerialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Handling") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LocaltimeOffset") + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("TimeFinished") + .HasColumnType("datetime2"); + + b.Property("TimeStarted") + .HasColumnType("datetime2"); + + b.Property("Timezone") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique() + .HasFilter("[CampaignExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique() + .HasFilter("[CommandExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique() + .HasFilter("[StepExecutionSummaryId] IS NOT NULL"); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ResultOutputPath") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("TemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Maximum") + .HasColumnType("float"); + + b.Property("Minimum") + .HasColumnType("float"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("MultiObjectiveCapable") + .HasColumnType("bit"); + + b.Property("PlannerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("ServiceName") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerType") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.Property("StepName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceCommandId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceType") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceCommandId") + .IsUnique() + .HasFilter("[DeviceCommandId] IS NOT NULL"); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("OutputVarName") + .HasColumnType("nvarchar(max)"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique() + .HasFilter("[CommandTemplateId] IS NOT NULL"); + + b.ToTable("DeviceCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerMaps") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExperimentId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignStartupId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanObjectives") + .HasColumnType("nvarchar(max)"); + + b.Property("Resolved") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique() + .HasFilter("[CampaignCloseoutId] IS NOT NULL"); + + b.HasIndex("CampaignExperimentId") + .IsUnique() + .HasFilter("[CampaignExperimentId] IS NOT NULL"); + + b.HasIndex("CampaignStartupId") + .IsUnique() + .HasFilter("[CampaignStartupId] IS NOT NULL"); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SourceJson") + .HasColumnType("nvarchar(max)") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NotPlannable") + .HasColumnType("bit"); + + b.Property("OutputName") + .HasColumnType("nvarchar(max)"); + + b.Property("ParameterId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Schema") + .HasColumnType("nvarchar(max)"); + + b.Property("Unit") + .HasColumnType("nvarchar(max)"); + + b.Property("UseDefault") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique() + .HasFilter("[ParameterId] IS NOT NULL"); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ChartTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("GridH") + .HasColumnType("int"); + + b.Property("GridW") + .HasColumnType("int"); + + b.Property("GridX") + .HasColumnType("int"); + + b.Property("GridY") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("int"); + + b.Property("Paths") + .HasColumnType("nvarchar(max)"); + + b.Property("PollingRate") + .HasColumnType("int"); + + b.Property("ShowDataLabels") + .HasColumnType("bit"); + + b.Property("ShowMarkers") + .HasColumnType("bit"); + + b.Property("Style") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssociatedDeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataType") + .HasColumnType("int"); + + b.Property("IsPlottable") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Path") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DriverId") + .HasColumnType("nvarchar(max)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommandVersion", null) + .WithMany("InputParameters") + .HasForeignKey("CustomCommandVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommand", null) + .WithMany() + .HasForeignKey("CustomCommandId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.DeviceCommand", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "DeviceCommandId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Ares.Datamodel.Templates.CustomCommandInvocation", "CustomCommandInvocation", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b1.Property("CustomCommandId") + .HasColumnType("nvarchar(max)"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.OwnsOne("Ares.Datamodel.Templates.SystemCommand", "SystemCommand", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b1.Property("Operation") + .HasColumnType("int"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.Navigation("CustomCommandInvocation"); + + b.Navigation("SystemCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("DeviceCommand") + .HasForeignKey("Ares.Datamodel.Templates.DeviceCommand", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("ArgumentBindings") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Navigation("InputParameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("ArgumentBindings"); + + b.Navigation("DeviceCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.cs b/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.cs new file mode 100644 index 00000000..66bd44cf --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260817202208_analyzer-upgrades_AresDbContext.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresDb +{ + /// + public partial class analyzerupgrades_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PlanObjectives", + table: "ExperimentTemplates", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: "{}"); + + migrationBuilder.AddColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "AnalysisOverview", + type: "nvarchar(max)", + nullable: false, + defaultValue: "[]"); + + migrationBuilder.CreateTable( + name: "AnalysisResponses", + columns: table => new + { + UniqueId = table.Column(type: "uniqueidentifier", nullable: false), + Objectives = table.Column(type: "nvarchar(max)", nullable: true), + AnalysisOutcome = table.Column(type: "int", nullable: false), + ErrorString = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + LastModified = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()") + }, + constraints: table => + { + table.PrimaryKey("PK_AnalysisResponses", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AnalysisResponses"); + + migrationBuilder.DropColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities"); + + migrationBuilder.DropColumn( + name: "PlanObjectives", + table: "ExperimentTemplates"); + + migrationBuilder.DropColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions"); + + migrationBuilder.DropColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities"); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "AnalysisOverview"); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "nvarchar(max)", + nullable: true); + } + } +} \ No newline at end of file diff --git a/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..5eb0ffc9 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.Designer.cs @@ -0,0 +1,279 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260817202211_analyzer-upgrades_AresIdentityContext")] + partial class analyzerupgrades_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.cs b/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.cs new file mode 100644 index 00000000..8e57236d --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260817202211_analyzer-upgrades_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresIdentity +{ + /// + public partial class analyzerupgrades_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs index 8d29bc70..11e62a2b 100644 --- a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs @@ -44,6 +44,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); + + b.Property("Result") + .HasColumnType("float"); + b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -53,6 +59,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisOutcome") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ErrorString") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("real"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") @@ -80,7 +116,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UniqueId"); - b.ToTable("Analyses", (string)null); + b.ToTable("AnalysisResponses", (string)null); }); modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => @@ -102,6 +138,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); + b.Property("ObjectiveOutputSchema") + .HasColumnType("nvarchar(max)"); + b.Property("SettingsSchema") .HasColumnType("nvarchar(max)"); @@ -224,6 +263,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AnalyzerName") .HasColumnType("nvarchar(max)"); + b.Property("AnalyzerResponse") + .HasColumnType("nvarchar(max)"); + b.Property("AnalyzerType") .HasColumnType("nvarchar(max)"); @@ -1164,6 +1206,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); + b.Property("MultiObjectiveCapable") + .HasColumnType("bit"); + b.Property("PlannerInfoId") .HasColumnType("uniqueidentifier"); @@ -1537,6 +1582,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("nvarchar(max)"); + b.Property("PlanObjectives") + .HasColumnType("nvarchar(max)"); + b.Property("Resolved") .HasColumnType("bit"); diff --git a/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.Designer.cs new file mode 100644 index 00000000..1c5941bd --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.Designer.cs @@ -0,0 +1,2322 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresDb +{ + [DbContext(typeof(AresDbContext))] + [Migration("20260817202156_analyzer-upgrades_AresDbContext")] + partial class analyzerupgrades_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfo") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Objectives") + .HasColumnType("TEXT"); + + b.Property("Result") + .HasColumnType("REAL"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentOverviewId") + .IsUnique(); + + b.ToTable("AnalysisOverview"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisOutcome") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ErrorString") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("REAL"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisOutcome") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ErrorString") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Objectives") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalysisResponses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfoId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ObjectiveOutputSchema") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisRequest") + .HasColumnType("TEXT"); + + b.Property("AnalysisResponse") + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("AnalyzerName") + .HasColumnType("TEXT"); + + b.Property("AnalyzerResponse") + .HasColumnType("TEXT"); + + b.Property("AnalyzerType") + .HasColumnType("TEXT"); + + b.Property("AnalyzerVersion") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TagName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLatency") + .HasColumnType("TEXT"); + + b.Property("CommandRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DisplayCompatabilityWarnings") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("DisplayDataCollectionWidget") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("ExperimentRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("RetryCooldown") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("CurrentVersionId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("CustomCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("CustomCommandVersionId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Schema") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandVersionId"); + + b.ToTable("CustomCommandParameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("CustomCommandId") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OutputSchema") + .HasColumnType("TEXT"); + + b.Property("ScriptBody") + .HasColumnType("TEXT"); + + b.Property("VersionNumber") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CustomCommandId", "VersionNumber") + .IsUnique(); + + b.ToTable("CustomCommandVersions", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignId") + .HasColumnType("TEXT"); + + b.Property("CampaignName") + .HasColumnType("TEXT"); + + b.Property("CampaignNotes") + .HasColumnType("TEXT"); + + b.Property("CampaignTags") + .HasColumnType("TEXT"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandId") + .HasColumnType("TEXT"); + + b.Property("CommandName") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("State") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StatusMessage") + .HasColumnType("TEXT"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("TEXT"); + + b.Property("VariableName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionStatusUniqueId"); + + b.ToTable("CommandExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandDescription") + .HasColumnType("TEXT"); + + b.Property("CommandId") + .HasColumnType("TEXT"); + + b.Property("CommandName") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StatusCode") + .HasColumnType("INTEGER"); + + b.Property("StepExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("TemplateId") + .HasColumnType("TEXT"); + + b.Property("VarName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepExecutionSummaryUniqueId"); + + b.ToTable("CommandExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AwaitUserInput") + .HasColumnType("INTEGER"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("StatusCode") + .HasColumnType("INTEGER"); + + b.Property("Success") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.ToTable("DeviceCommandResults", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DeviceInfoId") + .HasColumnType("TEXT"); + + b.Property("InputSchema") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OutputSchema") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceInfoId"); + + b.ToTable("DeviceCommandDescriptor"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("DeviceSettings") + .HasColumnType("TEXT"); + + b.Property("DriverId") + .HasColumnType("TEXT"); + + b.Property("IsSimulated") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SerialInfoUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("SerialInfoUniqueId"); + + b.ToTable("DeviceConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceLoggingSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Deltas") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("IntervalMs") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LoggingEnabled") + .HasColumnType("INTEGER"); + + b.Property("LoggingType") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BaudRate") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PortName") + .HasColumnType("TEXT"); + + b.Property("SerialId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Handling") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LocaltimeOffset") + .HasColumnType("TEXT"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("TimeFinished") + .HasColumnType("TEXT"); + + b.Property("TimeStarted") + .HasColumnType("TEXT"); + + b.Property("Timezone") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ResultOutputPath") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("TemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Maximum") + .HasColumnType("REAL"); + + b.Property("Minimum") + .HasColumnType("REAL"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ParameterUniqueId") + .HasColumnType("TEXT"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("MultiObjectiveCapable") + .HasColumnType("INTEGER"); + + b.Property("PlannerInfoId") + .HasColumnType("TEXT"); + + b.Property("ServiceName") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerType") + .HasColumnType("TEXT"); + + b.Property("PlannerVersion") + .HasColumnType("TEXT"); + + b.Property("PlanningRequest") + .HasColumnType("TEXT"); + + b.Property("PlanningResponse") + .HasColumnType("TEXT"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.Property("StepName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DeviceCommandId") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("DeviceCommandId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("OutputVarName") + .HasColumnType("TEXT"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("DeviceCommands", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("AnalyzerMaps") + .HasColumnType("TEXT"); + + b.Property("CampaignCloseoutId") + .HasColumnType("TEXT"); + + b.Property("CampaignExperimentId") + .HasColumnType("TEXT"); + + b.Property("CampaignStartupId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PlanObjectives") + .HasColumnType("TEXT"); + + b.Property("Resolved") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataSchema") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SourceJson") + .HasColumnType("TEXT") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("InitialValue") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NotPlannable") + .HasColumnType("INTEGER"); + + b.Property("OutputName") + .HasColumnType("TEXT"); + + b.Property("ParameterId") + .HasColumnType("TEXT"); + + b.Property("PlannerDescription") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("Schema") + .HasColumnType("TEXT"); + + b.Property("Unit") + .HasColumnType("TEXT"); + + b.Property("UseDefault") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsParallel") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ChartTitle") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("GridH") + .HasColumnType("INTEGER"); + + b.Property("GridW") + .HasColumnType("INTEGER"); + + b.Property("GridX") + .HasColumnType("INTEGER"); + + b.Property("GridY") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("NumberDisplayPoints") + .HasColumnType("INTEGER"); + + b.Property("Paths") + .HasColumnType("TEXT"); + + b.Property("PollingRate") + .HasColumnType("INTEGER"); + + b.Property("ShowDataLabels") + .HasColumnType("INTEGER"); + + b.Property("ShowMarkers") + .HasColumnType("INTEGER"); + + b.Property("Style") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AssociatedDeviceId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataType") + .HasColumnType("INTEGER"); + + b.Property("IsPlottable") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DisplayName") + .HasColumnType("TEXT"); + + b.Property("DriverId") + .HasColumnType("TEXT"); + + b.Property("FileSizeBytes") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandParameter", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommandVersion", null) + .WithMany("InputParameters") + .HasForeignKey("CustomCommandVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.HasOne("Ares.Datamodel.Automation.CustomCommand", null) + .WithMany() + .HasForeignKey("CustomCommandId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.DeviceCommand", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "DeviceCommandId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Ares.Datamodel.Templates.CustomCommandInvocation", "CustomCommandInvocation", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("TEXT"); + + b1.Property("CustomCommandId") + .HasColumnType("TEXT"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.OwnsOne("Ares.Datamodel.Templates.SystemCommand", "SystemCommand", b1 => + { + b1.Property("CommandTemplateUniqueId") + .HasColumnType("TEXT"); + + b1.Property("Operation") + .HasColumnType("INTEGER"); + + b1.HasKey("CommandTemplateUniqueId"); + + b1.ToTable("CommandTemplates"); + + b1.WithOwner() + .HasForeignKey("CommandTemplateUniqueId"); + }); + + b.Navigation("CustomCommandInvocation"); + + b.Navigation("SystemCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("DeviceCommand") + .HasForeignKey("Ares.Datamodel.Templates.DeviceCommand", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("ArgumentBindings") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Automation.CustomCommandVersion", b => + { + b.Navigation("InputParameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("ArgumentBindings"); + + b.Navigation("DeviceCommand"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.DeviceCommand", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.cs b/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.cs new file mode 100644 index 00000000..22aaa00c --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260817202156_analyzer-upgrades_AresDbContext.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresDb +{ + /// + public partial class analyzerupgrades_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PlanObjectives", + table: "ExperimentTemplates", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities", + type: "INTEGER", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions", + type: "TEXT", + nullable: false, + defaultValue: "{}"); + + migrationBuilder.AddColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "AnalysisOverview", + type: "TEXT", + nullable: false, + defaultValue: "[]"); + + migrationBuilder.CreateTable( + name: "AnalysisResponses", + columns: table => new + { + UniqueId = table.Column(type: "TEXT", nullable: false), + Objectives = table.Column(type: "TEXT", nullable: true), + AnalysisOutcome = table.Column(type: "INTEGER", nullable: false), + ErrorString = table.Column(type: "TEXT", nullable: true), + CreationTime = table.Column(type: "TEXT", nullable: false, defaultValueSql: "DATETIME('now')"), + LastModified = table.Column(type: "TEXT", nullable: false, defaultValueSql: "DATETIME('now')") + }, + constraints: table => + { + table.PrimaryKey("PK_AnalysisResponses", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AnalysisResponses"); + + migrationBuilder.DropColumn( + name: "MultiObjectiveCapable", + table: "PlannerServiceCapabilities"); + + migrationBuilder.DropColumn( + name: "PlanObjectives", + table: "ExperimentTemplates"); + + migrationBuilder.DropColumn( + name: "AnalyzerResponse", + table: "AnalyzerTransactions"); + + migrationBuilder.DropColumn( + name: "ObjectiveOutputSchema", + table: "AnalyzerCapabilities"); + + migrationBuilder.DropColumn( + name: "Objectives", + table: "AnalysisOverview"); + + migrationBuilder.AddColumn( + name: "Objectives", + table: "Analyses", + type: "TEXT", + nullable: true); + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..659faa95 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.Designer.cs @@ -0,0 +1,268 @@ +// +using System; +using AresService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresIdentity +{ + [DbContext(typeof(AresIdentityContext))] + [Migration("20260817202159_analyzer-upgrades_AresIdentityContext")] + partial class analyzerupgrades_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.cs b/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.cs new file mode 100644 index 00000000..08cce0fd --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260817202159_analyzer-upgrades_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresIdentity +{ + /// + public partial class analyzerupgrades_AresIdentityContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs index 9df86357..42c2c8c1 100644 --- a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs @@ -39,6 +39,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("Objectives") + .HasColumnType("TEXT"); + + b.Property("Result") + .HasColumnType("REAL"); + b.HasKey("UniqueId"); b.HasIndex("ExperimentOverviewId") @@ -47,6 +53,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AnalysisOverview"); }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.Analysis", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisOutcome") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ErrorString") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("REAL"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyses", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalysisResponse", b => { b.Property("UniqueId") @@ -74,7 +110,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UniqueId"); - b.ToTable("Analyses", (string)null); + b.ToTable("AnalysisResponses", (string)null); }); modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => @@ -96,6 +132,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("ObjectiveOutputSchema") + .HasColumnType("TEXT"); + b.Property("SettingsSchema") .HasColumnType("TEXT"); @@ -218,6 +257,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AnalyzerName") .HasColumnType("TEXT"); + b.Property("AnalyzerResponse") + .HasColumnType("TEXT"); + b.Property("AnalyzerType") .HasColumnType("TEXT"); @@ -1152,6 +1194,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("MultiObjectiveCapable") + .HasColumnType("INTEGER"); + b.Property("PlannerInfoId") .HasColumnType("TEXT"); @@ -1522,6 +1567,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("TEXT"); + b.Property("PlanObjectives") + .HasColumnType("TEXT"); + b.Property("Resolved") .HasColumnType("INTEGER"); From e271a449ab88bdb521128e84605c4c194c9822cf Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 19 Aug 2026 16:00:57 -0400 Subject: [PATCH 17/19] Fixed a few issues with planners not reconnecting very gracefully. Likely going to do the same for planners, analyzers and devices. Also made the UI for showing the errors much cleaner with an expansion button. --- Ares.Core.Grpc/Services/PlannerService.cs | 5 +- Ares.Core/Planning/IPlannerService.cs | 6 ++ Ares.Core/Planning/ManualPlanner.cs | 3 + Ares.Core/Planning/PlannerServiceBase.cs | 2 + Ares.Core/Planning/RemotePlannerService.cs | 5 ++ .../PlannerAllocationEditorViewModel.cs | 62 ++++++++++--------- .../ViewModels/PlanningViewModel.cs | 28 ++++++--- .../Views/PlannerAllocationEditor.razor | 38 +++++++++--- UI/Features/CampaignEdit/Views/Planning.razor | 54 +++++++++++++--- .../Layout/PlannerSettingsContainer.razor | 46 +++++++++----- 10 files changed, 179 insertions(+), 70 deletions(-) diff --git a/Ares.Core.Grpc/Services/PlannerService.cs b/Ares.Core.Grpc/Services/PlannerService.cs index b8db951e..b0787ad8 100644 --- a/Ares.Core.Grpc/Services/PlannerService.cs +++ b/Ares.Core.Grpc/Services/PlannerService.cs @@ -88,15 +88,16 @@ public override async Task RemovePlanner(RemovePlannerRequest request, Se return new Empty(); } - public override Task GetState(StateRequest request, ServerCallContext? context) + public override async Task GetState(StateRequest request, ServerCallContext? context) { var response = new StateResponse(); var planner = _plannerRepo.GetPlannerById(request.Id) ?? throw new ItemNotFoundException(request.Id, typeof(IPlannerService), "Failed to get state as planned was not found."); + await planner.Refresh(); response.State = planner.PlannerServiceState; response.StateMessage = planner.StateMessage; - return Task.FromResult(response); + return response; } public override async Task GetInfo(PlannerInfoRequest request, ServerCallContext? context) diff --git a/Ares.Core/Planning/IPlannerService.cs b/Ares.Core/Planning/IPlannerService.cs index 771cf59f..5cc7cb55 100644 --- a/Ares.Core/Planning/IPlannerService.cs +++ b/Ares.Core/Planning/IPlannerService.cs @@ -40,6 +40,12 @@ public interface IPlannerService /// Task Init(); + /// + /// Refreshes the planners connection, calling this prior to reporting updated information about a planner is ideal + /// + /// + Task Refresh(); + /// /// Provides an observable for the /// diff --git a/Ares.Core/Planning/ManualPlanner.cs b/Ares.Core/Planning/ManualPlanner.cs index 921678e9..1dca2ccf 100644 --- a/Ares.Core/Planning/ManualPlanner.cs +++ b/Ares.Core/Planning/ManualPlanner.cs @@ -81,6 +81,9 @@ public async Task Reseed() await Seed(LatestManualPlannerSeed); } + public Task Refresh() + => Task.CompletedTask; + public void Reset() { _planResultsQueue.Clear(); diff --git a/Ares.Core/Planning/PlannerServiceBase.cs b/Ares.Core/Planning/PlannerServiceBase.cs index 5448569e..84b22cb8 100644 --- a/Ares.Core/Planning/PlannerServiceBase.cs +++ b/Ares.Core/Planning/PlannerServiceBase.cs @@ -65,6 +65,8 @@ public void UpdateSettings(AresStruct settings) public abstract Task GetCapabilities(CancellationToken cancellationToken); + public abstract Task Refresh(); + public virtual Task Init() { return Task.CompletedTask; diff --git a/Ares.Core/Planning/RemotePlannerService.cs b/Ares.Core/Planning/RemotePlannerService.cs index b5bc448f..c35db7ed 100644 --- a/Ares.Core/Planning/RemotePlannerService.cs +++ b/Ares.Core/Planning/RemotePlannerService.cs @@ -38,6 +38,11 @@ public override async Task Init() await VerifyDatamodelCompatability(); await UpdateState(); await UpdateCapabilities(); + } + + public override async Task Refresh() + { + await UpdateState(); } internal async Task UpdateInfo() diff --git a/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs index 38f6a40f..b5aad2f1 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs @@ -12,7 +12,7 @@ public partial class PlannerAllocationEditorViewModel : ReactiveObject { private readonly PlannerService _plannerClient; private readonly IUiNotificationService _notificationService; - private PlannerServiceInfo? _selectedAdapter; + private PlannerServiceInfo? _selectedService; public PlannerAllocationEditorViewModel(ParameterMetadata metadata, PlannerServiceInfo? plannerInfo, @@ -27,13 +27,26 @@ public PlannerAllocationEditorViewModel(ParameterMetadata metadata, _plannerClient = plannerClient; _notificationService = notificationService; + // Initialize to empty by default + PlannerOptions = Enumerable.Empty(); + if(plannerInfo is not null) { - SelectedService = plannerInfo; - SelectedPlannerOption = plannerInfo.Capabilities.AvailablePlanners.FirstOrDefault(p => p.PlannerName == metadata.PlannerName); + // Match against the fresh live adapter collection instead of using the stale deserialized snapshot + var liveService = plannerArray.FirstOrDefault(p => p.UniqueId == plannerInfo.UniqueId || p.Name == plannerInfo.Name); + + SelectedService = liveService ?? plannerInfo; + + // Restore specific planner option selection if specified in metadata + if(!string.IsNullOrEmpty(metadata.PlannerName)) + { + var matchedOption = PlannerOptions.FirstOrDefault(p => p.PlannerName == metadata.PlannerName); + if(matchedOption != null) + { + SelectedPlannerOption = matchedOption; + } + } } - - PlannerOptions = Enumerable.Empty(); } public PlannerAllocation? Save() @@ -61,22 +74,20 @@ public async Task UpdatePlannerOptions() var updatedInfo = await _plannerClient.GetInfo(new PlannerInfoRequest { PlannerId = SelectedService.UniqueId }, null); - if(updatedInfo.Info.Name != string.Empty) + if(updatedInfo?.Info is not null && !string.IsNullOrEmpty(updatedInfo.Info.Name)) { - PlannerOptions = updatedInfo.Info.Capabilities.AvailablePlanners; - - //Not all adapters will have multiple options, if not auto assign the value - if(PlannerOptions.Count() == 1) - SelectedPlannerOption = SelectedService.Capabilities.AvailablePlanners.First(); + // Update the SelectedService reference with the fresh gRPC response + SelectedService = updatedInfo.Info; } - else { - var notification = new UiNotificationMessage(); - notification.Summary = "Assigned Planner Unavailable!"; - notification.Detail = $"This template uses a planner that ARES no longer has a connection with. The template won't be usable until this is resolved."; - notification.Severity = UiNotificationSeverity.Warning; - notification.CloseOnClick = true; + var notification = new UiNotificationMessage + { + Summary = "Assigned Planner Unavailable!", + Detail = "This template uses a planner that ARES no longer has a connection with. The template won't be usable until this is resolved.", + Severity = UiNotificationSeverity.Warning, + CloseOnClick = true + }; _notificationService.Notify(notification); } @@ -84,18 +95,13 @@ public async Task UpdatePlannerOptions() public PlannerServiceInfo? SelectedService { - get => _selectedAdapter; - + get => _selectedService; set { - if(value is null || _selectedAdapter == value) - return; - - if(_selectedAdapter is not null) - SelectedPlannerOption = _selectedAdapter.Capabilities.AvailablePlanners.FirstOrDefault(); + this.RaiseAndSetIfChanged(ref _selectedService, value); - _selectedAdapter = value; - _ = UpdatePlannerOptions(); + PlannerOptions = _selectedService?.Capabilities?.AvailablePlanners?.ToList() ?? new List(); + SelectedPlannerOption = PlannerOptions.FirstOrDefault(); } } @@ -106,6 +112,4 @@ public PlannerServiceInfo? SelectedService public partial IEnumerable PlannerOptions { get; set; } [Reactive] public partial Planner? SelectedPlannerOption { get; set; } -} - - +} \ No newline at end of file diff --git a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index 17c53ead..01fa0d4b 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -6,10 +6,11 @@ using UI.Application.Notifications; using Ares.Core.Analyzing; using Ares.Datamodel; +using ReactiveUI.SourceGenerators; namespace UI.Features.CampaignEdit.ViewModels; -public class PlanningViewModel : ReactiveObject +public partial class PlanningViewModel : ReactiveObject { private readonly CampaignTemplate _template; private readonly PlannerService _client; @@ -28,7 +29,6 @@ public PlanningViewModel(CampaignTemplate template, _selectedAnalyzer = selectedAnalyzer; PlannerAdapters = new ReadOnlyCollection(plannerAdapters.ToList()); PlannerAllocationEditors = template.PlannableParameters.Select(metadata => new PlannerAllocationEditorViewModel(metadata, template.PlannerAllocations.FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, client, notificationService)).ToArray(); - AnalyzerName = _selectedAnalyzer?.Name ?? "NONE"; } public async Task UpdateAnalyzerObjectives() @@ -39,17 +39,29 @@ public async Task UpdateAnalyzerObjectives() SelectedObjectives = AvailableObjectives?.Fields.Where(obj => _template.ExperimentTemplate.PlanObjectives.Contains(obj.Key)).ToList() ?? []; } - public IEnumerable PlannerAllocationEditors { get; private set; } = []; + public void RefreshMultiObjectiveCompatability() + => MultiObjectiveAllowed = PlannerAllocationEditors.All(editor => editor.SelectedService?.Capabilities.MultiObjectiveCapable ?? false); + - public IEnumerable PlannerAdapters { get; } = []; + [Reactive] + public partial IEnumerable PlannerAllocationEditors { get; private set; } = []; - public AresStructSchema? AvailableObjectives { get; private set; } + [Reactive] + public partial IEnumerable PlannerAdapters { get; private set; } = []; - public List> SelectedObjectives { get; private set; } = []; + [Reactive] + public partial AresStructSchema? AvailableObjectives { get; private set; } - public List IncludedObjectives { get; private set; } = []; + [Reactive] + public partial List> SelectedObjectives { get; private set; } = []; - public string AnalyzerName { get; private set; } = "NONE"; + [Reactive] + public partial List IncludedObjectives { get; private set; } = []; + + public string AnalyzerName => _selectedAnalyzer?.Name ?? "NONE"; + + [Reactive] + public partial bool MultiObjectiveAllowed { get; private set; } = false; public void Save() { diff --git a/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor b/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor index 2bc806a0..7dbd74fc 100644 --- a/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor +++ b/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor @@ -10,7 +10,13 @@ - + + @@ -21,7 +27,12 @@ @if(ViewModel!.PlannerOptions.Count() > 1) { - + @@ -43,14 +54,25 @@
@code { + + [Parameter] + public EventCallback OnPlannerChanged { get; set; } + + private async Task OnSelectionChanged() + { + if(OnPlannerChanged.HasDelegate) + { + await OnPlannerChanged.InvokeAsync(); + } + } + private async Task ShowPlannerOptionTip() { await DialogService.OpenAsync("Planner Selection", ds => - @
- In ARES, some planner services report the capability of using multiple types of planners. - In this scenario, a seperate dropdown allows you to choose what planning method to use from the services advertised options. -
- , new DialogOptions() { CloseDialogOnOverlayClick = true }); + @
+ In ARES, some planner services report the capability of using multiple types of planners. + In this scenario, a seperate dropdown allows you to choose what planning method to use from the services advertised options. +
+ , new DialogOptions() { CloseDialogOnOverlayClick = true }); } - } diff --git a/UI/Features/CampaignEdit/Views/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor index 841ad5e4..c4e48e6f 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -2,13 +2,11 @@ @using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase -
@if(ViewModel is null) { } - else {
@@ -22,13 +20,21 @@ { @foreach(var editor in ViewModel.PlannerAllocationEditors) { - + } }

Analyzer Objectives

+ + @if(ViewModel is not null && !ViewModel.MultiObjectiveAllowed) + { + + The currently selected planner does not support multi-objective optimization. Selecting an objective will replace your current selection. + + } + @if(ViewModel?.AvailableObjectives is not null) { @foreach(var item in ViewModel.AvailableObjectives.Fields) @@ -59,14 +65,12 @@
} } - else { if(ViewModel!.AnalyzerName == "NONE") {

Select an Analyzer to See Objectives

} - else {

Your Selected Analyer (@ViewModel!.AnalyzerName) Doesn't Report Any Objective Outputs

@@ -78,23 +82,55 @@ }
- @code { protected override async Task OnInitializedAsync() { if(ViewModel is not null) + { await ViewModel!.UpdateAnalyzerObjectives(); + ViewModel!.RefreshMultiObjectiveCompatability(); + } + await base.OnInitializedAsync(); } private void ToggleSelection(KeyValuePair item) { - if(ViewModel!.SelectedObjectives.Contains(item)) - ViewModel!.SelectedObjectives.Remove(item); + if(ViewModel is null) + return; + if(ViewModel.SelectedObjectives.Contains(item)) + { + ViewModel.SelectedObjectives.Remove(item); + } else - ViewModel!.SelectedObjectives.Add(item); + { + if(!ViewModel.MultiObjectiveAllowed) + { + ViewModel.SelectedObjectives.Clear(); + } + + ViewModel.SelectedObjectives.Add(item); + } + + StateHasChanged(); + } + + private async Task HandlePlannerAllocationChanged() + { + if(ViewModel is null) + return; + + ViewModel!.RefreshMultiObjectiveCompatability(); + + // If switching to a single-objective planner while multiple are selected, trim down to the first selection + if(!ViewModel.MultiObjectiveAllowed && ViewModel.SelectedObjectives.Count > 1) + { + var primarySelection = ViewModel.SelectedObjectives.First(); + ViewModel.SelectedObjectives.Clear(); + ViewModel.SelectedObjectives.Add(primarySelection); + } StateHasChanged(); } diff --git a/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor index 3995e9ce..c66353c4 100644 --- a/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor +++ b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor @@ -4,8 +4,8 @@
@Name
@if(PlannerStatusGetter is not null) { -
- @if(_plannerStatus is not null) @@ -14,22 +14,34 @@
@_plannerStatus.State.ToString()
-
- @if(_plannerStatus.State == State.Active && _plannerStatus.StateMessage == string.Empty) + +
+ @if(_plannerStatus.State == State.Active) { -
- Planner is Active! -
+ Planner is Active! } - else { -
- @_plannerStatus.StateMessage -
+ Planner is Inactive + + } -
+ + @if(_isMessageExpanded && !string.IsNullOrWhiteSpace(_plannerStatus.StateMessage)) + { +
+ @_plannerStatus.StateMessage +
+ }
} else @@ -72,6 +84,9 @@ [Parameter] public Func>? PlannerStatusGetter { get; set; } + private StateResponse? _plannerStatus; + private bool _isMessageExpanded; + protected override async Task OnInitializedAsync() { if(PlannerStatusGetter is not null) @@ -81,7 +96,10 @@ } } - private StateResponse? _plannerStatus; + private void ToggleMessage() + { + _isMessageExpanded = !_isMessageExpanded; + } private Task EditClicked() { @@ -116,4 +134,4 @@ await ActivateCallback(); await UpdatePlannerStatus(); } -} +} \ No newline at end of file From 39bc6f2807e61eb7367f6e0dd0f2a460ae557a4f Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 20 Aug 2026 11:52:03 -0400 Subject: [PATCH 18/19] Updated datamodel version, fixed demo projects --- Ares.Core/Ares.Core.csproj | 2 +- AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- .../Services/DemoAnalyzerService.cs | 18 +++++++++++------- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- .../Services/DemoPlannerService.cs | 13 ++++++++----- UI/UI.csproj | 2 +- 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 97791e18..f08b7154 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,6 +26,7 @@ + @@ -40,7 +41,6 @@ - diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index 4e8bf109..adccc18e 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 54e84ca4..385765e1 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteAnalyzer/Services/DemoAnalyzerService.cs b/DemoRemoteAnalyzer/Services/DemoAnalyzerService.cs index 997320a1..8d9b3119 100644 --- a/DemoRemoteAnalyzer/Services/DemoAnalyzerService.cs +++ b/DemoRemoteAnalyzer/Services/DemoAnalyzerService.cs @@ -2,6 +2,7 @@ using Ares.Datamodel.Analyzing; using Ares.Datamodel.Analyzing.Remote; using Ares.Datamodel.Connection; +using Ares.Datamodel.Extensions; using Google.Protobuf.WellKnownTypes; using Grpc.Core; @@ -17,18 +18,19 @@ public override Task GetState(Empty request, ServerCallContext co return Task.FromResult(new StateResponse { State = State.Active, StateMessage = "The Demo Analyzer is Active!" }); } - public override Task Analyze(AnalysisRequest request, ServerCallContext context) + public override Task Analyze(AnalysisRequest request, ServerCallContext context) { Console.WriteLine("Analysis requested"); var numInput = request.Inputs.Fields[DemoDataTypes.InputNumber.Key]; var input = numInput.NumberValue; Console.WriteLine($"Analysis input: {input}"); - var analysis = new Analysis + var analysis = new AnalysisResponse { - Result = (float)input, AnalysisOutcome = Outcome.Success, }; + analysis.Objectives.Add(new Objective() { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(input) }); + var numOperand = request.Inputs.Fields.GetValueOrDefault(DemoDataTypes.Operand.Key); if(numOperand is null) { @@ -40,13 +42,15 @@ public override Task Analyze(AnalysisRequest request, ServerCallContex var operation = request.Settings.Fields[DemoDataTypes.Operation.Key]; var operationValue = operation.StringValue; - analysis.Result = operationValue switch + var resultValue = operationValue switch { - "Multiply" => (float)(analysis.Result * operand), - "Divide" => (float)(analysis.Result / operand), - _ => 0,// you broke it :( + "Multiply" => AresValueHelper.CreateNumber(analysis.Objectives.First().ObjectiveValue.NumberValue * operand), + "Divide" => AresValueHelper.CreateNumber(analysis.Objectives.First().ObjectiveValue.NumberValue / operand), + _ => AresValueHelper.CreateNumber(0),// you broke it :( }; + analysis.Objectives.Add(new Objective() { ObjectiveValue = resultValue, ObjectiveName = "Result"}); + return Task.FromResult(analysis); } diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index 94be5fbf..f9d54735 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 54e84ca4..385765e1 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/Services/DemoPlannerService.cs b/DemoRemotePlanner/Services/DemoPlannerService.cs index 1b8a5ea6..fbe94b0d 100644 --- a/DemoRemotePlanner/Services/DemoPlannerService.cs +++ b/DemoRemotePlanner/Services/DemoPlannerService.cs @@ -38,7 +38,8 @@ public override async Task Plan(PlanningRequest request, Serve Console.WriteLine("Planning Requested!"); var inputs = request.PlanningParameters; Console.WriteLine($"Received a total of {inputs.Count} parameters to plan for."); - var response = new PlanningResponse(); + var response = new PlanningResponse(); + var newPlan = new Plan(); foreach(var parameter in inputs) { @@ -47,25 +48,27 @@ public override async Task Plan(PlanningRequest request, Serve case "Random Planner": { var plannedParam = await RandomPlanner(parameter); - response.PlannedParameters.Add(plannedParam); + newPlan.PlannedParameters.Add(plannedParam); break; } case "Gradual Planner": { var gradualPlannedParam = await GradualPlanner(parameter); - response.PlannedParameters.Add(gradualPlannedParam); + newPlan.PlannedParameters.Add(gradualPlannedParam); break; } default: { Console.WriteLine("Unrecognized Planned Requested! Defaulting to random planner..."); var plannedParam = await RandomPlanner(parameter); - response.PlannedParameters.Add(plannedParam); + newPlan.PlannedParameters.Add(plannedParam); break; } } } - + + newPlan.PlanningOutcome = Outcome.Success; + response.Plans.Add(newPlan); return response; } diff --git a/UI/UI.csproj b/UI/UI.csproj index 775b1e78..b0823978 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -37,6 +37,7 @@ + @@ -79,7 +80,6 @@ - From 0a7cc1eb88d7030338611e0a29140e0424cb61e2 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 20 Aug 2026 14:07:50 -0400 Subject: [PATCH 19/19] Incremented the minimum datamodel version requirement --- .../Execution/VersionChecking/DatamodelVersionValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ares.Core/Execution/VersionChecking/DatamodelVersionValidator.cs b/Ares.Core/Execution/VersionChecking/DatamodelVersionValidator.cs index 8297e122..5f101fe1 100644 --- a/Ares.Core/Execution/VersionChecking/DatamodelVersionValidator.cs +++ b/Ares.Core/Execution/VersionChecking/DatamodelVersionValidator.cs @@ -86,5 +86,5 @@ public static string NormalizePep440ToSemVer(string rawVersion) return Pep440ToSemVerRegex.Replace(rawVersion.Trim(), "$1-$2"); } - public static SemanticVersion MinimumRequiredDatamodelVersion { get; set; } = SemanticVersion.Parse("0.33.0"); + public static SemanticVersion MinimumRequiredDatamodelVersion { get; set; } = SemanticVersion.Parse("0.36.0"); }