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.Tests/AnalyzerManagerTests.cs b/Ares.Core.Tests/AnalyzerManagerTests.cs index 05000409..1299d799 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(); } @@ -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 28d58301..a472b710 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); } @@ -38,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.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs b/Ares.Core.Tests/DataManagement/DataMappers/CampaignDatasetGeneratorTests.cs index e2c28f1f..2a0ae0de 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"); @@ -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,42 +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)); - transaction.PlanningRequest.AnalysisResults.AddRange([1.5, 2.5]); - transaction.PlanningResponse.PlanningOutcome = Outcome.Warning; - transaction.PlanningResponse.ErrorString = "planner warning"; + ("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 }; + 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)); } } @@ -583,9 +637,10 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() DateTime.UnixEpoch.AddSeconds(2), DateTime.UnixEpoch.AddSeconds(4), ("Measurement", nestedInput)); - transaction.AnalysisResponse = new Analysis + + transaction.AnalyzerResponse = new AnalysisResponse { - Result = 9.5f, + Objectives = { new Objective() { ObjectiveName = "Result", ObjectiveValue = AresValueHelper.CreateNumber(9.5) } }, AnalysisOutcome = Outcome.Success, ErrorString = "analysis note" }; @@ -604,14 +659,14 @@ public async Task GenerateAsync_CreatesAnalyzerTransactionRows() "Time Request Sent", "Time Response Received", "Duration Seconds", - "Result", + "Objective.Result", "Outcome", "Error", "Input.Measurement.Mass" ])); 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)); } @@ -741,7 +796,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 +807,12 @@ private static ExperimentExecutionSummary CreateExperiment( }; if(analysisResult is not null) - overview.AnalysisOverview = new AnalysisOverview { Result = analysisResult.Value }; + { + overview.AnalysisOverview = new AnalysisOverview(); + overview.AnalysisOverview.Objectives.AddRange(analysisResult); + + } + foreach(var field in resultFields ?? []) { @@ -906,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; } @@ -940,7 +1003,7 @@ private static AnalyzerTransaction CreateAnalyzerTransaction( ExperimentId = experimentId } }, - AnalysisResponse = new Analysis() + AnalyzerResponse = new AnalysisResponse() }; foreach(var input in inputs) diff --git a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs index 80336507..dcc8547f 100644 --- a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs +++ b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs @@ -56,8 +56,8 @@ public void SetUp() .Setup(helper => helper.TryResolveParameters( It.IsAny>(), It.IsAny(), - It.IsAny>(), - It.IsAny>(), + It.IsAny(), + It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny>(), @@ -220,8 +220,8 @@ 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>(), It.IsAny(), It.IsAny>(), 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 11971e6b..e9341170 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 { @@ -60,34 +66,48 @@ public async Task Analyze(ExperimentTemplate template, ExperimentExecu var analysis = await analyzer.Analyze(analysisRequest, cancellationToken); transaction.TimeResponseReceived = DateTime.UtcNow.ToTimestamp(); - transaction.AnalysisResponse = analysis; + transaction.AnalyzerResponse = analysis; 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.Objectives.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..23347c8c 100644 --- a/Ares.Core/Analyzing/AnalyzerBase.cs +++ b/Ares.Core/Analyzing/AnalyzerBase.cs @@ -69,12 +69,14 @@ 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); + 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 85d27afb..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. /// @@ -100,7 +107,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 +115,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..529cfef9 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); } @@ -46,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 4a8894fe..0764acb2 100644 --- a/Ares.Core/Analyzing/RemoteAnalyzer.cs +++ b/Ares.Core/Analyzing/RemoteAnalyzer.cs @@ -34,7 +34,7 @@ public RemoteAnalyzer(string name, Uri address, IDatamodelVersionValidator datam 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); @@ -49,7 +49,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); @@ -57,6 +57,12 @@ public override Task Analyze(AnalysisRequest request, AresStruct setti 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/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index bb5f0548..f08b7154 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/Ares.Core/CoreDatabaseContext.cs b/Ares.Core/CoreDatabaseContext.cs index 5cf123e0..d02fdfb0 100644 --- a/Ares.Core/CoreDatabaseContext.cs +++ b/Ares.Core/CoreDatabaseContext.cs @@ -70,6 +70,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 20ab1726..3d6367d5 100644 --- a/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs +++ b/Ares.Core/DataManagement/DataMappers/CampaignDatasetGenerator.cs @@ -1,14 +1,10 @@ -using Ares.Core.Execution.Extensions; 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 YamlDotNet.Serialization.NodeDeserializers; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Ares.Core.DataManagement.DataMappers; @@ -24,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"; @@ -37,8 +33,8 @@ 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 ResultColumnName = "Result"; + private const string ObjectiveStatusColumnName = "Objective Status"; + private const string ResultColumnName = "Objective.Result"; private const string InputColumnPrefix = "Input."; private const string OutputColumnPrefix = "Output."; private const string OutputColumnName = "Output"; @@ -66,16 +62,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); @@ -129,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; } @@ -153,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) ]; } @@ -233,6 +239,26 @@ private static IEnumerable CreateExperimentDynamicColumns(IEnume { cancellationToken.ThrowIfCancellationRequested(); + if(experiment.ExperimentOverview?.AnalysisOverview is not null) + { + if(experiment.ExperimentOverview.AnalysisOverview.Objectives.Any()) + { + 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) ?? Enumerable.Empty>(); foreach(var field in resultFields) @@ -316,11 +342,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); + } } } @@ -434,7 +464,21 @@ 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); + { + 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 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); + AddFlattenedValue(data, $"Objective.Result", aresValueResult, cancellationToken); + } + + } foreach(var field in experiment.ExperimentOverview?.Result?.Fields ?? []) { @@ -499,24 +543,70 @@ 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, Plan? 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); 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, OutcomeColumnName, transaction.PlanningResponse.PlanningOutcome.ToString()); - AddString(data, ErrorColumnName, transaction.PlanningResponse.ErrorString); + 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.{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 transaction.PlanningResponse.PlannedParameters) + foreach(var parameter in plan.PlannedParameters) { cancellationToken.ThrowIfCancellationRequested(); if(parameter.ParameterValue is not null) @@ -524,9 +614,6 @@ private static AresDataRow CreatePlannerTransactionRow(PlannerRecord record, Can } } - if(transaction.PlanningRequest?.AnalysisResults.Count > 0) - data.Fields[AnalysisResultsColumnName] = AresValueHelper.CreateList(transaction.PlanningRequest.AnalysisResults.Select(AresValueHelper.CreateNumber)); - return new AresDataRow { Data = data }; } @@ -544,11 +631,22 @@ private static AresDataRow CreateAnalyzerTransactionRow(AnalyzerRecord record, C if(transaction.AnalysisResponse is not null) { - data.Fields[ResultColumnName] = AresValueHelper.CreateNumber(transaction.AnalysisResponse.Result); + 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.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 ?? []) { cancellationToken.ThrowIfCancellationRequested(); 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/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/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"); + } +} diff --git a/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Analyzer/AnalyzerTransactionEntityConfiguration.cs index a19a5988..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.AnalysisResponse).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/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/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs b/Ares.Core/EntityConfigurations/Helpers/AnalysisInfoSerializeHelper.cs index 215384c3..a0526886 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,6 +29,11 @@ public static PropertyBuilder HasAnalysisRequest(this PropertyB .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); } + /// + /// 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(); @@ -37,4 +43,24 @@ public static PropertyBuilder HasAnalysis(this PropertyBuilder JsonSerializer.Deserialize(v, settings) ?? new Analysis()) .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); } + + public static PropertyBuilder HasAnalyzerResponse(this PropertyBuilder value) + { + var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); + + return value.HasConversion( + v => JsonSerializer.Serialize(v, settings), + 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 fe8cf61e..ac98acbf 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -111,7 +111,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(); @@ -193,7 +193,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); @@ -213,9 +213,9 @@ private async Task NotifyCampaignStart() return (true, startupSummary); } - private async Task ExecuteExperimentLoop(string campaignPath, - List analyses, - List experimentSummaries, + private async Task ExecuteExperimentLoop(string campaignPath, + List analyses, + List experimentSummaries, ExecutionControlToken token, ExperimentExecutionSummary startupSummary) { @@ -298,7 +298,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 +402,10 @@ private async Task ExecuteCurrentExperiment(string currentExper } } - 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 +414,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 +423,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,8 +520,6 @@ private async Task HandleError(CommandExecutionSummary cmdSumma return ExperimentPhase.Failed; } } - - private void UpdatePlanStatus(CommandStatusCode failCode) { if(failCode == CommandStatusCode.OutOfRange || failCode == CommandStatusCode.ParametersUnachievable) @@ -527,9 +529,9 @@ private void UpdatePlanStatus(CommandStatusCode failCode) _latestPlanStatusCodes.Add(PlanStatusCode.PlanFailed); } - private async Task PlanExperiment(List analyses, - ExperimentTemplate currentExperimentTemplate, - List experimentSummaries, + private async Task PlanExperiment(List analyses, + ExperimentTemplate currentExperimentTemplate, + List experimentSummaries, ExecutionControlToken token) { Status.PlannerState = PlannerState.PlanningInProgress; @@ -549,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, @@ -568,7 +570,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 @@ -593,14 +595,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.Result == float.NaN) - { - 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); - return (false, false); - } - if(analysis.AnalysisOutcome == Outcome.Failure) { Status.AnalysisState = AnalysisState.AnalysisError; @@ -623,11 +617,20 @@ 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); _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)) @@ -639,7 +642,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); @@ -739,8 +742,8 @@ private static bool IsAwaitingResponse(ExperimentExecutionStatus status) .Any(step => step.CommandExecutionStatuses .Any(cmd => cmd.State == ExecutionState.AwaitingUser)); - private async Task GenerateExperimentExecutor(ExperimentTemplate template, - IEnumerable analyses, + private async Task GenerateExperimentExecutor(ExperimentTemplate template, + IEnumerable analyses, IEnumerable previousExperiments, CancellationToken cancellationToken) { @@ -769,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/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/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"); } 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/IPlanningHelper.cs b/Ares.Core/Planning/IPlanningHelper.cs index 99ecec7d..b0f8ac6e 100644 --- a/Ares.Core/Planning/IPlanningHelper.cs +++ b/Ares.Core/Planning/IPlanningHelper.cs @@ -20,8 +20,8 @@ public interface IPlanningHelper /// True if planning succeeded, false otherwise Task TryResolveParameters(IEnumerable plannerAllocations, RequestMetadata metadata, - IEnumerable parameters, - IEnumerable seedAnalyses, + ExperimentTemplate currentTemplate, + IEnumerable seedAnalyses, IEnumerable seedExperiments, int batchSize, List codes, diff --git a/Ares.Core/Planning/ManualPlanner.cs b/Ares.Core/Planning/ManualPlanner.cs index b3b117fe..1dca2ccf 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); } } @@ -76,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/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 4070d5b1..243c23b0 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -34,14 +34,15 @@ public PlanningHelper(IPlannerServiceRepo plannerManager, } public async Task TryResolveParameters(IEnumerable plannerAllocations, - RequestMetadata metadata, - IEnumerable parameters, - IEnumerable seedAnalyses, - IEnumerable seedExperiments, - int batchSize, - List statusCodes, - CancellationToken cancellationToken) + RequestMetadata metadata, + ExperimentTemplate currentTemplate, + IEnumerable seedAnalyses, + IEnumerable seedExperiments, + int batchSize, + List codes, + CancellationToken cancellationToken) { + var parameters = currentTemplate.GetAllPlannedParameters(); var parameterArray = parameters.ToArray(); var plannerToMetadataMaps = MapParameterMetadataToPlanners(plannerAllocations); @@ -63,7 +64,7 @@ public async Task TryResolveParameters(IEnumerable plan return true; } - return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, statusCodes, metadata, planQueue, parameterArray, batchSize, cancellationToken); + return await RequestNewPlans(planner, grouping, seedExperiments, seedAnalysesArr, codes, metadata, planQueue, parameterArray, batchSize, cancellationToken); }); var results = await Task.WhenAll(planningTasks); @@ -86,7 +87,7 @@ public async Task TryResolveParameters(IEnumerable plan private async Task RequestNewPlans(IPlannerService planner, IGrouping grouping, IEnumerable seedExperiments, - Analysis[] seedAnalysesArr, + AnalysisResponse[] seedAnalysesArr, List statusCodes, RequestMetadata metadata, ConcurrentQueue planQueue, @@ -130,10 +131,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; } } @@ -159,15 +159,35 @@ private async Task RequestNewPlans(IPlannerService planner, /// An ARES private PlanningRequest CreatePlanningRequest(ParameterMetadata[] plannableParameters, IEnumerable seedExperiments, - Analysis[] seedAnalysesArr, + AnalysisResponse[] seedAnalysesArr, List statusCodes, int batchSize, RequestMetadata metadata) { //Create the plan request. Store it in the transaction. + var relevantExperiments = seedExperiments.ToList(); var planRequest = new PlanningRequest(); + planRequest.PlanningParameters.AddRange(plannableParameters.Select(parameter => ConvertToPlanningParameter(parameter, seedExperiments))); - planRequest.AnalysisResults.AddRange(seedAnalysesArr.Select(a => (double)a.Result)); + for(int i = 0; i < seedAnalysesArr.Length; i++) + { + var currentAnalysis = seedAnalysesArr.ElementAtOrDefault(i); + 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; + + //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); + planRequest.AnalysisData.Add(analysisData); + } planRequest.PreviousPlanStatusCodes.AddRange(statusCodes); planRequest.Metadata = metadata; planRequest.BatchSize = batchSize; @@ -197,59 +217,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 /// @@ -315,6 +282,27 @@ private static PlanningParameter ConvertToPlanningParameter(ParameterMetadata me return parameter; } + private AnalysisData CreateAnalysisData(AnalysisResponse analysis, ExperimentOverview experiment) + { + var newData = new AnalysisData(); + + // If no objectives were specified, include all of them. + if(experiment.Template.PlanObjectives.Count == 0) + { + 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; + } + /// /// Logs the planner transaction to the database. /// 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/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/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/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 94a25908..1f8b1ebb 100644 --- a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs @@ -44,6 +44,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); + b.Property("Objectives") + .HasColumnType("jsonb"); + b.Property("Result") .HasColumnType("double precision"); @@ -85,6 +88,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") @@ -104,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"); @@ -226,6 +262,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AnalyzerName") .HasColumnType("text"); + b.Property("AnalyzerResponse") + .HasColumnType("jsonb"); + b.Property("AnalyzerType") .HasColumnType("text"); @@ -1160,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"); @@ -1530,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 1744d62a..11e62a2b 100644 --- a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs @@ -44,6 +44,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); + b.Property("Objectives") + .HasColumnType("nvarchar(max)"); + b.Property("Result") .HasColumnType("float"); @@ -86,6 +89,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") @@ -105,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)"); @@ -227,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)"); @@ -1167,6 +1206,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); + b.Property("MultiObjectiveCapable") + .HasColumnType("bit"); + b.Property("PlannerInfoId") .HasColumnType("uniqueidentifier"); @@ -1540,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 243d6fef..42c2c8c1 100644 --- a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs @@ -39,6 +39,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("Objectives") + .HasColumnType("TEXT"); + b.Property("Result") .HasColumnType("REAL"); @@ -80,6 +83,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") @@ -99,6 +132,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("ObjectiveOutputSchema") + .HasColumnType("TEXT"); + b.Property("SettingsSchema") .HasColumnType("TEXT"); @@ -221,6 +257,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AnalyzerName") .HasColumnType("TEXT"); + b.Property("AnalyzerResponse") + .HasColumnType("TEXT"); + b.Property("AnalyzerType") .HasColumnType("TEXT"); @@ -1155,6 +1194,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasDefaultValueSql("DATETIME('now')"); + b.Property("MultiObjectiveCapable") + .HasColumnType("INTEGER"); + b.Property("PlannerInfoId") .HasColumnType("TEXT"); @@ -1525,6 +1567,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("TEXT"); + b.Property("PlanObjectives") + .HasColumnType("TEXT"); + b.Property("Resolved") .HasColumnType("INTEGER"); 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/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(); 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/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/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/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 60c23c75..01fa0d4b 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -1,34 +1,67 @@ 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; +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; 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 void RefreshMultiObjectiveCompatability() + => MultiObjectiveAllowed = PlannerAllocationEditors.All(editor => editor.SelectedService?.Capabilities.MultiObjectiveCapable ?? false); + + + [Reactive] + public partial IEnumerable PlannerAllocationEditors { get; private set; } = []; + + [Reactive] + public partial IEnumerable PlannerAdapters { get; private set; } = []; - public IEnumerable PlannerAdapters { get; } + [Reactive] + public partial AresStructSchema? AvailableObjectives { get; private set; } + + [Reactive] + public partial List> SelectedObjectives { get; private set; } = []; + + [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() { @@ -40,6 +73,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/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 af3f3cce..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/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 46213ad3..c4e48e6f 100644 --- a/UI/Features/CampaignEdit/Views/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -1,16 +1,137 @@ -@using UI.Features.CampaignEdit.ViewModels +@using Ares.Datamodel +@using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase -
-

Planning

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

Planner Assignment

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

Loading allocations...

+ } + else + { + @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) + { + 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.

+ } + } +
+
+ } +
@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 is null) + return; + + if(ViewModel.SelectedObjectives.Contains(item)) + { + ViewModel.SelectedObjectives.Remove(item); + } + else + { + 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(); + } } \ 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; + } +} 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; + } } diff --git a/UI/Features/Execution/Execution.razor b/UI/Features/Execution/Execution.razor index f6d53649..bdb40c62 100644 --- a/UI/Features/Execution/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -494,15 +494,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 856815f1..a550d04e 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -401,11 +401,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) { @@ -422,13 +418,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; } @@ -463,11 +453,22 @@ private void ProcessTransactionParameterData(PlannedParameter field, PlannerTran public void OnAnalyzerTransactionReceived(AnalyzerTransaction transaction, int currentTurn) { - AnalyzerMetrics.Add(new ChartMetricPoint + foreach(var objective in transaction.AnalyzerResponse.Objectives) { - ExecutionIndex = currentTurn, - RawValue = transaction.AnalysisResponse.Result - }); + var found = objective.ObjectiveValue.TryGetNumericValue(out var numericValue); + if(!found) + return; + + if(!AnalyzerMetrics.ContainsKey(objective.ObjectiveName)) + AnalyzerMetrics[objective.ObjectiveName] = new List(); + + AnalyzerMetrics[objective.ObjectiveName].Add(new ChartMetricPoint + { + ExecutionIndex = currentTurn, + RawValue = numericValue, + PlotValue = numericValue + }); + } } public bool TryGetChartableValue(AresValue aresValue, out double result) @@ -813,7 +814,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] 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 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); diff --git a/UI/UI.csproj b/UI/UI.csproj index 7fa99e00..b0823978 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -37,7 +37,7 @@ - +