From 91188b9737b22ad3943dd0aac8bd213e2c1aa755 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 28 Apr 2026 13:17:31 -0400 Subject: [PATCH 01/25] Added the new error states, as well as an enum to define the behaviors available for the user to select from. --- Ares.Core/Ares.Core.csproj | 2 +- .../Drivers/Loading/DeviceDriverLoader.cs | 3 - AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Components/Layouts/SettingsLayout.razor | 4 + UI/Features/Settings/SystemSettingsView.razor | 129 ++++++++++++++++++ .../Settings/SystemSettingsViewModel.cs | 5 + UI/UI.csproj | 2 +- 10 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 UI/Features/Settings/SystemSettingsView.razor create mode 100644 UI/Features/Settings/SystemSettingsViewModel.cs diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 5b27001c..635dea8b 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -23,7 +23,7 @@ - + diff --git a/Ares.Core/Device/Plugins/Drivers/Loading/DeviceDriverLoader.cs b/Ares.Core/Device/Plugins/Drivers/Loading/DeviceDriverLoader.cs index b1e76b23..db7e795f 100644 --- a/Ares.Core/Device/Plugins/Drivers/Loading/DeviceDriverLoader.cs +++ b/Ares.Core/Device/Plugins/Drivers/Loading/DeviceDriverLoader.cs @@ -1,4 +1,3 @@ -using Ares.Core.Device.Plugins.Drivers; using Ares.Core.Device.Plugins.Manifest; using Ares.Core.Device.Repos; using Ares.Device; @@ -32,7 +31,6 @@ public async Task LoadModulesAsync(string directoryPath, CancellationToken ct = if(!Directory.Exists(directoryPath)) return; - // 1. Process and extract all .ares files var aresFiles = Directory.GetFiles(directoryPath, "*.ares"); foreach(var file in aresFiles) { @@ -110,7 +108,6 @@ public async Task LoadFromDirectoryAsync(string moduleDirectory, C viewModelType = assembly.GetType(manifest.ViewModelTypeName); } - //Attempt to Manually Find the View Model else { viewModelType = assembly.GetTypes().FirstOrDefault(t => diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index a027b8a1..63cdadea 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -10,7 +10,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 73ba639e..5bc1190a 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index b891fb6b..ec6856dc 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 73ba639e..5bc1190a 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Components/Layouts/SettingsLayout.razor b/UI/Components/Layouts/SettingsLayout.razor index a981f1a2..3aaaa186 100644 --- a/UI/Components/Layouts/SettingsLayout.razor +++ b/UI/Components/Layouts/SettingsLayout.razor @@ -14,6 +14,10 @@ + +
@Body diff --git a/UI/Features/Settings/SystemSettingsView.razor b/UI/Features/Settings/SystemSettingsView.razor new file mode 100644 index 00000000..aff40895 --- /dev/null +++ b/UI/Features/Settings/SystemSettingsView.razor @@ -0,0 +1,129 @@ +@page "/settings/system" +@using Ares.Datamodel +@using Radzen +@using Radzen.Blazor +@inherits LayoutComponentBase +@layout SettingsLayout + +
+ + +
+@* + Application Settings + *@ +
+ + + + + + + + + + + + +
+ + Launch on system startup +
+
+
+
+
+
+ + + + + + + @foreach(var error in Enum.GetValues()) + { + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ High Performance Mode + +
+ + Enabling this allows the system to prioritize modular device drivers and decentralized command processing. + +
+
+
+
+
+ + + + + Connect to the external marketplace to download new device implementations. + + + + +
+
+ +
+ + +
+ + + +@code { + // State logic + private string appName = "ARES System"; + private bool autoStart = true; + private string selectedTheme = "Dark"; + private int refreshRate = 500; + private bool highPerf = false; + + private List themes = new() { "Light", "Dark", "Software", "Humanistic" }; + + private Dictionary SelectedErrorHandlers = + Enum.GetValues().ToDictionary(e => e, e => ErrorHandling.PromptUser); + + private void SaveChanges() + { + // Notification logic would go here + Console.WriteLine("Settings synchronized."); + } +} \ No newline at end of file diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs new file mode 100644 index 00000000..2fb4bc33 --- /dev/null +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -0,0 +1,5 @@ +namespace UI.Features.Settings; + +public class SystemSettingsViewModel +{ +} diff --git a/UI/UI.csproj b/UI/UI.csproj index ae8ae880..d8b02654 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 4c3679f81a2b5c75d7d8b73ec2f5154a75f99504 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 29 Apr 2026 11:00:30 -0400 Subject: [PATCH 02/25] Error handling updates --- Ares.Core/Ares.Core.csproj | 2 +- .../Shared/ErrorHandlingEntityConfiguration.cs | 7 +++++++ AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/UI.csproj | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 635dea8b..b78d3475 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -23,7 +23,7 @@ - + diff --git a/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs new file mode 100644 index 00000000..57222659 --- /dev/null +++ b/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs @@ -0,0 +1,7 @@ +using Ares.Datamodel; + +namespace Ares.Core.EntityConfigurations.Shared; + +internal class ErrorHandlingEntityConfiguration : AresEntityTypeBaseConfiguration +{ +} diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index 63cdadea..aa102d71 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -10,7 +10,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 5bc1190a..451a4d60 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index ec6856dc..709b9b54 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 5bc1190a..451a4d60 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/UI.csproj b/UI/UI.csproj index d8b02654..152d51cb 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 454cc8300adfe88f0a9599055ffa055e3b75fc6e Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 12 May 2026 10:11:42 -0400 Subject: [PATCH 03/25] Settings menu now has a functionable selection process for handling errors --- Ares.Core/Ares.Core.csproj | 2 +- Ares.Core/CoreDatabaseContext.cs | 1 + .../ErrorHandlingEntityConfiguration.cs | 14 +++ Ares.Core/ServiceCollectionExtensions.cs | 2 + Ares.Core/Settings/ISystemSettingsManager.cs | 10 ++ Ares.Core/Settings/SystemSettingsManager.cs | 38 +++++++ AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Features/Settings/SystemSettingsView.razor | 101 +++--------------- .../Settings/SystemSettingsViewModel.cs | 41 ++++++- UI/ServiceCollectionExtensions.cs | 4 +- UI/UI.csproj | 2 +- 14 files changed, 127 insertions(+), 96 deletions(-) create mode 100644 Ares.Core/EntityConfigurations/Settings/ErrorHandlingEntityConfiguration.cs create mode 100644 Ares.Core/Settings/ISystemSettingsManager.cs create mode 100644 Ares.Core/Settings/SystemSettingsManager.cs diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index f9767ef5..e1248a6c 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 a7b52024..807f8a74 100644 --- a/Ares.Core/CoreDatabaseContext.cs +++ b/Ares.Core/CoreDatabaseContext.cs @@ -42,6 +42,7 @@ public CoreDatabaseContext(DbContextOptions options) : base(options) public DbSet DeviceVisualizationConfigs => Set(); public DbSet PlannerTransactions => Set(); public DbSet AnalyzerTransactions => Set(); + public DbSet DeviceErrorHandlingConfigs => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/Ares.Core/EntityConfigurations/Settings/ErrorHandlingEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Settings/ErrorHandlingEntityConfiguration.cs new file mode 100644 index 00000000..cbf23e24 --- /dev/null +++ b/Ares.Core/EntityConfigurations/Settings/ErrorHandlingEntityConfiguration.cs @@ -0,0 +1,14 @@ +using Ares.Datamodel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Ares.Core.EntityConfigurations.Settings; + +internal class ErrorHandlingEntityConfiguration : AresEntityTypeBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable("ErrorHandlingConfigs"); + } +} diff --git a/Ares.Core/ServiceCollectionExtensions.cs b/Ares.Core/ServiceCollectionExtensions.cs index 1c31bc56..580e2e78 100644 --- a/Ares.Core/ServiceCollectionExtensions.cs +++ b/Ares.Core/ServiceCollectionExtensions.cs @@ -26,6 +26,7 @@ using Ares.Core.Visualization.Repos; using Ares.Core.Visualization.Providers; using Ares.Core.Visualization.Managers; +using Ares.Core.Settings; namespace Ares.Core; @@ -63,6 +64,7 @@ public static void AddAresCoreComponents(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/Ares.Core/Settings/ISystemSettingsManager.cs b/Ares.Core/Settings/ISystemSettingsManager.cs new file mode 100644 index 00000000..9d99e87e --- /dev/null +++ b/Ares.Core/Settings/ISystemSettingsManager.cs @@ -0,0 +1,10 @@ +using Ares.Datamodel; + +namespace Ares.Core.Settings; + +public interface ISystemSettingsManager +{ + public Task UpdateErrorHandlingSettings(List configs); + + public Task> GetCurrentErrorHandlingSettings(); +} diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs new file mode 100644 index 00000000..32906f0e --- /dev/null +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -0,0 +1,38 @@ +using Ares.Datamodel; +using Microsoft.EntityFrameworkCore; + +namespace Ares.Core.Settings; + +public class SystemSettingsManager : ISystemSettingsManager +{ + private readonly IDbContextFactory _dbContextFactory; + + public SystemSettingsManager(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public async Task UpdateErrorHandlingSettings(List configs) + { + using var context = _dbContextFactory.CreateDbContext(); + foreach(var config in configs) + { + var matchingConfig = context.DeviceErrorHandlingConfigs.FirstOrDefault(c => c.Code == config.Code); + + if(matchingConfig is null) + context.DeviceErrorHandlingConfigs.Add(config); + + else + matchingConfig.Handling = config.Handling; + } + + await context.SaveChangesAsync(); + } + + public async Task> GetCurrentErrorHandlingSettings() + { + using var context = _dbContextFactory.CreateDbContext(); + var results = await context.DeviceErrorHandlingConfigs.ToListAsync(); + return results; + } +} diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index 3f11a654..be2a6d9f 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 2f478030..9539a730 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index 6008ec6b..bb7ce436 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 2f478030..9539a730 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Settings/SystemSettingsView.razor b/UI/Features/Settings/SystemSettingsView.razor index aff40895..83478306 100644 --- a/UI/Features/Settings/SystemSettingsView.razor +++ b/UI/Features/Settings/SystemSettingsView.razor @@ -2,48 +2,23 @@ @using Ares.Datamodel @using Radzen @using Radzen.Blazor -@inherits LayoutComponentBase +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @layout SettingsLayout
-
-@* - Application Settings - *@ -
- - - - - - - - - - -
- - Launch on system startup -
-
-
-
-
-
- - @foreach(var error in Enum.GetValues()) + @foreach(var error in GetFilteredValues()) { - @@ -54,76 +29,28 @@ - - - - - - - - - - - - - - - - - - - - - - - -
- High Performance Mode - -
- - Enabling this allows the system to prioritize modular device drivers and decentralized command processing. - -
-
-
-
-
- - - - - Connect to the external marketplace to download new device implementations. - - - -
- +
@code { - // State logic - private string appName = "ARES System"; - private bool autoStart = true; - private string selectedTheme = "Dark"; - private int refreshRate = 500; - private bool highPerf = false; - - private List themes = new() { "Light", "Dark", "Software", "Humanistic" }; - - private Dictionary SelectedErrorHandlers = - Enum.GetValues().ToDictionary(e => e, e => ErrorHandling.PromptUser); + protected override async Task OnInitializedAsync() + { + if(ViewModel is not null) + await ViewModel!.GetUpdatedSettings(); + } - private void SaveChanges() + private IEnumerable GetFilteredValues() { - // Notification logic would go here - Console.WriteLine("Settings synchronized."); + var values = Enum.GetValues() + .Where(c => c != CommandStatusCode.StatusUnspecified && c != CommandStatusCode.CommandSuccess && c != CommandStatusCode.SuccessWithWarnings); + + return values; } } \ No newline at end of file diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index 2fb4bc33..c675dfa9 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -1,5 +1,42 @@ -namespace UI.Features.Settings; +using Ares.Core.Settings; +using Ares.Datamodel; +using ReactiveUI; -public class SystemSettingsViewModel +namespace UI.Features.Settings; + +public class SystemSettingsViewModel : ReactiveObject { + private readonly ISystemSettingsManager _settingsManager; + + public SystemSettingsViewModel(ISystemSettingsManager settingsManager) + { + _settingsManager = settingsManager; + CurrentErrorHandlingSettings = new(); + Initialize(); + } + + public async Task PushUpdatedSettings() + { + var configs = CurrentErrorHandlingSettings.Select(kvp => new DeviceErrorHandlingConfig() { Code = kvp.Key, Handling = kvp.Value }).ToList(); + await _settingsManager.UpdateErrorHandlingSettings(configs); + } + + public async Task GetUpdatedSettings() + { + var newSettings = await _settingsManager.GetCurrentErrorHandlingSettings(); + foreach(var setting in newSettings) + CurrentErrorHandlingSettings[setting.Code] = setting.Handling; + } + + private void Initialize() + { + foreach(var status in Enum.GetValues()) + { + if(!CurrentErrorHandlingSettings.TryGetValue(status, out var val)) + CurrentErrorHandlingSettings[status] = ErrorHandling.UnknownHandling; + } + } + + + public Dictionary CurrentErrorHandlingSettings { get; set; } } diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 023db323..fdfe4e99 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -48,6 +48,7 @@ using ManualPlannerViewModel = UI.Features.Execution.Planning.ManualPlannerViewModel; using RemoteDeviceSettingsListViewModel = UI.Features.Devices.Remote.RemoteDeviceSettingsListViewModel; using ScriptPlaygroundViewModel = UI.Features.ScriptPlayground.ScriptPlaygroundViewModel; +using UI.Features.Settings; namespace UI; @@ -116,13 +117,14 @@ private static void BindViewModels(this IServiceCollection services) services.AddScoped(); services.AddScoped(); - //Device Settings List View Models + //Settings List View Models services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); //Other View Models services.AddTransient(); diff --git a/UI/UI.csproj b/UI/UI.csproj index 3aba4126..743d367d 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@
- + From f49f299c8333edce828644a2a5f03388bb1e0508 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 12 May 2026 14:51:26 -0400 Subject: [PATCH 04/25] Working towards integrating the new settings into the loop. First with retying commands, which is done at the step execution level when applicable. Next is going to be dealing with things like replanning and safe mode entry. --- .../Execution/CampaignExecutorTests.cs | 5 ++- .../Composers/ExperimentComposerTests.cs | 18 +++++++++-- .../Execution/Composers/StepComposerTests.cs | 9 ++++-- Ares.Core/Ares.Core.csproj | 4 +-- .../Execution/Executors/CampaignExecutor.cs | 3 +- .../Execution/Executors/CommandExecutor.cs | 9 +++--- .../Executors/Composers/StepComposer.cs | 10 +++--- .../Executors/ExecutorSummaryHelpers.cs | 5 +-- .../Executors/SequentialStepExecutor.cs | 32 ++++++++++++++++++- Ares.Core/Settings/ISystemSettingsManager.cs | 2 ++ Ares.Core/Settings/SystemSettingsManager.cs | 20 ++++++++++++ AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- .../Settings/SystemSettingsViewModel.cs | 7 +++- UI/UI.csproj | 2 +- 17 files changed, 107 insertions(+), 27 deletions(-) diff --git a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs index 0f415774..b2840cd9 100644 --- a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs +++ b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs @@ -16,6 +16,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ares.Core.Device.Providers; +using Ares.Core.Settings; namespace Ares.Core.Tests.Execution; @@ -39,6 +40,7 @@ internal class CampaignExecutorTests private ILoggerFactory _loggerFactory; private IAresDeviceProvider _deviceProvider; private IDbContextFactory _dbContextFactory; + private ISystemSettingsManager _settingsManager; private IAnalyzer _replyAnalyzer; @@ -61,6 +63,7 @@ public void OneTimeSetUp() _stateLoggerManagerLogger = new Mock>().Object; _campaignExecutorLogger = new Mock>().Object; _deviceProvider = new Mock().Object; + _settingsManager = new Mock().Object; var loggerFactoryMock = new Mock(); loggerFactoryMock.Setup(f => f.CreateLogger(typeof(CampaignExecutor).FullName)) @@ -69,7 +72,7 @@ public void OneTimeSetUp() var deviceRepo = new AresDeviceRepo(); deviceRepo.AddOrUpdate(new TestDevice()); - var stepComposer = new StepComposer(deviceRepo, _notifier); + var stepComposer = new StepComposer(deviceRepo, _notifier, _settingsManager); var experimentComposer = new ExperimentComposer(stepComposer, _analyzerRepo); var stateLoggerRepository = new DeviceStateLoggerRepository(); diff --git a/Ares.Core.Tests/Execution/Composers/ExperimentComposerTests.cs b/Ares.Core.Tests/Execution/Composers/ExperimentComposerTests.cs index 358edc91..97022cf2 100644 --- a/Ares.Core.Tests/Execution/Composers/ExperimentComposerTests.cs +++ b/Ares.Core.Tests/Execution/Composers/ExperimentComposerTests.cs @@ -1,20 +1,32 @@ -using System.Reflection; -using Ares.Core.Analyzing; +using Ares.Core.Analyzing; using Ares.Core.Execution.Executors; using Ares.Core.Execution.Executors.Composers; +using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel.Templates; using Moq; +using System.Reflection; namespace Ares.Core.Tests.Execution.Composers; internal class ExperimentComposerTests { + private INotifier _notifier; + private ISystemSettingsManager _settingsManager; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _settingsManager = new Mock().Object; + _notifier = new Mock().Object; + } + [Test] public void ExperimentComposer_Composes_StepTemplates_In_Order() { var stepComposerMock = new Mock>(); - stepComposerMock.Setup(composer => composer.Compose(It.IsAny())).Returns(template => new SequentialStepExecutor(template, [])); + stepComposerMock.Setup(composer => composer.Compose(It.IsAny())).Returns(template => new SequentialStepExecutor(template, [], _settingsManager, _notifier)); var stepTemplate1 = new StepTemplate { Index = 0, UniqueId = Guid.NewGuid().ToString() }; diff --git a/Ares.Core.Tests/Execution/Composers/StepComposerTests.cs b/Ares.Core.Tests/Execution/Composers/StepComposerTests.cs index feda33e4..85df98bc 100644 --- a/Ares.Core.Tests/Execution/Composers/StepComposerTests.cs +++ b/Ares.Core.Tests/Execution/Composers/StepComposerTests.cs @@ -6,6 +6,7 @@ using Ares.Datamodel.Templates; using Moq; using System.Reflection; +using Ares.Core.Settings; namespace Ares.Core.Tests.Execution.Composers; @@ -14,6 +15,7 @@ internal class StepComposerTests private StepTemplate _stepTemplate; private AresDeviceRepo _deviceRepo; private INotifier _notifer; + private ISystemSettingsManager _settingsManager; [SetUp] public void SetUp() @@ -63,6 +65,7 @@ public void SetUp() public void OneTimeSetUp() { _notifer = new Mock().Object; + _settingsManager = new Mock().Object; } [TearDown] @@ -74,7 +77,7 @@ public void Dispose() [Test] public void StepComposer_Composes_CommandTemplates_Correctly() { - var stepComposer = new StepComposer(_deviceRepo, _notifer); + var stepComposer = new StepComposer(_deviceRepo, _notifer, _settingsManager); var stepExecutor = stepComposer.Compose(_stepTemplate); var templates = stepExecutor.CommandExecutors.Select(executor => typeof(CommandExecutor).GetProperty("Template", BindingFlags.Public | BindingFlags.Instance).GetValue(executor)).OfType(); Assert.That(templates.Select((template, i) => template.Index == i), Is.All.True); @@ -84,7 +87,7 @@ public void StepComposer_Composes_CommandTemplates_Correctly() public void StepComposer_Composes_Parallel_Template() { _stepTemplate.IsParallel = true; - var stepComposer = new StepComposer(_deviceRepo, _notifer); + var stepComposer = new StepComposer(_deviceRepo, _notifer, _settingsManager); var stepExecutor = stepComposer.Compose(_stepTemplate); Assert.That(stepExecutor, Is.TypeOf()); } @@ -93,7 +96,7 @@ public void StepComposer_Composes_Parallel_Template() public void StepComposer_Composes_Sequential_Template() { _stepTemplate.IsParallel = false; - var stepComposer = new StepComposer(_deviceRepo, _notifer); + var stepComposer = new StepComposer(_deviceRepo, _notifer, _settingsManager); var stepExecutor = stepComposer.Compose(_stepTemplate); Assert.That(stepExecutor, Is.TypeOf()); } diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index e1248a6c..ff998331 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index c396d4f7..eda903e6 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -206,7 +206,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteTemplate(ExperimentExecuto if(IsAwaitingResponse(experimentStatus)) Status.State = ExecutionState.AwaitingUser; + else Status.State = token.IsPaused ? ExecutionState.Paused : ExecutionState.Running; diff --git a/Ares.Core/Execution/Executors/CommandExecutor.cs b/Ares.Core/Execution/Executors/CommandExecutor.cs index 18179068..9169f496 100644 --- a/Ares.Core/Execution/Executors/CommandExecutor.cs +++ b/Ares.Core/Execution/Executors/CommandExecutor.cs @@ -1,5 +1,6 @@ using Ares.Core.Execution.ControlTokens; using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Templates; using Google.Protobuf.WellKnownTypes; @@ -13,8 +14,9 @@ public class CommandExecutor : IExecutor> _command; private readonly BehaviorSubject _stateSubject; private readonly INotifier _notifier; + private readonly ISystemSettingsManager _settingsManager; - public CommandExecutor(Func> command, CommandTemplate template, INotifier notificer) + public CommandExecutor(Func> command, CommandTemplate template, INotifier notifier, ISystemSettingsManager settingsManager) { _command = command; Template = template; @@ -27,7 +29,8 @@ public CommandExecutor(Func> command, Com }; _stateSubject = new BehaviorSubject(executionStatus); - _notifier = notificer; + _notifier = notifier; + _settingsManager = settingsManager; ExperimentStatusObservable = _stateSubject.AsObservable(); } @@ -68,14 +71,12 @@ public async Task Execute(ExecutionControlToken token) else if(result.Success) Status.State = ExecutionState.Succeeded; - else { Status.State = ExecutionState.Failed; await _notifier.Notify("Command Failed!", result.Error, NotificationSeverityEnum.Error); } - _stateSubject.OnNext(Status); _stateSubject.OnCompleted(); diff --git a/Ares.Core/Execution/Executors/Composers/StepComposer.cs b/Ares.Core/Execution/Executors/Composers/StepComposer.cs index 14b14ed1..bc8a921c 100644 --- a/Ares.Core/Execution/Executors/Composers/StepComposer.cs +++ b/Ares.Core/Execution/Executors/Composers/StepComposer.cs @@ -1,5 +1,6 @@ using Ares.Core.Device.Repos; using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Device; using Ares.Datamodel.Templates; @@ -10,11 +11,12 @@ public class StepComposer : ICommandComposer { private readonly INotifier _notifier; private readonly IAresDeviceRepo _deviceRepo; - - public StepComposer(IAresDeviceRepo deviceRepo, INotifier notifier) + private readonly ISystemSettingsManager _settingsManager; + public StepComposer(IAresDeviceRepo deviceRepo, INotifier notifier, ISystemSettingsManager settingsManager) { _deviceRepo = deviceRepo; _notifier = notifier; + _settingsManager = settingsManager; } public StepExecutor Compose(StepTemplate template) @@ -41,7 +43,7 @@ public StepExecutor Compose(StepTemplate template) Func> internalAction = async (ct) => await device.ExecuteCommand(commandTemplate.Metadata.Name, commandArgs, ct); - return new CommandExecutor(internalAction, commandTemplate, _notifier); + return new CommandExecutor(internalAction, commandTemplate, _notifier, _settingsManager); } throw new InvalidOperationException("I'm not certain what to do here yet :("); @@ -52,7 +54,7 @@ public StepExecutor Compose(StepTemplate template) return template.IsParallel ? new ParallelStepExecutor(template, executables) - : new SequentialStepExecutor(template, executables); + : new SequentialStepExecutor(template, executables, _settingsManager, _notifier); } public StepExecutor Compose(StepTemplate template, ExperimentExecutionStatus experimentExecutionStatus) diff --git a/Ares.Core/Execution/Executors/ExecutorSummaryHelpers.cs b/Ares.Core/Execution/Executors/ExecutorSummaryHelpers.cs index 0f447582..0e8b3350 100644 --- a/Ares.Core/Execution/Executors/ExecutorSummaryHelpers.cs +++ b/Ares.Core/Execution/Executors/ExecutorSummaryHelpers.cs @@ -56,8 +56,9 @@ public static CommandExecutionSummary CreateCommandExecutionSummary(CommandTempl Result = deviceResult, TemplateId = template.UniqueId, CommandDescription = template.Metadata.Description, - CommandName = template.Metadata.Name - }; + CommandName = template.Metadata.Name, + StatusCode = deviceResult?.StatusCode ?? CommandStatusCode.StatusUnspecified + }; return commandExecutionSummary; } diff --git a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs index 93785689..f39db884 100644 --- a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs +++ b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs @@ -1,4 +1,6 @@ using Ares.Core.Execution.ControlTokens; +using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Templates; @@ -6,8 +8,16 @@ namespace Ares.Core.Execution.Executors; public class SequentialStepExecutor : StepExecutor { - public SequentialStepExecutor(StepTemplate template, CommandExecutor[] commandExecutors) : base(template, commandExecutors) + private readonly ISystemSettingsManager _settingsManager; + private readonly INotifier _notifier; + + public SequentialStepExecutor(StepTemplate template, + CommandExecutor[] commandExecutors, + ISystemSettingsManager settingsManager, + INotifier notifier) : base(template, commandExecutors) { + _settingsManager = settingsManager; + _notifier = notifier; } public override async Task Execute(ExecutionControlToken token) @@ -21,6 +31,20 @@ public override async Task Execute(ExecutionControlToken t var commandExecutionSummary = await command.Execute(token); + //Handle Retry if needed + var shouldRetry = await ShouldRetry(commandExecutionSummary.StatusCode); + if(!commandExecutionSummary.Result.Success && shouldRetry) + { + var msg = $"ARES attempted to run the command {commandExecutionSummary.CommandName} but it failed. Based on your settings ARES will retry running this command"; + await _notifier.Notify("Retrying Command", msg, NotificationSeverityEnum.Info); + await Task.Delay(2000); + + var retriedCommandExecutionSummary = await command.Execute(token); + + if(retriedCommandExecutionSummary.Result.Success) + commandExecutionSummary = retriedCommandExecutionSummary; + } + if(commandExecutionSummary.Result.Success) commandSummaries.Add(commandExecutionSummary); @@ -30,4 +54,10 @@ public override async Task Execute(ExecutionControlToken t return ExecutorSummaryHelpers.CreateStepExecutionSummary(startTime, DateTime.UtcNow, commandSummaries); } + + private async Task ShouldRetry(CommandStatusCode code) + { + var errorHandling = await _settingsManager.GetErrorHandlingByStatusCode(code); + return errorHandling == ErrorHandling.Retry; + } } diff --git a/Ares.Core/Settings/ISystemSettingsManager.cs b/Ares.Core/Settings/ISystemSettingsManager.cs index 9d99e87e..fac72d81 100644 --- a/Ares.Core/Settings/ISystemSettingsManager.cs +++ b/Ares.Core/Settings/ISystemSettingsManager.cs @@ -7,4 +7,6 @@ public interface ISystemSettingsManager public Task UpdateErrorHandlingSettings(List configs); public Task> GetCurrentErrorHandlingSettings(); + + public Task GetErrorHandlingByStatusCode(CommandStatusCode code); } diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs index 32906f0e..abbeef5d 100644 --- a/Ares.Core/Settings/SystemSettingsManager.cs +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -29,6 +29,26 @@ public async Task UpdateErrorHandlingSettings(List co await context.SaveChangesAsync(); } + /// + /// Based on the provided status code, returns the current setting for that codes error handling procedure. + /// + /// The status code the handling protocol is being requested for. + /// The error handling protocol assigned to that error code. + public async Task GetErrorHandlingByStatusCode(CommandStatusCode code) + { + try + { + using var context = _dbContextFactory.CreateDbContext(); + var matchingSetting = await context.DeviceErrorHandlingConfigs.FirstAsync(c => c.Code == code); + return matchingSetting.Handling; + } + + catch(Exception) + { + return ErrorHandling.UnknownHandling; + } + } + public async Task> GetCurrentErrorHandlingSettings() { using var context = _dbContextFactory.CreateDbContext(); diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index be2a6d9f..b0381df3 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 9539a730..c1a98bec 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index bb7ce436..c3c03cea 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 9539a730..c1a98bec 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index c675dfa9..30f5e6b6 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -17,7 +17,12 @@ public SystemSettingsViewModel(ISystemSettingsManager settingsManager) public async Task PushUpdatedSettings() { - var configs = CurrentErrorHandlingSettings.Select(kvp => new DeviceErrorHandlingConfig() { Code = kvp.Key, Handling = kvp.Value }).ToList(); + var configs = CurrentErrorHandlingSettings.Select(kvp => new DeviceErrorHandlingConfig() + { + Code = kvp.Key, + Handling = kvp.Value } + ).ToList(); + await _settingsManager.UpdateErrorHandlingSettings(configs); } diff --git a/UI/UI.csproj b/UI/UI.csproj index 743d367d..b4970862 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From fc27ce137495644244d48e395ca6b28a5c229bb2 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Tue, 12 May 2026 14:52:33 -0400 Subject: [PATCH 05/25] Spacing change --- Ares.Core/Execution/Executors/CommandExecutor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Ares.Core/Execution/Executors/CommandExecutor.cs b/Ares.Core/Execution/Executors/CommandExecutor.cs index 9169f496..2f54a90f 100644 --- a/Ares.Core/Execution/Executors/CommandExecutor.cs +++ b/Ares.Core/Execution/Executors/CommandExecutor.cs @@ -90,6 +90,7 @@ private async Task InternalExecute(CancellationToken token) var result = await _command(token); return result; } + catch(Exception e) { var result = new CommandResult() { Success = false, Error = e.Message }; From 6d6186c20759772b6c8d750e66821b720357fe9d Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 14 May 2026 13:16:32 -0400 Subject: [PATCH 06/25] Working on adding some general settings --- .../GeneralSettingsEntityConfiguration.cs | 14 ++++ Ares.Core/Settings/ISystemSettingsManager.cs | 5 +- Ares.Core/Settings/SystemSettingsManager.cs | 75 +++++++++++++++++++ AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Features/Settings/SystemSettingsView.razor | 13 ++++ .../Settings/SystemSettingsViewModel.cs | 32 +++++++- UI/ServiceStarter.cs | 5 ++ UI/UI.csproj | 2 +- 11 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs diff --git a/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs new file mode 100644 index 00000000..8618db79 --- /dev/null +++ b/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs @@ -0,0 +1,14 @@ +using Ares.Datamodel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Ares.Core.EntityConfigurations.Settings; + +internal class GeneralSettingsEntityConfiguration : AresEntityTypeBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable("AresGeneralSettingsConfig"); + } +} diff --git a/Ares.Core/Settings/ISystemSettingsManager.cs b/Ares.Core/Settings/ISystemSettingsManager.cs index fac72d81..94ac522f 100644 --- a/Ares.Core/Settings/ISystemSettingsManager.cs +++ b/Ares.Core/Settings/ISystemSettingsManager.cs @@ -4,9 +4,10 @@ namespace Ares.Core.Settings; public interface ISystemSettingsManager { + public Task Initialize(); public Task UpdateErrorHandlingSettings(List configs); - public Task> GetCurrentErrorHandlingSettings(); - public Task GetErrorHandlingByStatusCode(CommandStatusCode code); + public Task GetAresGeneralSettings(); + public Task UpdateAresGeneralSettings(AresGeneralSettingsConfig config); } diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs index abbeef5d..78535e99 100644 --- a/Ares.Core/Settings/SystemSettingsManager.cs +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -12,6 +12,46 @@ public SystemSettingsManager(IDbContextFactory dbContextFac _dbContextFactory = dbContextFactory; } + public async Task Initialize() + { + using var context = _dbContextFactory.CreateDbContext(); + var existingGeneralSettings = await context.GeneralSettingsConfigs.FirstOrDefaultAsync(); + + if(existingGeneralSettings is null) + { + var newGeneralSettingsConfig = new AresGeneralSettingsConfig() + { + UniqueId = Guid.NewGuid().ToString(), + CommandLatency = 0, + ExperimentRetryLimit = 1, + RetryCooldown = 0 + }; + + await context.GeneralSettingsConfigs.AddAsync(newGeneralSettingsConfig); + } + + var existingErrorHandling = await context.DeviceErrorHandlingConfigs.ToListAsync(); + var expectedEntries = Enum.GetValues().Length; + + if(existingErrorHandling.Count != expectedEntries) + { + foreach(var code in Enum.GetValues()) + { + var match = context.DeviceErrorHandlingConfigs.FirstOrDefault(c => c.Code == code); + + if(match is null) + context.DeviceErrorHandlingConfigs.Add(new DeviceErrorHandlingConfig { Code = code, Handling = ErrorHandling.StopAndCloseout }); + } + } + + await context.SaveChangesAsync(); + } + + /// + /// Updates the databases error handling settings based on the provided new configs. + /// + /// The configs to be updated in the database. + /// public async Task UpdateErrorHandlingSettings(List configs) { using var context = _dbContextFactory.CreateDbContext(); @@ -49,10 +89,45 @@ public async Task GetErrorHandlingByStatusCode(CommandStatusCode } } + /// + /// Gets the latest error handling settings from the database. + /// + /// An enumerable of device error handling configs representing the latest settings. public async Task> GetCurrentErrorHandlingSettings() { using var context = _dbContextFactory.CreateDbContext(); var results = await context.DeviceErrorHandlingConfigs.ToListAsync(); return results; } + + /// + /// Gets the latest general settings config from the database. + /// + /// An ARES general settings config containing all the latest settings + public async Task GetAresGeneralSettings() + { + using var context = _dbContextFactory.CreateDbContext(); + var config = await context.GeneralSettingsConfigs.FirstOrDefaultAsync(); + return config; + } + + /// + /// Updates the stored settings config in the database to match the provided config settings. + /// + /// A config containing the updated settings. + /// + public async Task UpdateAresGeneralSettings(AresGeneralSettingsConfig config) + { + using var context = _dbContextFactory.CreateDbContext(); + var existingConfig = await context.GeneralSettingsConfigs.FirstOrDefaultAsync(); + + if(existingConfig is not null) + { + existingConfig.RetryCooldown = config.RetryCooldown; + existingConfig.CommandLatency = config.CommandLatency; + existingConfig.ExperimentRetryLimit = config.ExperimentRetryLimit; + + await context.SaveChangesAsync(); + } + } } diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index b0381df3..f09367f7 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index c1a98bec..5cca4a5e 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index c3c03cea..3853db86 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index c1a98bec..5cca4a5e 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Settings/SystemSettingsView.razor b/UI/Features/Settings/SystemSettingsView.razor index 83478306..3605118c 100644 --- a/UI/Features/Settings/SystemSettingsView.razor +++ b/UI/Features/Settings/SystemSettingsView.razor @@ -4,12 +4,23 @@ @using Radzen.Blazor @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @layout SettingsLayout +@inject TooltipService tooltipService
+ + + + + + + + + + @@ -53,4 +64,6 @@ return values; } + + void ShowRetryLimitTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the max times ARES will retry a failing command or experiment", options); } \ No newline at end of file diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index 30f5e6b6..9cc3a5fb 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -1,10 +1,11 @@ using Ares.Core.Settings; using Ares.Datamodel; using ReactiveUI; +using ReactiveUI.SourceGenerators; namespace UI.Features.Settings; -public class SystemSettingsViewModel : ReactiveObject +public partial class SystemSettingsViewModel : ReactiveObject { private readonly ISystemSettingsManager _settingsManager; @@ -24,13 +25,28 @@ public async Task PushUpdatedSettings() ).ToList(); await _settingsManager.UpdateErrorHandlingSettings(configs); + await _settingsManager.UpdateAresGeneralSettings(new AresGeneralSettingsConfig + { + ExperimentRetryLimit = ExperimentRetryLimit, + RetryCooldown = ExperimentRetryCooldown, + CommandLatency = CommandLatency + }); } public async Task GetUpdatedSettings() { - var newSettings = await _settingsManager.GetCurrentErrorHandlingSettings(); - foreach(var setting in newSettings) + var newErrorHandlingSettings = await _settingsManager.GetCurrentErrorHandlingSettings(); + var newGeneralSettings = await _settingsManager.GetAresGeneralSettings(); + + foreach(var setting in newErrorHandlingSettings) CurrentErrorHandlingSettings[setting.Code] = setting.Handling; + + if(newGeneralSettings is not null) + { + ExperimentRetryCooldown = newGeneralSettings.RetryCooldown; + ExperimentRetryLimit = newGeneralSettings.ExperimentRetryLimit; + CommandLatency = newGeneralSettings.CommandLatency; + } } private void Initialize() @@ -42,6 +58,14 @@ private void Initialize() } } - public Dictionary CurrentErrorHandlingSettings { get; set; } + + [Reactive] + public partial int ExperimentRetryLimit { get; set; } + + [Reactive] + public partial int ExperimentRetryCooldown { get; set; } + + [Reactive] + public partial int CommandLatency { get; set; } } diff --git a/UI/ServiceStarter.cs b/UI/ServiceStarter.cs index 56203c62..2fe9e82c 100644 --- a/UI/ServiceStarter.cs +++ b/UI/ServiceStarter.cs @@ -5,6 +5,7 @@ using Ares.Core.Device.Remote; using Ares.Core.Device.State.Logging; using Ares.Core.Planning; +using Ares.Core.Settings; using Ares.Core.Visualization.Managers; using UI.Application.Devices.Repos; using UI.Application.Notifications; @@ -31,6 +32,7 @@ public class ServiceStarter : BackgroundService private readonly IConfiguration _configuration; private readonly StartupStateTracker _tracker; private readonly ILogger _logger; + private readonly ISystemSettingsManager _settingsManager; private readonly string _dataPath; private readonly string _resultsPath; @@ -54,6 +56,7 @@ public ServiceStarter( DeviceAdapterManager deviceAdapterManager, StateLoggerManager stateLoggerManager, StartupStateTracker tracker, + ISystemSettingsManager settingsManager, ILogger logger) { _notificationReceivingService = notificationReceivingService; @@ -71,6 +74,7 @@ public ServiceStarter( _deviceConfigManager = deviceConfigManager; _driverDbManager = driverDbManager; _tracker = tracker; + _settingsManager = settingsManager; _dataPath = _configuration.Get()?.AresDataPath ?? ""; _resultsPath = Path.Combine(_dataPath, AppSettings.ResultsFolder); @@ -86,6 +90,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken) var localTrack = Task.Run(async () => { + await _settingsManager.Initialize(); _stateLoggerManager.Initialize(); await _deviceDriverLoader.LoadModulesAsync(_pluginsPath); _deviceControlViewModelRepo.Initialize(); diff --git a/UI/UI.csproj b/UI/UI.csproj index b4970862..b6d273e1 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 6edeb8e547580fcffda73f2f7852524b5372b009 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 14 May 2026 13:16:57 -0400 Subject: [PATCH 07/25] Forgot some additional changes --- .../Execution/CampaignExecutorTests.cs | 5 +- Ares.Core/Ares.Core.csproj | 2 +- Ares.Core/CoreDatabaseContext.cs | 2 +- .../Execution/Executors/CampaignExecutor.cs | 205 +++++++++++++++++- .../Execution/Executors/CommandExecutor.cs | 2 +- .../Executors/Composers/CampaignComposer.cs | 12 +- .../Execution/Executors/ExperimentExecutor.cs | 4 +- .../Executors/SequentialStepExecutor.cs | 5 +- .../Safety/ExecutionSafetyManager.cs | 1 - 9 files changed, 222 insertions(+), 16 deletions(-) diff --git a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs index b2840cd9..27fbc193 100644 --- a/Ares.Core.Tests/Execution/CampaignExecutorTests.cs +++ b/Ares.Core.Tests/Execution/CampaignExecutorTests.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging; using Ares.Core.Device.Providers; using Ares.Core.Settings; +using Ares.Core.Execution.Safety; namespace Ares.Core.Tests.Execution; @@ -41,6 +42,7 @@ internal class CampaignExecutorTests private IAresDeviceProvider _deviceProvider; private IDbContextFactory _dbContextFactory; private ISystemSettingsManager _settingsManager; + private IExecutionSafetyManager _safetyManager; private IAnalyzer _replyAnalyzer; @@ -64,6 +66,7 @@ public void OneTimeSetUp() _campaignExecutorLogger = new Mock>().Object; _deviceProvider = new Mock().Object; _settingsManager = new Mock().Object; + _safetyManager = new Mock().Object; var loggerFactoryMock = new Mock(); loggerFactoryMock.Setup(f => f.CreateLogger(typeof(CampaignExecutor).FullName)) @@ -79,7 +82,7 @@ public void OneTimeSetUp() var factory = Mock.Of(); var dbContextFactory = Mock.Of>(); _stateLoggerManager = new StateLoggerManager(stateLoggerRepository, factory, _stateLoggerManagerLogger, dbContextFactory, _deviceProvider); - _campaignComposer = new CampaignComposer(_analysisHelper, experimentComposer, _planningHelper, _executionReporter, _resultHandlers, _analysisRepo, _analyzerRepo, _notifier, _loggerFactory, _variableManager, _stateLoggerManager); + _campaignComposer = new CampaignComposer(_analysisHelper, experimentComposer, _planningHelper, _executionReporter, _resultHandlers, _analysisRepo, _analyzerRepo, _notifier, _loggerFactory, _variableManager, _stateLoggerManager, _settingsManager, _safetyManager); } [SetUp] diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index ff998331..d75df40e 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 807f8a74..4d32e44d 100644 --- a/Ares.Core/CoreDatabaseContext.cs +++ b/Ares.Core/CoreDatabaseContext.cs @@ -43,7 +43,7 @@ public CoreDatabaseContext(DbContextOptions options) : base(options) public DbSet PlannerTransactions => Set(); public DbSet AnalyzerTransactions => Set(); public DbSet DeviceErrorHandlingConfigs => Set(); - + public DbSet GeneralSettingsConfigs => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { var assembly = Assembly.GetAssembly(typeof(CoreDatabaseContext)); diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index eda903e6..1b556c2d 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -7,10 +7,12 @@ using Ares.Core.Execution.ControlTokens; using Ares.Core.Execution.Executors.Composers; using Ares.Core.Execution.Extensions; +using Ares.Core.Execution.Safety; using Ares.Core.Execution.StopConditions; using Ares.Core.Notifications; using Ares.Core.Output; using Ares.Core.Planning; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Analyzing; using Ares.Datamodel.Templates; @@ -33,6 +35,8 @@ public class CampaignExecutor : ICampaignExecutor readonly AnalysisHelper _analysisHelper; readonly AnalysisRepo _analysisRepo; readonly IAnalyzerRepo _analyzerRepo; + readonly ISystemSettingsManager _settingsManager; + readonly IExecutionSafetyManager _executionSafetyManager; internal CampaignExecutor(ICommandComposer experimentComposer, IPlanningHelper planningHelper, @@ -45,7 +49,9 @@ internal CampaignExecutor(ICommandComposer logger, AresVariableManager variableManager, - StateLoggerManager stateLoggerManager) + StateLoggerManager stateLoggerManager, + ISystemSettingsManager settingsManager, + IExecutionSafetyManager executionSafetyManager) { _analyzerRepo = analyzerRepo; _analysisRepo = analysisRepo; @@ -59,6 +65,8 @@ internal CampaignExecutor(ICommandComposer ExecuteExperimentLoop(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess, ExperimentExecutionSummary startupSummary) + { + var campaignFailedTitle = "Campaign Execution Failed!"; + var currentState = ExecutionState.InitializeExperiment; + executionSuccess = true; + var experimentCount = 0; + var currentExperimentFolder = ""; + var currentExperimentPath = ""; + + ExperimentExecutorResult currentExecutorResult = new(); + ExperimentExecutionSummary? currentSummary = null; + ExperimentTemplate? currentExperimentTemplate = null; + + while(!ShouldStop() && !token.IsCancelled && executionSuccess != false) + { + switch(currentState) + { + case ExecutionState.InitializeExperiment: + experimentCount++; + currentExperimentFolder = $"Experiment_{++experimentCount}"; + currentExperimentPath = CampaignOutputHelper.CreateExperimentSubFolder(campaignPath, currentExperimentFolder); + currentExperimentTemplate = Template.ExperimentTemplate.CloneWithNewIds(); + currentExperimentTemplate.Name = Template.Name; + + AresEnvironment.AresEnvironment.SetInternalVariable(InternalVariableType.CurrentExperimentNumber, experimentCount.ToString()); + _logger.LogInformation("ARES is initializing experiment {exp_number}, and has created the associated directories.", experimentCount); + currentState = ExecutionState.Planning; + break; + + case ExecutionState.GenerateExecutor: + if(currentExperimentTemplate is null) + { + var nullTemplateMsg = "ARES attempted to generate an experiment executor, but failed due to a null experiment template! The experiment will be terminated."; + _logger.LogError(nullTemplateMsg); + await _notifier.Notify(campaignFailedTitle, nullTemplateMsg, NotificationSeverityEnum.Error); + currentState = ExecutionState.Failed; + break; + } + + try + { + currentExecutorResult = new ExperimentExecutorResult(); + currentExecutorResult.ExperimentExecutor = _experimentComposer.Compose(currentExperimentTemplate); + currentState = ExecutionState.Running; + } + + catch(Exception e) + { + currentState = ExecutionState.Failed; + _logger.LogError("ARES encountered an error while composing an experiment executor! {msg}", e.Message); + await _notifier.Notify(campaignFailedTitle, $"Executor Composition Error: {e.Message}", NotificationSeverityEnum.Error); + break; + } + + break; + + case ExecutionState.Succeeded: + _logger.LogInformation("ARES successfully executed the experiment {exp_name}!", currentExperimentTemplate?.Name ?? "UNKNOWN"); + break; + + case ExecutionState.Running: + if(currentExecutorResult is null || currentExecutorResult.ExperimentExecutor is null) + { + _logger.LogError("ARES tried to execute an experiment but failed due to a null experiment executor."); + currentState = ExecutionState.Failed; + break; + } + + var experimentSummary = await ExecuteTemplate(currentExecutorResult.ExperimentExecutor, token); + experimentSummary.ResultOutputPath = currentExperimentPath; + + //Handle Failure Cases Here.... + + await PostExperimentExecution(experimentSummary); + experimentSummaries.Add(experimentSummary); + break; + + case ExecutionState.Failed: + executionSuccess = false; + _logger.LogInformation("ARES experiment reached the 'Failed' execution state. Experiment will be terminated."); + break; + + case ExecutionState.WaitingForUserDecision: + break; + + case ExecutionState.Replanning: + _logger.LogInformation("ARES had an experiment fail, but based on your settings will attempt to re-plan the experiment and run it again."); + currentState = ExecutionState.Planning; + break; + + case ExecutionState.Retrying: + _logger.LogInformation("ARES had an experiment fail, but based on your settings will retry running the experiment."); + currentState = ExecutionState.Running; + break; + + case ExecutionState.EnteringSafeMode: + await _executionSafetyManager.EnterSafeMode(); + currentState = ExecutionState.Failed; + break; + + case ExecutionState.Planning: + if(currentExperimentTemplate is not null && !currentExperimentTemplate.IsResolved()) + { + _logger.LogTrace("Experiment has not been resolved, ARES will now begin the planning process."); + if(analyses.Count() % ReplanRate == 0) + { + Status.PlannerState = PlannerState.PlanningInProgress; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); + _logger.LogTrace("Analyses count is {count} and replan rate {rate}", analyses.Count(), ReplanRate); + + var metadata = new RequestMetadata + { + CampaignId = Template.UniqueId, + CampaignName = Template.Name, + ExperimentId = currentExperimentTemplate.UniqueId, + SystemName = "ARES OS", + ExperimentStartTime = DateTime.UtcNow.ToUniversalTime().ToTimestamp() + }; + + var resolveSuccess = await _planningHelper.TryResolveParameters(Template.PlannerAllocations, + metadata, + currentExperimentTemplate.GetAllPlannedParameters(), + analyses, + experimentSummaries.Select(es => es.ExperimentOverview), + token.CancellationToken); + + if(!resolveSuccess) + { + Status.PlannerState = PlannerState.PlanningError; + _logger.LogDebug("Failed to resolve the experiment template."); + currentExecutorResult?.ErrorString = "Failed to plan! Experiment will be terminated!"; + } + } + + else + { + currentExperimentTemplate = experimentSummaries.Select(es => es.ExperimentOverview).Last().Template.CloneWithNewIds(); + _logger.LogDebug("Experiment was already resolved, so ARES cloned the template with new UUID's"); + } + + Status.PlannerState = PlannerState.PlanningComplete; + } + + if(currentExperimentTemplate is not null && !currentExperimentTemplate.IsEnvironmentResolved()) + { + _logger.LogTrace("Environment variables were not resolved yet. ARES will attempt to resolve the experiments environment variables."); + var resolveVarsSuccess = _variableManager.TryResolveVariable(currentExperimentTemplate.GetAllParameters()); + + if(!resolveVarsSuccess) + { + _logger.LogDebug("Failed to assign environment variables, experiment will be terminated!"); + currentState = ExecutionState.Failed; + break; + } + + else + _logger.LogDebug("Successfully resolved ARES environment variables."); + } + + currentState = ExecutionState.GenerateExecutor; + break; + + case ExecutionState.Analyzing: + if(!token.IsCancelled && currentExecutorResult is not null && currentExecutorResult.ExperimentExecutor is not null && currentSummary is not null) + { + var result = await AnalyzeResult(currentExecutorResult.ExperimentExecutor, currentSummary, startupSummary, analyses, token); + + if(!result.Success) + executionSuccess = false; + + if(!result.Continue) + break; + } + else + executionSuccess = false; + + break; + + default: + break; + } + } + + return executionSuccess; + } + + private async Task ExecuteExperimentLoopArchive(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess, ExperimentExecutionSummary startupSummary) { var experimentCount = 0; while(!ShouldStop() && !token.IsCancelled && executionSuccess == true) { var experimentFolder = $"Experiment_{++experimentCount}"; var experimentPath = CampaignOutputHelper.CreateExperimentSubFolder(campaignPath, experimentFolder); - var startTime = DateTime.UtcNow; //Populate Internal Variables Related to Experiment AresEnvironment.AresEnvironment.SetInternalVariable(InternalVariableType.CurrentExperimentNumber, experimentCount.ToString()); @@ -224,10 +418,11 @@ private async Task ExecuteExperimentLoop(string campaignPath, List step.CommandSummaries.Any(cmd => !cmd.Result.Success)) || !experimentSummary.StepSummaries.Any()) { - _logger.LogWarning("Command failure detected. Stopping experiment."); + _logger.LogWarning("Command failure detected."); + executionSuccess = false; experimentSummaries.Add(experimentSummary); break; @@ -242,9 +437,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List Execute(ExecutionControlToken token) else { Status.State = ExecutionState.Failed; - await _notifier.Notify("Command Failed!", result.Error, NotificationSeverityEnum.Error); + await _notifier.Notify("Command Execution Failed!", $"ARES failed to execute a command. {result.Error}", NotificationSeverityEnum.Error); } _stateSubject.OnNext(Status); diff --git a/Ares.Core/Execution/Executors/Composers/CampaignComposer.cs b/Ares.Core/Execution/Executors/Composers/CampaignComposer.cs index 09e0e329..36850df6 100644 --- a/Ares.Core/Execution/Executors/Composers/CampaignComposer.cs +++ b/Ares.Core/Execution/Executors/Composers/CampaignComposer.cs @@ -1,8 +1,10 @@ using Ares.Core.Analyzing; using Ares.Core.AresEnvironment; using Ares.Core.Device.State.Logging; +using Ares.Core.Execution.Safety; using Ares.Core.Notifications; using Ares.Core.Planning; +using Ares.Core.Settings; using Ares.Datamodel.Templates; using Microsoft.Extensions.Logging; @@ -21,6 +23,8 @@ public class CampaignComposer : ICommandComposer experimentComposer, @@ -32,7 +36,9 @@ public CampaignComposer(AnalysisHelper analysisHelper, INotifier notifier, ILoggerFactory loggerFactory, AresVariableManager variableManager, - StateLoggerManager stateLoggerManager) + StateLoggerManager stateLoggerManager, + ISystemSettingsManager settingsManager, + IExecutionSafetyManager safetyManager) { _analyzerRepo = analyzerRepo; _analysisRepo = analysisRepo; @@ -45,8 +51,10 @@ public CampaignComposer(AnalysisHelper analysisHelper, _resultHandlers = resultHandlers; _notifier = notifier; _loggerFactory = loggerFactory; + _settingsManager = settingsManager; + _safetyManager = safetyManager; } public ICampaignExecutor Compose(CampaignTemplate template) - => new CampaignExecutor(_experimentComposer, _planningHelper, _executionReporter, _analysisHelper, template, _resultHandlers, _analysisRepo, _notifier, _analyzerRepo, _loggerFactory.CreateLogger(), _variableManager, _stateLoggerManager); + => new CampaignExecutor(_experimentComposer, _planningHelper, _executionReporter, _analysisHelper, template, _resultHandlers, _analysisRepo, _notifier, _analyzerRepo, _loggerFactory.CreateLogger(), _variableManager, _stateLoggerManager, _settingsManager, _safetyManager); } diff --git a/Ares.Core/Execution/Executors/ExperimentExecutor.cs b/Ares.Core/Execution/Executors/ExperimentExecutor.cs index d4b5c44d..42046ed5 100644 --- a/Ares.Core/Execution/Executors/ExperimentExecutor.cs +++ b/Ares.Core/Execution/Executors/ExperimentExecutor.cs @@ -1,6 +1,7 @@ using System.Reactive.Linq; using Ares.Core.Execution.ControlTokens; using Ares.Core.Execution.Extensions; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Templates; @@ -8,8 +9,7 @@ namespace Ares.Core.Execution.Executors; public class ExperimentExecutor : IExecutor { - public ExperimentExecutor(ExperimentTemplate template, - IExecutor[] experimentStepExecutors) + public ExperimentExecutor(ExperimentTemplate template, IExecutor[] experimentStepExecutors) { ExperimentStepExecutors = experimentStepExecutors; Template = template; diff --git a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs index f39db884..a8c15659 100644 --- a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs +++ b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs @@ -49,7 +49,10 @@ public override async Task Execute(ExecutionControlToken t commandSummaries.Add(commandExecutionSummary); else - return ExecutorSummaryHelpers.CreateEmptyStepExecutionSummary(startTime, DateTime.UtcNow); + { + commandSummaries.Add(commandExecutionSummary); + return ExecutorSummaryHelpers.CreateStepExecutionSummary(startTime, DateTime.UtcNow, commandSummaries); + } } return ExecutorSummaryHelpers.CreateStepExecutionSummary(startTime, DateTime.UtcNow, commandSummaries); diff --git a/Ares.Core/Execution/Safety/ExecutionSafetyManager.cs b/Ares.Core/Execution/Safety/ExecutionSafetyManager.cs index c6a720e8..0f956f5b 100644 --- a/Ares.Core/Execution/Safety/ExecutionSafetyManager.cs +++ b/Ares.Core/Execution/Safety/ExecutionSafetyManager.cs @@ -1,5 +1,4 @@ using Ares.Core.Device.Repos; -using Microsoft.Build.Framework; using Microsoft.Extensions.Logging; namespace Ares.Core.Execution.Safety; From 8460a5ef07d59909f9842dfdf0e4f031d3a8c63d Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 14 May 2026 14:07:04 -0400 Subject: [PATCH 08/25] Some cleanup and additional UI for general settings --- .../Execution/Executors/ExperimentExecutor.cs | 1 - Ares.Core/Settings/SystemSettingsManager.cs | 26 +++++++++++++------ UI/Features/Settings/SystemSettingsView.razor | 23 +++++++++++++--- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/Ares.Core/Execution/Executors/ExperimentExecutor.cs b/Ares.Core/Execution/Executors/ExperimentExecutor.cs index 42046ed5..1e57966b 100644 --- a/Ares.Core/Execution/Executors/ExperimentExecutor.cs +++ b/Ares.Core/Execution/Executors/ExperimentExecutor.cs @@ -1,7 +1,6 @@ using System.Reactive.Linq; using Ares.Core.Execution.ControlTokens; using Ares.Core.Execution.Extensions; -using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Templates; diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs index 78535e99..c771b8f8 100644 --- a/Ares.Core/Settings/SystemSettingsManager.cs +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -27,21 +27,31 @@ public async Task Initialize() RetryCooldown = 0 }; - await context.GeneralSettingsConfigs.AddAsync(newGeneralSettingsConfig); + context.GeneralSettingsConfigs.Add(newGeneralSettingsConfig); } var existingErrorHandling = await context.DeviceErrorHandlingConfigs.ToListAsync(); - var expectedEntries = Enum.GetValues().Length; + var allEnumValues = Enum.GetValues(); - if(existingErrorHandling.Count != expectedEntries) + if(existingErrorHandling.Count != allEnumValues.Length) { - foreach(var code in Enum.GetValues()) - { - var match = context.DeviceErrorHandlingConfigs.FirstOrDefault(c => c.Code == code); + var existingCodes = existingErrorHandling.Select(c => c.Code).ToHashSet(); + var missingConfigs = new List(); - if(match is null) - context.DeviceErrorHandlingConfigs.Add(new DeviceErrorHandlingConfig { Code = code, Handling = ErrorHandling.StopAndCloseout }); + foreach(var code in allEnumValues) + { + if(!existingCodes.Contains(code)) + { + missingConfigs.Add(new DeviceErrorHandlingConfig + { + Code = code, + Handling = ErrorHandling.StopAndCloseout + }); + } } + + if(missingConfigs.Any()) + context.DeviceErrorHandlingConfigs.AddRange(missingConfigs); } await context.SaveChangesAsync(); diff --git a/UI/Features/Settings/SystemSettingsView.razor b/UI/Features/Settings/SystemSettingsView.razor index 3605118c..f495b519 100644 --- a/UI/Features/Settings/SystemSettingsView.razor +++ b/UI/Features/Settings/SystemSettingsView.razor @@ -12,12 +12,24 @@ - - + + - + + + + + + + + + + + + + @@ -65,5 +77,10 @@ return values; } + TooltipOptions Options = new TooltipOptions { Duration = null }; + void ShowRetryLimitTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the max times ARES will retry a failing command or experiment", options); + void ShowRetryCooldownTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the a cooldown value, in seconds, on how long ARES waits to re-attempt failed commands", options); + void ShowCommandLatencyTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets whether a universal latency should be applied, in seconds, between commands (i.e. a one second universal latency results in a constant one second delay between commands being executed)", options); + } \ No newline at end of file From 8cc1d74431c41f047c009e7ef92b654efe2f9c59 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 14 May 2026 17:32:27 -0400 Subject: [PATCH 09/25] Most error handling cases are now working. Only outlier left is the "prompt user" case which will need some additional UI work done. --- Ares.Core/Ares.Core.csproj | 2 +- .../Execution/Executors/CampaignExecutor.cs | 203 ++++++++++-------- .../Execution/Executors/CommandExecutor.cs | 4 +- .../Executors/SequentialStepExecutor.cs | 39 +++- .../StopConditions/NumExperimentsRun.cs | 9 +- Ares.Core/Settings/SystemSettingsManager.cs | 4 +- AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Features/Settings/SystemSettingsView.razor | 11 +- .../Settings/SystemSettingsViewModel.cs | 7 +- UI/UI.csproj | 2 +- 13 files changed, 179 insertions(+), 110 deletions(-) diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index d75df40e..645176a9 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 1b556c2d..f12d2a4e 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -1,6 +1,4 @@ -using System.Reactive.Linq; -using System.Reactive.Subjects; -using Ares.Core.Analyzing; +using Ares.Core.Analyzing; using Ares.Core.AresEnvironment; using Ares.Core.Device.State.Logging; using Ares.Core.Exceptions; @@ -18,6 +16,8 @@ using Ares.Datamodel.Templates; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; +using System.Reactive.Linq; +using System.Reactive.Subjects; namespace Ares.Core.Execution.Executors; @@ -38,6 +38,9 @@ public class CampaignExecutor : ICampaignExecutor readonly ISystemSettingsManager _settingsManager; readonly IExecutionSafetyManager _executionSafetyManager; + private readonly string _campaignFailedTitle = "Campaign Execution Failed!"; + private AresGeneralSettingsConfig? _generalSettingsConfig; + internal CampaignExecutor(ICommandComposer experimentComposer, IPlanningHelper planningHelper, IExecutionReporter executionReporter, @@ -116,8 +119,6 @@ await _stateLoggerManager.EnableOverrideAsync(new Datamodel.Device.DeviceLogging executionSuccess = await ExecuteExperimentLoop(campaignPath, analyses, experimentSummaries, token, executionSuccess, startupResult.Summary); _logger.LogDebug("The campaign loop is now finished. Moving onto the closeout script."); - - var closeoutResult = await ExecuteCloseout(analyses, experimentSummaries, token, executionSuccess); closeoutSummary = closeoutResult.Summary; @@ -156,6 +157,7 @@ private async Task InitializeCampaign(DateTime startTime) _logger.LogInformation("Analyzer selected {AnalyzerName}", analyzer?.Name ?? "NO ANALYZER"); await CampaignOutputHelper.OutputVersionFile(campaignPath, Template, analyzer); + _generalSettingsConfig = await _settingsManager.GetAresGeneralSettings(); return campaignPath; } @@ -200,18 +202,20 @@ private async Task NotifyCampaignStart() private async Task ExecuteExperimentLoop(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess, ExperimentExecutionSummary startupSummary) { - var campaignFailedTitle = "Campaign Execution Failed!"; var currentState = ExecutionState.InitializeExperiment; executionSuccess = true; var experimentCount = 0; var currentExperimentFolder = ""; var currentExperimentPath = ""; + var failedExperimentRetryCount = 0; + var failedExperimentRetryLimit = _generalSettingsConfig?.ExperimentRetryLimit ?? 1; + var experimentRetryCooldown = _generalSettingsConfig?.RetryCooldown ?? 0; ExperimentExecutorResult currentExecutorResult = new(); ExperimentExecutionSummary? currentSummary = null; ExperimentTemplate? currentExperimentTemplate = null; - while(!ShouldStop() && !token.IsCancelled && executionSuccess != false) + while(!ShouldStop() && !token.IsCancelled && executionSuccess) { switch(currentState) { @@ -232,7 +236,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); + + if(failedCommandSummary is not null) + currentState = await HandleError(failedCommandSummary); + + else + { + await PostExperimentExecution(experimentSummary); + experimentSummaries.Add(experimentSummary); + currentState = ExecutionState.InitializeExperiment; + } - await PostExperimentExecution(experimentSummary); - experimentSummaries.Add(experimentSummary); break; case ExecutionState.Failed: @@ -284,12 +296,49 @@ private async Task ExecuteExperimentLoop(string campaignPath, List failedExperimentRetryLimit) + { + var retryLimitMsg = "An ARES experiment failed, based on your settings ARES reached the maximum number of retries for this campaign. Execution will be terminated."; + _logger.LogError(retryLimitMsg); + await _notifier.Notify("Retry Limit Reached, Experiment Failed", retryLimitMsg, NotificationSeverityEnum.Error); + currentState = ExecutionState.Failed; + break; + } + + var retryingMsg = $"An ARES experiment failed, but based on your settings ARES will retry running the experiment up to {failedExperimentRetryLimit} times. This is retry number {failedExperimentRetryCount}"; + _logger.LogInformation(retryingMsg); + await _notifier.Notify("Retrying Experiment", retryingMsg, NotificationSeverityEnum.Info); + + if(experimentRetryCooldown != 0) + await Task.Delay(TimeSpan.FromSeconds(experimentRetryCooldown)); + currentState = ExecutionState.Running; break; @@ -302,35 +351,13 @@ private async Task ExecuteExperimentLoop(string campaignPath, List es.ExperimentOverview), - token.CancellationToken); - - if(!resolveSuccess) - { - Status.PlannerState = PlannerState.PlanningError; - _logger.LogDebug("Failed to resolve the experiment template."); + var success = await PlanExperiment(analyses, currentExperimentTemplate, experimentSummaries, token); + + if(!success) currentExecutorResult?.ErrorString = "Failed to plan! Experiment will be terminated!"; - } } else @@ -385,64 +412,64 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoopArchive(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, bool executionSuccess, ExperimentExecutionSummary startupSummary) + private async Task HandleError(CommandExecutionSummary cmdSummary) { - var experimentCount = 0; - while(!ShouldStop() && !token.IsCancelled && executionSuccess == true) + var errorHandling = await _settingsManager.GetErrorHandlingByStatusCode(cmdSummary.Result.StatusCode); + + switch(errorHandling) { - var experimentFolder = $"Experiment_{++experimentCount}"; - var experimentPath = CampaignOutputHelper.CreateExperimentSubFolder(campaignPath, experimentFolder); + case ErrorHandling.PromptUser: + break; - //Populate Internal Variables Related to Experiment - AresEnvironment.AresEnvironment.SetInternalVariable(InternalVariableType.CurrentExperimentNumber, experimentCount.ToString()); - _logger.LogDebug("Set ARES environment variable {VarName} to {VarValue}", InternalVariableType.CurrentExperimentNumber, experimentCount); - var experimentExecutorResult = await GenerateExperimentExecutor(Template.ExperimentTemplate, analyses, experimentSummaries.Select(es => es.ExperimentOverview), token.CancellationToken); + case ErrorHandling.Replan: + return ExecutionState.Replanning; - if(experimentExecutorResult.ErrorString is not null) - { - _logger.LogError("Experiment Executor Generation Failed! Reason: {errorString}", experimentExecutorResult.ErrorString); - await _notifier.Notify("Experiment Executor Generation Failure", experimentExecutorResult.ErrorString, NotificationSeverityEnum.Error); - executionSuccess = false; - break; - } + case ErrorHandling.RetryExperiment: + return ExecutionState.Retrying; + - if(experimentExecutorResult.ExperimentExecutor is null) - { - _logger.LogError("Experiment Executor Generation Failed! Reason was not specified."); - await _notifier.Notify("Experiment Executor Generation Failure", "Error was not specified, but the executor generation has failed.", NotificationSeverityEnum.Error); - executionSuccess = false; - break; - } + case ErrorHandling.EnterSafeMode: + await _executionSafetyManager.EnterSafeMode(); + await _notifier.Notify("Safe Mode Activated", "An ARES experiment has failed and based on your settings ARES has entered safe mode in response.", NotificationSeverityEnum.Error); + return ExecutionState.Failed; - var experimentExecutor = experimentExecutorResult.ExperimentExecutor; - var experimentSummary = await ExecuteTemplate(experimentExecutor, token); - experimentSummary.ResultOutputPath = experimentPath; + default: + return ExecutionState.Failed; + } - //COMMAND FAILURE. Check for failure status code, act as needed. - if(experimentSummary.StepSummaries.Any(step => step.CommandSummaries.Any(cmd => !cmd.Result.Success)) || !experimentSummary.StepSummaries.Any()) - { - _logger.LogWarning("Command failure detected."); + return ExecutionState.Failed; + } - executionSuccess = false; - experimentSummaries.Add(experimentSummary); - break; - } + private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, ExecutionControlToken token) + { + Status.PlannerState = PlannerState.PlanningInProgress; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); + _logger.LogTrace("Analyses count is {count} and replan rate {rate}", analyses.Count(), ReplanRate); - // if the execution was canceled, the experiment may not have executed the command to provide the output - // and thus sending a null result to the analyzer might break it depending on the analyzer - if(!token.IsCancelled) - { - var result = await AnalyzeResult(experimentExecutor, experimentSummary, startupSummary, analyses, token); - if (!result.Success) executionSuccess = false; - if (!result.Continue) break; - } - else - executionSuccess = false; + var metadata = new RequestMetadata + { + CampaignId = Template.UniqueId, + CampaignName = Template.Name, + ExperimentId = currentExperimentTemplate.UniqueId ?? "", + SystemName = "ARES OS", + ExperimentStartTime = DateTime.UtcNow.ToUniversalTime().ToTimestamp() + }; + + var resolveSuccess = await _planningHelper.TryResolveParameters(Template.PlannerAllocations, + metadata, + currentExperimentTemplate.GetAllPlannedParameters(), + analyses, + experimentSummaries.Select(es => es.ExperimentOverview), + token.CancellationToken); - await PostExperimentExecution(experimentSummary); - experimentSummaries.Add(experimentSummary); + if(!resolveSuccess) + { + Status.PlannerState = PlannerState.PlanningError; + _logger.LogDebug("Failed to resolve the experiment template."); } - return executionSuccess; + + return resolveSuccess; } private async Task<(bool Success, bool Continue)> AnalyzeResult(ExperimentExecutor experimentExecutor, ExperimentExecutionSummary experimentSummary, ExperimentExecutionSummary startupSummary, List analyses, ExecutionControlToken token) diff --git a/Ares.Core/Execution/Executors/CommandExecutor.cs b/Ares.Core/Execution/Executors/CommandExecutor.cs index 764a7828..5931e8c3 100644 --- a/Ares.Core/Execution/Executors/CommandExecutor.cs +++ b/Ares.Core/Execution/Executors/CommandExecutor.cs @@ -72,10 +72,8 @@ public async Task Execute(ExecutionControlToken token) Status.State = ExecutionState.Succeeded; else - { Status.State = ExecutionState.Failed; - await _notifier.Notify("Command Execution Failed!", $"ARES failed to execute a command. {result.Error}", NotificationSeverityEnum.Error); - } + _stateSubject.OnNext(Status); _stateSubject.OnCompleted(); diff --git a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs index a8c15659..0d170d6b 100644 --- a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs +++ b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs @@ -24,6 +24,8 @@ public override async Task Execute(ExecutionControlToken t { var startTime = DateTime.UtcNow; var commandSummaries = new List(); + var currentSettings = await _settingsManager.GetAresGeneralSettings(); + foreach (var command in CommandExecutors) { if(token.IsCancelled) @@ -33,16 +35,39 @@ public override async Task Execute(ExecutionControlToken t //Handle Retry if needed var shouldRetry = await ShouldRetry(commandExecutionSummary.StatusCode); + if(!commandExecutionSummary.Result.Success && shouldRetry) { - var msg = $"ARES attempted to run the command {commandExecutionSummary.CommandName} but it failed. Based on your settings ARES will retry running this command"; - await _notifier.Notify("Retrying Command", msg, NotificationSeverityEnum.Info); - await Task.Delay(2000); + var commandRetries = 0; + var retryLimit = currentSettings?.CommandRetryLimit ?? 1; + + while(commandRetries < retryLimit) + { + commandRetries++; + var msg = $"ARES attempted to run the command {commandExecutionSummary.CommandName} but it failed. " + + $"Based on your settings ARES will retry running this command up to {retryLimit} times, this is attempt {commandRetries}"; + + await _notifier.Notify("Retrying Command", msg, NotificationSeverityEnum.Info); + + + if(currentSettings is not null) + await Task.Delay(TimeSpan.FromSeconds(currentSettings.RetryCooldown)); + + var retriedCommandExecutionSummary = await command.Execute(token); - var retriedCommandExecutionSummary = await command.Execute(token); + if(retriedCommandExecutionSummary.Result.Success) + { + commandExecutionSummary = retriedCommandExecutionSummary; + break; + } + } - if(retriedCommandExecutionSummary.Result.Success) - commandExecutionSummary = retriedCommandExecutionSummary; + if(commandRetries == retryLimit && !commandExecutionSummary.Result.Success) + { + await _notifier.Notify("Maximum Command Retries Exceeded", + "ARES retried a failed command based on your settings, but exceeded the maximum number of allowed retries. Execution will stop.", + NotificationSeverityEnum.Error); + } } if(commandExecutionSummary.Result.Success) @@ -61,6 +86,6 @@ public override async Task Execute(ExecutionControlToken t private async Task ShouldRetry(CommandStatusCode code) { var errorHandling = await _settingsManager.GetErrorHandlingByStatusCode(code); - return errorHandling == ErrorHandling.Retry; + return errorHandling == ErrorHandling.RetryCommand; } } diff --git a/Ares.Core/Execution/StopConditions/NumExperimentsRun.cs b/Ares.Core/Execution/StopConditions/NumExperimentsRun.cs index 70835f8c..5d474506 100644 --- a/Ares.Core/Execution/StopConditions/NumExperimentsRun.cs +++ b/Ares.Core/Execution/StopConditions/NumExperimentsRun.cs @@ -17,8 +17,13 @@ public NumExperimentsRun(IExecutionReportStore executionReportStore, uint numExp public bool ShouldStop() { - //Subtract one to offset the startup script, as even when it's empty it's always considered present here - var currentExperiments = _executionReportStore.CampaignExecutionStatus?.ExperimentExecutionStatuses.Count - 1; + var successfullyCompletedExperiments = _executionReportStore.CampaignExecutionStatus?.ExperimentExecutionStatuses + .Where(e => e.StepExecutionStatuses + .All(s => s.CommandExecutionStatuses + .All(c => c.State != Datamodel.ExecutionState.Failed))); + + //Subtract one to offset the startup script, as even when it's empty it's always considered present here + var currentExperiments = successfullyCompletedExperiments?.Count() - 1; return currentExperiments >= _numExperiments; } } diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs index c771b8f8..253b3ed0 100644 --- a/Ares.Core/Settings/SystemSettingsManager.cs +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -24,7 +24,8 @@ public async Task Initialize() UniqueId = Guid.NewGuid().ToString(), CommandLatency = 0, ExperimentRetryLimit = 1, - RetryCooldown = 0 + RetryCooldown = 0, + CommandRetryLimit = 1 }; context.GeneralSettingsConfigs.Add(newGeneralSettingsConfig); @@ -136,6 +137,7 @@ public async Task UpdateAresGeneralSettings(AresGeneralSettingsConfig config) existingConfig.RetryCooldown = config.RetryCooldown; existingConfig.CommandLatency = config.CommandLatency; existingConfig.ExperimentRetryLimit = config.ExperimentRetryLimit; + existingConfig.CommandRetryLimit = config.CommandRetryLimit; await context.SaveChangesAsync(); } diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index f09367f7..ba8422ff 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 5cca4a5e..11403e4a 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index 3853db86..afed0abd 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 5cca4a5e..11403e4a 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Settings/SystemSettingsView.razor b/UI/Features/Settings/SystemSettingsView.razor index f495b519..3b0cb81e 100644 --- a/UI/Features/Settings/SystemSettingsView.razor +++ b/UI/Features/Settings/SystemSettingsView.razor @@ -17,7 +17,13 @@ - + + + + + + + @@ -79,7 +85,8 @@ TooltipOptions Options = new TooltipOptions { Duration = null }; - void ShowRetryLimitTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the max times ARES will retry a failing command or experiment", options); + void ShowExperimentRetryLimitTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the max times ARES will retry a failing experiment", options); + void ShowCommandRetryLimitTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the max times ARES will retry a failing command", options); void ShowRetryCooldownTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets the a cooldown value, in seconds, on how long ARES waits to re-attempt failed commands", options); void ShowCommandLatencyTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, "Sets whether a universal latency should be applied, in seconds, between commands (i.e. a one second universal latency results in a constant one second delay between commands being executed)", options); diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index 9cc3a5fb..9d730d7f 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -29,7 +29,8 @@ await _settingsManager.UpdateAresGeneralSettings(new AresGeneralSettingsConfig { ExperimentRetryLimit = ExperimentRetryLimit, RetryCooldown = ExperimentRetryCooldown, - CommandLatency = CommandLatency + CommandLatency = CommandLatency, + CommandRetryLimit = CommandRetryLimit }); } @@ -46,6 +47,7 @@ public async Task GetUpdatedSettings() ExperimentRetryCooldown = newGeneralSettings.RetryCooldown; ExperimentRetryLimit = newGeneralSettings.ExperimentRetryLimit; CommandLatency = newGeneralSettings.CommandLatency; + CommandRetryLimit = newGeneralSettings.CommandRetryLimit; } } @@ -63,6 +65,9 @@ private void Initialize() [Reactive] public partial int ExperimentRetryLimit { get; set; } + [Reactive] + public partial int CommandRetryLimit { get; set; } + [Reactive] public partial int ExperimentRetryCooldown { get; set; } diff --git a/UI/UI.csproj b/UI/UI.csproj index b6d273e1..d28eefad 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 1f417d7696691fa92d2994dcb8291d9545832cf7 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Fri, 15 May 2026 13:35:46 -0400 Subject: [PATCH 10/25] Basic implementation of the user decision request for when commands fail in an experiment --- Ares.Core.Grpc/Services/AutomationService.cs | 6 ++ Ares.Core/Ares.Core.csproj | 2 +- Ares.Core/Execution/ExecutionManager.cs | 8 ++ .../Execution/Executors/CampaignExecutor.cs | 79 +++++++++++-------- .../Execution/Executors/ICampaignExecutor.cs | 2 + Ares.Core/Execution/IExecutionManager.cs | 6 ++ AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Features/Execution/Execution.razor | 19 +++++ UI/Features/Execution/ExecutionViewModel.cs | 20 +++++ UI/UI.csproj | 2 +- 13 files changed, 114 insertions(+), 38 deletions(-) diff --git a/Ares.Core.Grpc/Services/AutomationService.cs b/Ares.Core.Grpc/Services/AutomationService.cs index 74ae2176..ea546ccb 100644 --- a/Ares.Core.Grpc/Services/AutomationService.cs +++ b/Ares.Core.Grpc/Services/AutomationService.cs @@ -314,6 +314,12 @@ public override Task ResumeExecution(Empty request, ServerCallContext? co return Task.FromResult(new Empty()); } + public override Task SubmitUserDecision(UserDecisionRequest request, ServerCallContext? context) + { + _executionManager.SubmitUserDecision(request.Decision); + return Task.FromResult(new Empty()); + } + public override Task GetAssignedStopConditions(Empty request, ServerCallContext? context) { var conditions = _executionManager.CampaignStopConditions; diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 645176a9..27b39f8a 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/Ares.Core/Execution/ExecutionManager.cs b/Ares.Core/Execution/ExecutionManager.cs index a9671c40..34571449 100644 --- a/Ares.Core/Execution/ExecutionManager.cs +++ b/Ares.Core/Execution/ExecutionManager.cs @@ -24,6 +24,7 @@ public class ExecutionManager : IExecutionManager private readonly INotifier _notifier; private readonly ILogger _logger; private ExecutionControlTokenSource? _executionControlTokenSource; + private ICampaignExecutor? _activeExecutor; public ExecutionManager(IEnumerable startConditions, IDbContextFactory dbContextFactory, @@ -62,6 +63,7 @@ public async Task Start(string executionNotes, List campaignTag throw new InvalidOperationException(err); } var executor = _campaignComposer.Compose(_activeCampaignTemplateStore.CampaignTemplate!); + _activeExecutor = executor; if(!string.IsNullOrEmpty(executionNotes)) executor.UpdateExecutionNotes(executionNotes); @@ -142,11 +144,17 @@ public void UpdateReplanRate(int newRate) ReplanRate = newRate; } + public void SubmitUserDecision(ErrorHandling decision) + { + _activeExecutor?.SubmitUserDecision(decision); + } + private async Task PostExecution(CampaignExecutionSummary result) { await StoreCompletedCampaign(result); _executionControlTokenSource?.Dispose(); _executionControlTokenSource = null; + _activeExecutor = null; } private async Task StoreCompletedCampaign(CampaignExecutionSummary result) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index f12d2a4e..e1513c6f 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -40,6 +40,11 @@ public class CampaignExecutor : ICampaignExecutor private readonly string _campaignFailedTitle = "Campaign Execution Failed!"; private AresGeneralSettingsConfig? _generalSettingsConfig; + private ExperimentExecutorResult _currentExecutorResult = new(); + private ExperimentExecutionSummary? _currentSummary = null; + private ExperimentTemplate? _currentExperimentTemplate = null; + private int _experimentCount = 0; + private TaskCompletionSource? _userDecisionSource; internal CampaignExecutor(ICommandComposer experimentComposer, IPlanningHelper planningHelper, @@ -158,6 +163,7 @@ private async Task InitializeCampaign(DateTime startTime) await CampaignOutputHelper.OutputVersionFile(campaignPath, Template, analyzer); _generalSettingsConfig = await _settingsManager.GetAresGeneralSettings(); + _experimentCount = 0; return campaignPath; } @@ -204,35 +210,30 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); @@ -296,7 +297,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List es.ExperimentOverview).Last().Template.CloneWithNewIds(); + _currentExperimentTemplate = experimentSummaries.Select(es => es.ExperimentOverview).Last().Template.CloneWithNewIds(); _logger.LogDebug("Experiment was already resolved, so ARES cloned the template with new UUID's"); } Status.PlannerState = PlannerState.PlanningComplete; } - if(currentExperimentTemplate is not null && !currentExperimentTemplate.IsEnvironmentResolved()) + if(_currentExperimentTemplate is not null && !_currentExperimentTemplate.IsEnvironmentResolved()) { _logger.LogTrace("Environment variables were not resolved yet. ARES will attempt to resolve the experiments environment variables."); - var resolveVarsSuccess = _variableManager.TryResolveVariable(currentExperimentTemplate.GetAllParameters()); + var resolveVarsSuccess = _variableManager.TryResolveVariable(_currentExperimentTemplate.GetAllParameters()); if(!resolveVarsSuccess) { @@ -389,9 +390,9 @@ private async Task ExecuteExperimentLoop(string campaignPath, List HandleError(CommandExecutionSummary cmdSummar { var errorHandling = await _settingsManager.GetErrorHandlingByStatusCode(cmdSummary.Result.StatusCode); - switch(errorHandling) + if(errorHandling == ErrorHandling.PromptUser) { - case ErrorHandling.PromptUser: - break; + _logger.LogInformation("ARES encountered an error that requires a user decision: {Code}", cmdSummary.Result.StatusCode); + Status.State = ExecutionState.WaitingForUserDecision; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); + + var msg = $"ARES encountered an error in command '{cmdSummary.CommandName}' and requires your decision on how to proceed. " + + $"The error reported was: {cmdSummary.Result.Error}"; + + await _notifier.Notify("User Decision Required!", msg, NotificationSeverityEnum.Warning); + _userDecisionSource = new TaskCompletionSource(); + errorHandling = await _userDecisionSource.Task; + } + + switch(errorHandling) + { case ErrorHandling.Replan: return ExecutionState.Replanning; case ErrorHandling.RetryExperiment: + case ErrorHandling.RetryCommand: return ExecutionState.Retrying; @@ -436,8 +451,6 @@ private async Task HandleError(CommandExecutionSummary cmdSummar default: return ExecutionState.Failed; } - - return ExecutionState.Failed; } private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, ExecutionControlToken token) @@ -614,6 +627,8 @@ private bool ShouldStop() public void UpdateCampaignTags(List tags) => CampaignTags = tags; + public void SubmitUserDecision(ErrorHandling decision) => _userDecisionSource?.TrySetResult(decision); + private static bool IsAwaitingResponse(ExperimentExecutionStatus status) => status.StepExecutionStatuses .Any(step => step.CommandExecutionStatuses diff --git a/Ares.Core/Execution/Executors/ICampaignExecutor.cs b/Ares.Core/Execution/Executors/ICampaignExecutor.cs index bb9ea554..6e56cc87 100644 --- a/Ares.Core/Execution/Executors/ICampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/ICampaignExecutor.cs @@ -10,4 +10,6 @@ public interface ICampaignExecutor : IExecutor campaignTags); + + void SubmitUserDecision(ErrorHandling decision); } diff --git a/Ares.Core/Execution/IExecutionManager.cs b/Ares.Core/Execution/IExecutionManager.cs index 6fc7f22f..866fe287 100644 --- a/Ares.Core/Execution/IExecutionManager.cs +++ b/Ares.Core/Execution/IExecutionManager.cs @@ -54,6 +54,12 @@ public interface IExecutionManager /// void UpdateReplanRate(int newRate); + /// + /// Submits a user decision for how to handle an error that has occurred during execution + /// + /// + void SubmitUserDecision(ErrorHandling decision); + /// /// Checks whether the prerequisites to execution have been met /// diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index ba8422ff..70b658e2 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 11403e4a..47733c01 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index afed0abd..18069fbe 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 11403e4a..47733c01 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Execution/Execution.razor b/UI/Features/Execution/Execution.razor index c9860993..0263d176 100644 --- a/UI/Features/Execution/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -48,6 +48,25 @@
+ @if(ViewModel!.CampaignExecutionState == ExecutionState.WaitingForUserDecision) + { + + +
+
+ Action Required: + An error occurred during execution. Please select how you would like to proceed. +
+
+ + + + +
+
+
+
+ }

Live Execution Data

diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 02b6ff9d..cdf898f9 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -190,6 +190,9 @@ public Task PauseCampaign() public Task ResumeCampaign() => _automationClient.ResumeExecution(new Empty(), null); + public Task SubmitUserDecision(ErrorHandling decision) + => _automationClient.SubmitUserDecision(new UserDecisionRequest { Decision = decision }, null); + public async Task ExecutionNotesUploaded(Stream fileStream) { using var reader = new StreamReader(fileStream); @@ -225,6 +228,20 @@ public Task RequestUserConfirmation() return Task.CompletedTask; } + public Task RequestUserDecision() + { + var notification = new AresNotification(); + notification.NotificationSeverity = Severity.Warning; + notification.Title = "User Decision Required to Proceed"; + notification.Message = $"ARES has encountered an error and requires your decision on how to proceed. Please check the execution screen."; + notification.Timestamp = DateTime.UtcNow.ToTimestamp(); + notification.Loiter = true; + + _notificationService.PushNotification(notification); + + return Task.CompletedTask; + } + public async Task AddTag() { if(NewTagName is not null && AvailableTags.Any(t => t.TagName == NewTagName)) @@ -453,6 +470,9 @@ private void UpdateCampaignStatus(CampaignExecutionState state) //TODO: FIX THIS!!! if(CampaignExecutionState == ExecutionState.AwaitingUser) _ = RequestUserConfirmation(); + + if(CampaignExecutionState == ExecutionState.WaitingForUserDecision) + _ = RequestUserDecision(); } public Task UpdateDeviceChartA() diff --git a/UI/UI.csproj b/UI/UI.csproj index d28eefad..236b4855 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From a792cb80b54cbaefe0f51cbc6f6a0847429831b8 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 18 May 2026 09:36:27 -0400 Subject: [PATCH 11/25] Updates to solidify execution logic in the new "state based" execution method --- .../Execution/Executors/CampaignExecutor.cs | 71 +++++++++++++++---- .../Execution/Executors/CommandExecutor.cs | 1 - .../Execution/Executors/ExperimentExecutor.cs | 2 +- .../StopConditions/NumExperimentsRun.cs | 28 +++++--- Ares.Core/Planning/PlanningHelper.cs | 3 +- UI/Features/Execution/ExecutionViewModel.cs | 17 ----- 6 files changed, 80 insertions(+), 42 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index e1513c6f..df6ede7d 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -271,20 +271,16 @@ private async Task ExecuteExperimentLoop(string campaignPath, List s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); + var failedCommandSummary = _currentSummary.StepSummaries.SelectMany(s => s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); if(failedCommandSummary is not null) currentState = await HandleError(failedCommandSummary); else - { - await PostExperimentExecution(experimentSummary); - experimentSummaries.Add(experimentSummary); - currentState = ExecutionState.InitializeExperiment; - } + currentState = ExecutionState.Analyzing; break; @@ -304,6 +300,10 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List failedExperimentRetryLimit) @@ -340,7 +346,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List HandleError(CommandExecutionSummary cmdSummar switch(errorHandling) { case ErrorHandling.Replan: + Status.State = ExecutionState.Replanning; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); return ExecutionState.Replanning; case ErrorHandling.RetryExperiment: case ErrorHandling.RetryCommand: + Status.State = ExecutionState.Retrying; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); return ExecutionState.Retrying; case ErrorHandling.EnterSafeMode: + Status.State = ExecutionState.EnteringSafeMode; + _executionStatusSubject.OnNext(Status); + _executionReporter.Report(Status); await _executionSafetyManager.EnterSafeMode(); await _notifier.Notify("Safe Mode Activated", "An ARES experiment has failed and based on your settings ARES has entered safe mode in response.", NotificationSeverityEnum.Error); return ExecutionState.Failed; @@ -629,6 +656,22 @@ private bool ShouldStop() public void SubmitUserDecision(ErrorHandling decision) => _userDecisionSource?.TrySetResult(decision); + private void ResetCurrentExperimentStatus() + { + if(_currentExecutorResult?.ExperimentExecutor?.Status == null) + return; + + foreach(var step in _currentExecutorResult.ExperimentExecutor.Status.StepExecutionStatuses) + { + foreach(var cmd in step.CommandExecutionStatuses) + { + cmd.State = ExecutionState.Waiting; + } + } + + _executionReporter.Report(_currentExecutorResult.ExperimentExecutor.Status); + } + private static bool IsAwaitingResponse(ExperimentExecutionStatus status) => status.StepExecutionStatuses .Any(step => step.CommandExecutionStatuses @@ -712,8 +755,12 @@ private async Task GenerateExperimentExecutor(Experime private async Task ExecuteTemplate(ExperimentExecutor experimentExecutor, ExecutionControlToken token) { - Status.ExperimentExecutionStatuses.Add(experimentExecutor.Status); - experimentExecutor.ExperimentStatusObservable.Subscribe(experimentStatus => + if(!Status.ExperimentExecutionStatuses.Any(s => s.ExperimentId == experimentExecutor.Status.ExperimentId)) + { + Status.ExperimentExecutionStatuses.Add(experimentExecutor.Status); + } + + using var statusSub = experimentExecutor.ExperimentStatusObservable.Subscribe(experimentStatus => { _executionReporter.Report(experimentStatus); diff --git a/Ares.Core/Execution/Executors/CommandExecutor.cs b/Ares.Core/Execution/Executors/CommandExecutor.cs index 5931e8c3..e0deb0e0 100644 --- a/Ares.Core/Execution/Executors/CommandExecutor.cs +++ b/Ares.Core/Execution/Executors/CommandExecutor.cs @@ -76,7 +76,6 @@ public async Task Execute(ExecutionControlToken token) _stateSubject.OnNext(Status); - _stateSubject.OnCompleted(); return ExecutorSummaryHelpers.CreateCommandExecutionSummary(Template, result, timeStarted, DateTime.UtcNow); } diff --git a/Ares.Core/Execution/Executors/ExperimentExecutor.cs b/Ares.Core/Execution/Executors/ExperimentExecutor.cs index 1e57966b..2503071e 100644 --- a/Ares.Core/Execution/Executors/ExperimentExecutor.cs +++ b/Ares.Core/Execution/Executors/ExperimentExecutor.cs @@ -29,7 +29,7 @@ public ExperimentExecutor(ExperimentTemplate template, IExecutor $"Campaign will stop after {_numExperiments} runs."; - public bool ShouldStop() - { - var successfullyCompletedExperiments = _executionReportStore.CampaignExecutionStatus?.ExperimentExecutionStatuses - .Where(e => e.StepExecutionStatuses - .All(s => s.CommandExecutionStatuses - .All(c => c.State != Datamodel.ExecutionState.Failed))); + public bool ShouldStop() + { + var experiments = _executionReportStore.CampaignExecutionStatus?.ExperimentExecutionStatuses ?? Enumerable.Empty(); + + //We skip one to account for the startup script summary + var successCount = experiments + .Skip(1) + .Count(e => + e.StepExecutionStatuses?.Any() == true && + e.StepExecutionStatuses.All(s => + s.CommandExecutionStatuses != null && + s.CommandExecutionStatuses.All(c => c.State == Datamodel.ExecutionState.Succeeded) + ) + ); - //Subtract one to offset the startup script, as even when it's empty it's always considered present here - var currentExperiments = successfullyCompletedExperiments?.Count() - 1; - return currentExperiments >= _numExperiments; + return successCount >= _numExperiments; } } diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 31f12073..5b99f0a9 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -119,7 +119,8 @@ public async Task TryResolveParameters(IEnumerable plan } catch(Exception e) { - _logger.LogError("Failed to plan. {}", e); + _logger.LogError("Failed to plan. {}", e.Message); + await _notifier.Notify("Planner Error!", e.Message, NotificationSeverityEnum.Error); return false; } diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index cdf898f9..def4eca4 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -228,20 +228,6 @@ public Task RequestUserConfirmation() return Task.CompletedTask; } - public Task RequestUserDecision() - { - var notification = new AresNotification(); - notification.NotificationSeverity = Severity.Warning; - notification.Title = "User Decision Required to Proceed"; - notification.Message = $"ARES has encountered an error and requires your decision on how to proceed. Please check the execution screen."; - notification.Timestamp = DateTime.UtcNow.ToTimestamp(); - notification.Loiter = true; - - _notificationService.PushNotification(notification); - - return Task.CompletedTask; - } - public async Task AddTag() { if(NewTagName is not null && AvailableTags.Any(t => t.TagName == NewTagName)) @@ -470,9 +456,6 @@ private void UpdateCampaignStatus(CampaignExecutionState state) //TODO: FIX THIS!!! if(CampaignExecutionState == ExecutionState.AwaitingUser) _ = RequestUserConfirmation(); - - if(CampaignExecutionState == ExecutionState.WaitingForUserDecision) - _ = RequestUserDecision(); } public Task UpdateDeviceChartA() From d9b1fc1091a6327441349bc36759cbd3e865e54b Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 18 May 2026 10:27:18 -0400 Subject: [PATCH 12/25] Updates to support durations in settings values --- Ares.Core/Ares.Core.csproj | 2 +- Ares.Core/Execution/Executors/CampaignExecutor.cs | 6 +++--- Ares.Core/Execution/Executors/SequentialStepExecutor.cs | 2 +- Ares.Core/Settings/SystemSettingsManager.cs | 9 +++++---- AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/Features/Settings/SystemSettingsViewModel.cs | 7 ++++--- UI/UI.csproj | 2 +- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 27b39f8a..e98f9d57 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index df6ede7d..d869bdb1 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -214,7 +214,7 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List Execute(ExecutionControlToken t if(currentSettings is not null) - await Task.Delay(TimeSpan.FromSeconds(currentSettings.RetryCooldown)); + await Task.Delay(currentSettings.RetryCooldown.ToTimeSpan()); var retriedCommandExecutionSummary = await command.Execute(token); diff --git a/Ares.Core/Settings/SystemSettingsManager.cs b/Ares.Core/Settings/SystemSettingsManager.cs index 253b3ed0..1c62c162 100644 --- a/Ares.Core/Settings/SystemSettingsManager.cs +++ b/Ares.Core/Settings/SystemSettingsManager.cs @@ -1,4 +1,5 @@ using Ares.Datamodel; +using Google.Protobuf.WellKnownTypes; using Microsoft.EntityFrameworkCore; namespace Ares.Core.Settings; @@ -18,13 +19,13 @@ public async Task Initialize() var existingGeneralSettings = await context.GeneralSettingsConfigs.FirstOrDefaultAsync(); if(existingGeneralSettings is null) - { + { var newGeneralSettingsConfig = new AresGeneralSettingsConfig() { UniqueId = Guid.NewGuid().ToString(), - CommandLatency = 0, + CommandLatency = new Duration() { Seconds = 0 }, ExperimentRetryLimit = 1, - RetryCooldown = 0, + RetryCooldown = new Duration() { Seconds = 0 }, CommandRetryLimit = 1 }; @@ -32,7 +33,7 @@ public async Task Initialize() } var existingErrorHandling = await context.DeviceErrorHandlingConfigs.ToListAsync(); - var allEnumValues = Enum.GetValues(); + var allEnumValues = System.Enum.GetValues(); if(existingErrorHandling.Count != allEnumValues.Length) { diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index 70b658e2..ef5fb4fd 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 47733c01..f268fdd3 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index 18069fbe..d1d1eb41 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 47733c01..f268fdd3 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index 9d730d7f..44228cfb 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -1,5 +1,6 @@ using Ares.Core.Settings; using Ares.Datamodel; +using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; @@ -53,7 +54,7 @@ public async Task GetUpdatedSettings() private void Initialize() { - foreach(var status in Enum.GetValues()) + foreach(var status in System.Enum.GetValues()) { if(!CurrentErrorHandlingSettings.TryGetValue(status, out var val)) CurrentErrorHandlingSettings[status] = ErrorHandling.UnknownHandling; @@ -69,8 +70,8 @@ private void Initialize() public partial int CommandRetryLimit { get; set; } [Reactive] - public partial int ExperimentRetryCooldown { get; set; } + public partial Duration ExperimentRetryCooldown { get; set; } [Reactive] - public partial int CommandLatency { get; set; } + public partial Duration CommandLatency { get; set; } } diff --git a/UI/UI.csproj b/UI/UI.csproj index 236b4855..2e7f03e9 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 612daa3c6d18101f290d900ddb1b79f6c590aa17 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 18 May 2026 10:53:24 -0400 Subject: [PATCH 13/25] Updated entity configurations --- .../Helpers/AresValueConverters.cs | 4 ---- .../Helpers/DurationSerializationHelper.cs | 18 ++++++++++++++++++ .../Helpers/EfCoreValueConverters.cs | 1 - .../GeneralSettingsEntityConfiguration.cs | 6 +++++- 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 Ares.Core/EntityConfigurations/Helpers/DurationSerializationHelper.cs diff --git a/Ares.Core/EntityConfigurations/Helpers/AresValueConverters.cs b/Ares.Core/EntityConfigurations/Helpers/AresValueConverters.cs index 22338d8a..71c26306 100644 --- a/Ares.Core/EntityConfigurations/Helpers/AresValueConverters.cs +++ b/Ares.Core/EntityConfigurations/Helpers/AresValueConverters.cs @@ -24,11 +24,7 @@ public AresValueConverter() : base( public class AresTimestampConverter : ValueConverter { public AresTimestampConverter() : base( - // C# to Database: Convert Protobuf Timestamp to standard C# DateTime protobufTimestamp => protobufTimestamp.ToDateTime(), - - // Database to C#: Convert Database DateTime back to Protobuf Timestamp - // We MUST specify the Kind as UTC or Protobuf will complain dateTime => Timestamp.FromDateTime(DateTime.SpecifyKind(dateTime, DateTimeKind.Utc))) { } } diff --git a/Ares.Core/EntityConfigurations/Helpers/DurationSerializationHelper.cs b/Ares.Core/EntityConfigurations/Helpers/DurationSerializationHelper.cs new file mode 100644 index 00000000..a9115c9f --- /dev/null +++ b/Ares.Core/EntityConfigurations/Helpers/DurationSerializationHelper.cs @@ -0,0 +1,18 @@ +using Google.Protobuf.WellKnownTypes; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.Text.Json; + +namespace Ares.Core.EntityConfigurations.Helpers; + +public static class DurationSerializationHelper +{ + public static PropertyBuilder HasDuration(this PropertyBuilder value) + { + var settings = SerializerSettingsHelper.CreateCustomSerializationSettings(); + return value.HasConversion( + v => JsonSerializer.Serialize(v, settings), + v => JsonSerializer.Deserialize(v, settings) ?? new Duration()) + .HasColumnType(SerializerSettingsHelper.DetermineColumnType()); + } +} diff --git a/Ares.Core/EntityConfigurations/Helpers/EfCoreValueConverters.cs b/Ares.Core/EntityConfigurations/Helpers/EfCoreValueConverters.cs index b37b8032..ae42db00 100644 --- a/Ares.Core/EntityConfigurations/Helpers/EfCoreValueConverters.cs +++ b/Ares.Core/EntityConfigurations/Helpers/EfCoreValueConverters.cs @@ -1,6 +1,5 @@ using System.Text.Json; using Google.Protobuf.Collections; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; diff --git a/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs index 8618db79..aab3672a 100644 --- a/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs +++ b/Ares.Core/EntityConfigurations/Settings/GeneralSettingsEntityConfiguration.cs @@ -1,4 +1,5 @@ -using Ares.Datamodel; +using Ares.Core.EntityConfigurations.Helpers; +using Ares.Datamodel; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -9,6 +10,9 @@ internal class GeneralSettingsEntityConfiguration : AresEntityTypeBaseConfigurat public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); + builder.Property(b => b.CommandLatency).HasDuration(); + builder.Property(b => b.RetryCooldown).HasDuration(); + builder.ToTable("AresGeneralSettingsConfig"); } } From 3f1c39fe462b0b0c2e51035e1f182a883d090a3b Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Mon, 18 May 2026 11:01:27 -0400 Subject: [PATCH 14/25] Updated system settings to handle new duration type gracefully --- UI/Features/Settings/SystemSettingsViewModel.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index 44228cfb..fc13d66a 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -29,8 +29,8 @@ public async Task PushUpdatedSettings() await _settingsManager.UpdateAresGeneralSettings(new AresGeneralSettingsConfig { ExperimentRetryLimit = ExperimentRetryLimit, - RetryCooldown = ExperimentRetryCooldown, - CommandLatency = CommandLatency, + RetryCooldown = new Duration() { Seconds = ExperimentRetryCooldown }, + CommandLatency = new Duration() { Seconds = CommandLatency }, CommandRetryLimit = CommandRetryLimit }); } @@ -45,9 +45,9 @@ public async Task GetUpdatedSettings() if(newGeneralSettings is not null) { - ExperimentRetryCooldown = newGeneralSettings.RetryCooldown; + ExperimentRetryCooldown = (int)newGeneralSettings.RetryCooldown.Seconds; ExperimentRetryLimit = newGeneralSettings.ExperimentRetryLimit; - CommandLatency = newGeneralSettings.CommandLatency; + CommandLatency = (int)newGeneralSettings.CommandLatency.Seconds; CommandRetryLimit = newGeneralSettings.CommandRetryLimit; } } @@ -70,8 +70,8 @@ private void Initialize() public partial int CommandRetryLimit { get; set; } [Reactive] - public partial Duration ExperimentRetryCooldown { get; set; } + public partial int ExperimentRetryCooldown { get; set; } [Reactive] - public partial Duration CommandLatency { get; set; } + public partial int CommandLatency { get; set; } } From a83c5b0973bd2dd6d5ff3cab3c1076ee65cc1fcc Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 3 Jun 2026 16:42:23 -0400 Subject: [PATCH 15/25] Fixed some tests --- .../Execution/CommandVariableResolutionTests.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs b/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs index b329fde6..11e3c8ef 100644 --- a/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs +++ b/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs @@ -3,6 +3,7 @@ using Ares.Core.Execution.Executors; using Ares.Core.Execution.Executors.Composers; using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Device; using Ares.Datamodel.Templates; @@ -15,6 +16,14 @@ namespace Ares.Core.Tests.Execution; internal class CommandVariableResolutionTests { + private ISystemSettingsManager _systemSettingsManager; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _systemSettingsManager = new Mock().Object; + } + [Test] public async Task SequentialStepExecutor_ResolvesVariableParameterFromEarlierCommandOutput() { @@ -26,7 +35,7 @@ public async Task SequentialStepExecutor_ResolvesVariableParameterFromEarlierCom stepTemplate.CommandTemplates.Add(CreateSourceCommand()); stepTemplate.CommandTemplates.Add(CreateConsumerCommand("sourceResult")); - var stepComposer = new StepComposer(deviceRepo, new Mock().Object); + var stepComposer = new StepComposer(deviceRepo, new Mock().Object, _systemSettingsManager); var stepExecutor = stepComposer.Compose(stepTemplate); using var tokenSource = new ExecutionControlTokenSource(); @@ -52,7 +61,8 @@ public async Task CommandExecutor_FailsVariableParameterWhenVariableIsMissing() return Task.FromResult(new CommandResult { Success = true }); }, template, - new Mock().Object); + new Mock().Object, + _systemSettingsManager); using var tokenSource = new ExecutionControlTokenSource(); var summary = await executor.Execute(tokenSource.Token); From 271774bcaaf42393389abe8afc0473b0209c017a Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 10:11:26 -0400 Subject: [PATCH 16/25] Fixed some bugs in the retry command logic. Added notifications to confirm settings were saved --- .../Execution/Executors/CampaignExecutor.cs | 5 +- .../Executors/SequentialStepExecutor.cs | 10 ++-- .../Settings/SystemSettingsViewModel.cs | 47 +++++++++++++------ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index d869bdb1..48240b59 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -276,6 +276,9 @@ private async Task ExecuteExperimentLoop(string campaignPath, List s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); + if(!_currentSummary.StepSummaries.Any()) + currentState = ExecutionState.Failed; + if(failedCommandSummary is not null) currentState = await HandleError(failedCommandSummary); @@ -460,7 +463,6 @@ private async Task HandleError(CommandExecutionSummary cmdSummar return ExecutionState.Replanning; case ErrorHandling.RetryExperiment: - case ErrorHandling.RetryCommand: Status.State = ExecutionState.Retrying; _executionStatusSubject.OnNext(Status); _executionReporter.Report(Status); @@ -475,6 +477,7 @@ private async Task HandleError(CommandExecutionSummary cmdSummar await _notifier.Notify("Safe Mode Activated", "An ARES experiment has failed and based on your settings ARES has entered safe mode in response.", NotificationSeverityEnum.Error); return ExecutionState.Failed; + case ErrorHandling.RetryCommand: // We handle command retries at the step execution level, so we end the campaign if it reaches this point default: return ExecutionState.Failed; } diff --git a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs index 3ef9beb4..895fb03b 100644 --- a/Ares.Core/Execution/Executors/SequentialStepExecutor.cs +++ b/Ares.Core/Execution/Executors/SequentialStepExecutor.cs @@ -72,20 +72,18 @@ await _notifier.Notify("Maximum Command Retries Exceeded", "ARES retried a failed command based on your settings, but exceeded the maximum number of allowed retries. Execution will stop.", NotificationSeverityEnum.Error); } - } + } + + commandSummaries.Add(commandExecutionSummary); if(commandExecutionSummary.Result.Success) { - commandSummaries.Add(commandExecutionSummary); - foreach(var variable in CommandVariableResolver.CreateVariableScope([commandExecutionSummary])) combinedScope[variable.Key] = variable.Value; } - - else - return ExecutorSummaryHelpers.CreateEmptyStepExecutionSummary(startTime, DateTime.UtcNow); + return ExecutorSummaryHelpers.CreateStepExecutionSummary(startTime, DateTime.UtcNow, commandSummaries); } return ExecutorSummaryHelpers.CreateStepExecutionSummary(startTime, DateTime.UtcNow, commandSummaries); diff --git a/UI/Features/Settings/SystemSettingsViewModel.cs b/UI/Features/Settings/SystemSettingsViewModel.cs index fc13d66a..d58a25fb 100644 --- a/UI/Features/Settings/SystemSettingsViewModel.cs +++ b/UI/Features/Settings/SystemSettingsViewModel.cs @@ -1,4 +1,5 @@ -using Ares.Core.Settings; +using Ares.Core.Notifications; +using Ares.Core.Settings; using Ares.Datamodel; using Google.Protobuf.WellKnownTypes; using ReactiveUI; @@ -9,30 +10,46 @@ namespace UI.Features.Settings; public partial class SystemSettingsViewModel : ReactiveObject { private readonly ISystemSettingsManager _settingsManager; + private readonly INotificationHandler _notificationHandler; + private readonly ILogger _logger; - public SystemSettingsViewModel(ISystemSettingsManager settingsManager) + public SystemSettingsViewModel(ISystemSettingsManager settingsManager, INotificationHandler notificationHandler, ILogger logger) { _settingsManager = settingsManager; + _notificationHandler = notificationHandler; CurrentErrorHandlingSettings = new(); Initialize(); + _logger = logger; } public async Task PushUpdatedSettings() { - var configs = CurrentErrorHandlingSettings.Select(kvp => new DeviceErrorHandlingConfig() - { - Code = kvp.Key, - Handling = kvp.Value } - ).ToList(); - - await _settingsManager.UpdateErrorHandlingSettings(configs); - await _settingsManager.UpdateAresGeneralSettings(new AresGeneralSettingsConfig + try { - ExperimentRetryLimit = ExperimentRetryLimit, - RetryCooldown = new Duration() { Seconds = ExperimentRetryCooldown }, - CommandLatency = new Duration() { Seconds = CommandLatency }, - CommandRetryLimit = CommandRetryLimit - }); + var configs = CurrentErrorHandlingSettings.Select(kvp => new DeviceErrorHandlingConfig() + { + Code = kvp.Key, + Handling = kvp.Value + }).ToList(); + + await _settingsManager.UpdateErrorHandlingSettings(configs); + await _settingsManager.UpdateAresGeneralSettings(new AresGeneralSettingsConfig + { + ExperimentRetryLimit = ExperimentRetryLimit, + RetryCooldown = new Duration() { Seconds = ExperimentRetryCooldown }, + CommandLatency = new Duration() { Seconds = CommandLatency }, + CommandRetryLimit = CommandRetryLimit + }); + + await _notificationHandler.HandleNotification("Settings Updated!", "ARES successfully updated your settings.", NotificationSeverityEnum.Success); + _logger.LogInformation("ARES settings successfully updated"); + } + + catch(Exception e) + { + await _notificationHandler.HandleNotification("Failed to Update Settings", $"ARES couldn't update your settings. {e.Message}", NotificationSeverityEnum.Success); + _logger.LogError("ARES failed to update settings. Error: {error}", e.Message); + } } public async Task GetUpdatedSettings() From 0564bc15a9b86d3f35ef5e97ed6dba1c350220a7 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 11:16:04 -0400 Subject: [PATCH 17/25] Updates to make the notification stream a bit more stable and thread safe. Also prompt user for decision when receiving the default (unset) status code from a command failure. --- .../Execution/Executors/CampaignExecutor.cs | 2 +- .../INotificationReceivingService.cs | 2 ++ UI/Components/Layouts/ErrorLayout.razor | 5 ----- UI/Components/Layouts/MainLayout.razor | 20 +++++++++++++------ .../NotificationReceivingService.cs | 8 +++++--- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 48240b59..df8e036d 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -438,7 +438,7 @@ private async Task HandleError(CommandExecutionSummary cmdSummar { var errorHandling = await _settingsManager.GetErrorHandlingByStatusCode(cmdSummary.Result.StatusCode); - if(errorHandling == ErrorHandling.PromptUser) + if(errorHandling == ErrorHandling.PromptUser || cmdSummary.Result.StatusCode == CommandStatusCode.StatusUnspecified) { _logger.LogInformation("ARES encountered an error that requires a user decision: {Code}", cmdSummary.Result.StatusCode); Status.State = ExecutionState.WaitingForUserDecision; diff --git a/UI/Application/Notifications/INotificationReceivingService.cs b/UI/Application/Notifications/INotificationReceivingService.cs index c2647390..aed551ab 100644 --- a/UI/Application/Notifications/INotificationReceivingService.cs +++ b/UI/Application/Notifications/INotificationReceivingService.cs @@ -9,5 +9,7 @@ public interface INotificationReceivingService // to some sort of app-specific implementation so we're not depending on datamodel in our // Application layer void PushNotification(AresNotification notification); + + event Action? OnNotificationReceived; } diff --git a/UI/Components/Layouts/ErrorLayout.razor b/UI/Components/Layouts/ErrorLayout.razor index e4764d16..bfa18321 100644 --- a/UI/Components/Layouts/ErrorLayout.razor +++ b/UI/Components/Layouts/ErrorLayout.razor @@ -6,11 +6,6 @@ ARES
- - - - -
@Body Go Home diff --git a/UI/Components/Layouts/MainLayout.razor b/UI/Components/Layouts/MainLayout.razor index 7bff1e65..426a0137 100644 --- a/UI/Components/Layouts/MainLayout.razor +++ b/UI/Components/Layouts/MainLayout.razor @@ -1,3 +1,5 @@ +@implements IDisposable + @using Ares.Core.Device.Providers @using Ares.Core.Execution.Safety @using Ares.Core.Device.Repos @@ -11,6 +13,7 @@ @inject IUiDialogService dialogService @inject INotificationReceivingService notificationService +@inject IUiNotificationService uiNotificationService @inject IExecutionSafetyManager executionSafetyManager @inject IDeviceDriverProvider deviceDriverRepo @inject StartupStateTracker Tracker @@ -25,11 +28,7 @@ ARES
- - - -
@* @@ -142,11 +141,19 @@ protected override void OnInitialized() { Tracker.OnSystemReady += HandleSystemReady; - - // Fire and forget the message cycler + notificationService.OnNotificationReceived += HandleBackgroundNotification; _ = CycleLoadingMessagesAsync(_cts.Token); } + private void HandleBackgroundNotification(UiNotificationMessage message) + { + InvokeAsync(() => + { + uiNotificationService.Notify(message); + StateHasChanged(); + }); + } + private async Task CycleLoadingMessagesAsync(CancellationToken token) { var random = new Random(); @@ -238,6 +245,7 @@ public void Dispose() { Tracker.OnSystemReady -= HandleSystemReady; + notificationService.OnNotificationReceived -= HandleBackgroundNotification; _cts.Cancel(); _cts.Dispose(); } diff --git a/UI/Infrastructure/Notifications/NotificationReceivingService.cs b/UI/Infrastructure/Notifications/NotificationReceivingService.cs index cd34cb06..16fa01f1 100644 --- a/UI/Infrastructure/Notifications/NotificationReceivingService.cs +++ b/UI/Infrastructure/Notifications/NotificationReceivingService.cs @@ -1,7 +1,6 @@ using Ares.Services; using Ares.Core.Grpc.Services.Notifications; using Google.Protobuf.WellKnownTypes; -using Grpc.Core; using NuGet.Packaging; using UI.Application.Notifications; using UI.Infrastructure.Grpc; @@ -13,6 +12,7 @@ public class NotificationReceivingService : INotificationReceivingService private readonly AresNotificationService _notificationClient; private readonly IUiNotificationService _uiNotificationService; private readonly INotificationRepository _notificationRepo; + public event Action? OnNotificationReceived; public NotificationReceivingService( AresNotificationService notificationClient, @@ -47,8 +47,9 @@ public void StartNotificationStream() CloseOnClick = notification.NotificationSeverity == Severity.Danger }; - _uiNotificationService.Notify(userNotification); + //_uiNotificationService.Notify(userNotification); _notificationRepo.Add(notification); + OnNotificationReceived?.Invoke(userNotification); } } catch(Exception ex) @@ -75,8 +76,9 @@ public void PushNotification(AresNotification notification) if(notification.Timestamp is null) notification.Timestamp = DateTime.UtcNow.ToTimestamp(); - _uiNotificationService.Notify(uiNotification); + //_uiNotificationService.Notify(uiNotification); _notificationRepo.Add(notification); + OnNotificationReceived?.Invoke(uiNotification); } public async Task GetLatestNotificationHistory() From 6531f6ca381146256fde01d70eb2cfffec897a13 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 11:47:37 -0400 Subject: [PATCH 18/25] Report status consistently --- Ares.Core/Execution/Executors/CampaignExecutor.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index df8e036d..09e01807 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -218,6 +218,9 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List failedExperimentRetryLimit) From 4519ca9fe86728ea0e95fed7023e0b425bc950ef Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 11:49:34 -0400 Subject: [PATCH 19/25] Migration updates --- ...handling-updates_AresDbContext.Designer.cs | 2061 ++++++++++++++++ ...58_error-handling-updates_AresDbContext.cs | 57 + ...ng-updates_AresIdentityContext.Designer.cs | 277 +++ ...or-handling-updates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 62 +- .../AresIdentityContextModelSnapshot.cs | 2 +- ...handling-updates_AresDbContext.Designer.cs | 2073 +++++++++++++++++ ...04_error-handling-updates_AresDbContext.cs | 57 + ...ng-updates_AresIdentityContext.Designer.cs | 279 +++ ...or-handling-updates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 62 +- .../AresIdentityContextModelSnapshot.cs | 2 +- ...handling-updates_AresDbContext.Designer.cs | 2056 ++++++++++++++++ ...52_error-handling-updates_AresDbContext.cs | 57 + ...ng-updates_AresIdentityContext.Designer.cs | 268 +++ ...or-handling-updates_AresIdentityContext.cs | 22 + .../Migrations/AresDbContextModelSnapshot.cs | 62 +- .../AresIdentityContextModelSnapshot.cs | 2 +- 18 files changed, 7437 insertions(+), 6 deletions(-) create mode 100644 AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.Designer.cs create mode 100644 AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.cs diff --git a/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.Designer.cs new file mode 100644 index 00000000..549fd90b --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.Designer.cs @@ -0,0 +1,2061 @@ +// +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("20260603203658_error-handling-updates_AresDbContext")] + partial class errorhandlingupdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfo") + .HasColumnType("jsonb"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.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.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalysisRequest") + .HasColumnType("jsonb"); + + b.Property("AnalysisResponse") + .HasColumnType("jsonb"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("AnalyzerName") + .HasColumnType("text"); + + b.Property("AnalyzerType") + .HasColumnType("text"); + + b.Property("AnalyzerVersion") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("TagName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandLatency") + .HasColumnType("jsonb"); + + b.Property("CommandRetryLimit") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("RetryCooldown") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignId") + .HasColumnType("text"); + + b.Property("CampaignName") + .HasColumnType("text"); + + b.Property("CampaignNotes") + .HasColumnType("text"); + + b.Property("CampaignTags") + .HasColumnType("text"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandId") + .HasColumnType("text"); + + b.Property("CommandName") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceName") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uuid"); + + 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("LoggingType") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BaudRate") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PortName") + .HasColumnType("text"); + + b.Property("SerialId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Handling") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LocaltimeOffset") + .HasColumnType("text"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uuid"); + + b.Property("TimeFinished") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeStarted") + .HasColumnType("timestamp with time zone"); + + b.Property("Timezone") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentId") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ResultOutputPath") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentResultId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Result") + .HasColumnType("text"); + + b.Property("TemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Maximum") + .HasColumnType("double precision"); + + b.Property("Minimum") + .HasColumnType("double precision"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uuid"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uuid"); + + b.Property("PlannerId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerInfoId") + .HasColumnType("uuid"); + + b.Property("ServiceName") + .HasColumnType("text"); + + b.Property("SettingsSchema") + .HasColumnType("text"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("PlannerId") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("PlannerType") + .HasColumnType("text"); + + b.Property("PlannerVersion") + .HasColumnType("text"); + + b.Property("PlanningRequest") + .HasColumnType("jsonb"); + + b.Property("PlanningResponse") + .HasColumnType("jsonb"); + + b.Property("TimeRequestSent") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeResponseReceived") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.Property("StepName") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uuid"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("StepId") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceType") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("OutputVarName") + .HasColumnType("text"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uuid"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnalyzerId") + .HasColumnType("text"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uuid"); + + b.Property("CampaignExperimentId") + .HasColumnType("uuid"); + + b.Property("CampaignStartupId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Resolved") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataSchema") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("SourceJson") + .HasColumnType("jsonb") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("text"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NotPlannable") + .HasColumnType("boolean"); + + b.Property("OutputName") + .HasColumnType("text"); + + b.Property("ParameterId") + .HasColumnType("uuid"); + + b.Property("PlannerDescription") + .HasColumnType("text"); + + b.Property("PlannerName") + .HasColumnType("text"); + + b.Property("Schema") + .HasColumnType("text"); + + b.Property("Unit") + .HasColumnType("text"); + + b.Property("UseDefault") + .HasColumnType("boolean"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChartTitle") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("GridH") + .HasColumnType("integer"); + + b.Property("GridW") + .HasColumnType("integer"); + + b.Property("GridX") + .HasColumnType("integer"); + + b.Property("GridY") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("integer"); + + b.Property("Paths") + .HasColumnType("jsonb"); + + b.Property("PollingRate") + .HasColumnType("integer"); + + b.Property("ShowDataLabels") + .HasColumnType("boolean"); + + b.Property("ShowMarkers") + .HasColumnType("boolean"); + + b.Property("Style") + .HasColumnType("integer"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AssociatedDeviceId") + .HasColumnType("text"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DataType") + .HasColumnType("integer"); + + b.Property("IsPlottable") + .HasColumnType("boolean"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Path") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Version") + .HasColumnType("text"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.cs b/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.cs new file mode 100644 index 00000000..bc43bd97 --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260603203658_error-handling-updates_AresDbContext.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresDb +{ + /// + public partial class errorhandlingupdates_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AresGeneralSettingsConfig", + columns: table => new + { + UniqueId = table.Column(type: "uuid", nullable: false), + ExperimentRetryLimit = table.Column(type: "integer", nullable: false), + CommandRetryLimit = table.Column(type: "integer", nullable: false), + RetryCooldown = table.Column(type: "jsonb", nullable: true), + CommandLatency = table.Column(type: "jsonb", 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_AresGeneralSettingsConfig", x => x.UniqueId); + }); + + migrationBuilder.CreateTable( + name: "ErrorHandlingConfigs", + columns: table => new + { + UniqueId = table.Column(type: "uuid", nullable: false), + Code = table.Column(type: "integer", nullable: false), + Handling = table.Column(type: "integer", nullable: false), + 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_ErrorHandlingConfigs", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AresGeneralSettingsConfig"); + + migrationBuilder.DropTable( + name: "ErrorHandlingConfigs"); + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.Designer.cs b/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..442c8d4b --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_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("20260603203701_error-handling-updates_AresIdentityContext")] + partial class errorhandlingupdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.cs b/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.cs new file mode 100644 index 00000000..5be6ca67 --- /dev/null +++ b/AresService.Migrations.Postgres/Migrations/20260603203701_error-handling-updates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Postgres.Migrations.AresIdentity +{ + /// + public partial class errorhandlingupdates_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 fe01885c..2b654457 100644 --- a/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Postgres/Migrations/AresDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -277,6 +277,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("CampaignTags"); }); + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CommandLatency") + .HasColumnType("jsonb"); + + b.Property("CommandRetryLimit") + .HasColumnType("integer"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("integer"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("RetryCooldown") + .HasColumnType("jsonb"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => { b.Property("UniqueId") @@ -710,6 +743,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") diff --git a/AresService.Migrations.Postgres/Migrations/AresIdentityContextModelSnapshot.cs b/AresService.Migrations.Postgres/Migrations/AresIdentityContextModelSnapshot.cs index c568aaa4..b2ccb6b5 100644 --- a/AresService.Migrations.Postgres/Migrations/AresIdentityContextModelSnapshot.cs +++ b/AresService.Migrations.Postgres/Migrations/AresIdentityContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); diff --git a/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.Designer.cs new file mode 100644 index 00000000..e4e3a7e4 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.Designer.cs @@ -0,0 +1,2073 @@ +// +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("20260603203704_error-handling-updates_AresDbContext")] + partial class errorhandlingupdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfo") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.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.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalysisRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalysisResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerName") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerType") + .HasColumnType("nvarchar(max)"); + + b.Property("AnalyzerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("TagName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandLatency") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandRetryLimit") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("RetryCooldown") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignId") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignName") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignNotes") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignTags") + .HasColumnType("nvarchar(max)"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandId") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("State") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionStatusUniqueId") + .HasColumnType("uniqueidentifier"); + + 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("LoggingType") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BaudRate") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PortName") + .HasColumnType("nvarchar(max)"); + + b.Property("SerialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Handling") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LocaltimeOffset") + .HasColumnType("nvarchar(max)"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("uniqueidentifier"); + + b.Property("TimeFinished") + .HasColumnType("datetime2"); + + b.Property("TimeStarted") + .HasColumnType("datetime2"); + + b.Property("Timezone") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique() + .HasFilter("[CampaignExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique() + .HasFilter("[CommandExecutionSummaryId] IS NOT NULL"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique() + .HasFilter("[StepExecutionSummaryId] IS NOT NULL"); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentId") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ResultOutputPath") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentResultId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Result") + .HasColumnType("nvarchar(max)"); + + b.Property("TemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique() + .HasFilter("[ExperimentResultId] IS NOT NULL"); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Maximum") + .HasColumnType("float"); + + b.Property("Minimum") + .HasColumnType("float"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ParameterUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("ServiceName") + .HasColumnType("nvarchar(max)"); + + b.Property("SettingsSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeoutSeconds") + .HasColumnType("bigint"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("PlannerId") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerType") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningRequest") + .HasColumnType("nvarchar(max)"); + + b.Property("PlanningResponse") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeRequestSent") + .HasColumnType("datetime2"); + + b.Property("TimeResponseReceived") + .HasColumnType("datetime2"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.Property("StepName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("StepId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceType") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("OutputVarName") + .HasColumnType("nvarchar(max)"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnalyzerId") + .HasColumnType("nvarchar(max)"); + + b.Property("CampaignCloseoutId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignExperimentId") + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignStartupId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Resolved") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique() + .HasFilter("[CampaignCloseoutId] IS NOT NULL"); + + b.HasIndex("CampaignExperimentId") + .IsUnique() + .HasFilter("[CampaignExperimentId] IS NOT NULL"); + + b.HasIndex("CampaignStartupId") + .IsUnique() + .HasFilter("[CampaignStartupId] IS NOT NULL"); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataSchema") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("SourceJson") + .HasColumnType("nvarchar(max)") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("InitialValue") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NotPlannable") + .HasColumnType("bit"); + + b.Property("OutputName") + .HasColumnType("nvarchar(max)"); + + b.Property("ParameterId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlannerDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("PlannerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Schema") + .HasColumnType("nvarchar(max)"); + + b.Property("Unit") + .HasColumnType("nvarchar(max)"); + + b.Property("UseDefault") + .HasColumnType("bit"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique() + .HasFilter("[ParameterId] IS NOT NULL"); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("Index") + .HasColumnType("bigint"); + + b.Property("IsParallel") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ChartTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("GridH") + .HasColumnType("int"); + + b.Property("GridW") + .HasColumnType("int"); + + b.Property("GridX") + .HasColumnType("int"); + + b.Property("GridY") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("NumberDisplayPoints") + .HasColumnType("int"); + + b.Property("Paths") + .HasColumnType("nvarchar(max)"); + + b.Property("PollingRate") + .HasColumnType("int"); + + b.Property("ShowDataLabels") + .HasColumnType("bit"); + + b.Property("ShowMarkers") + .HasColumnType("bit"); + + b.Property("Style") + .HasColumnType("int"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssociatedDeviceId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DataType") + .HasColumnType("int"); + + b.Property("IsPlottable") + .HasColumnType("bit"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Path") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DriverId") + .HasColumnType("nvarchar(max)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Version") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.cs b/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.cs new file mode 100644 index 00000000..25957857 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260603203704_error-handling-updates_AresDbContext.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresDb +{ + /// + public partial class errorhandlingupdates_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AresGeneralSettingsConfig", + columns: table => new + { + UniqueId = table.Column(type: "uniqueidentifier", nullable: false), + ExperimentRetryLimit = table.Column(type: "int", nullable: false), + CommandRetryLimit = table.Column(type: "int", nullable: false), + RetryCooldown = table.Column(type: "nvarchar(max)", nullable: true), + CommandLatency = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false, defaultValueSql: "getdate()"), + LastModified = table.Column(type: "datetime2", nullable: false, defaultValueSql: "getdate()") + }, + constraints: table => + { + table.PrimaryKey("PK_AresGeneralSettingsConfig", x => x.UniqueId); + }); + + migrationBuilder.CreateTable( + name: "ErrorHandlingConfigs", + columns: table => new + { + UniqueId = table.Column(type: "uniqueidentifier", nullable: false), + Code = table.Column(type: "int", nullable: false), + Handling = table.Column(type: "int", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false, defaultValueSql: "getdate()"), + LastModified = table.Column(type: "datetime2", nullable: false, defaultValueSql: "getdate()") + }, + constraints: table => + { + table.PrimaryKey("PK_ErrorHandlingConfigs", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AresGeneralSettingsConfig"); + + migrationBuilder.DropTable( + name: "ErrorHandlingConfigs"); + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.Designer.cs b/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..f44f1847 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_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("20260603203706_error-handling-updates_AresIdentityContext")] + partial class errorhandlingupdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.cs b/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.cs new file mode 100644 index 00000000..7d2dc555 --- /dev/null +++ b/AresService.Migrations.SqlServer/Migrations/20260603203706_error-handling-updates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.SqlServer.Migrations.AresIdentity +{ + /// + public partial class errorhandlingupdates_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 0950b569..2c32dfb7 100644 --- a/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.SqlServer/Migrations/AresDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -278,6 +278,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("CampaignTags"); }); + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommandLatency") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandRetryLimit") + .HasColumnType("int"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("int"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("RetryCooldown") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => { b.Property("UniqueId") @@ -712,6 +745,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") diff --git a/AresService.Migrations.SqlServer/Migrations/AresIdentityContextModelSnapshot.cs b/AresService.Migrations.SqlServer/Migrations/AresIdentityContextModelSnapshot.cs index 44c16689..b4bf2269 100644 --- a/AresService.Migrations.SqlServer/Migrations/AresIdentityContextModelSnapshot.cs +++ b/AresService.Migrations.SqlServer/Migrations/AresIdentityContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); diff --git a/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.Designer.cs new file mode 100644 index 00000000..355bca41 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.Designer.cs @@ -0,0 +1,2056 @@ +// +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("20260603203652_error-handling-updates_AresDbContext")] + partial class errorhandlingupdates_AresDbContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfo") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.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.AnalyzerCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerInfoId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("AnalyzerInfoId") + .IsUnique(); + + b.ToTable("AnalyzerCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Analyzers", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalysisRequest") + .HasColumnType("TEXT"); + + b.Property("AnalysisResponse") + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("AnalyzerName") + .HasColumnType("TEXT"); + + b.Property("AnalyzerType") + .HasColumnType("TEXT"); + + b.Property("AnalyzerVersion") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AnalyzerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresCampaignTag", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("TagName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("CampaignTags"); + }); + + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLatency") + .HasColumnType("TEXT"); + + b.Property("CommandRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("RetryCooldown") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignId") + .HasColumnType("TEXT"); + + b.Property("CampaignName") + .HasColumnType("TEXT"); + + b.Property("CampaignNotes") + .HasColumnType("TEXT"); + + b.Property("CampaignTags") + .HasColumnType("TEXT"); + + b.Property("CloseoutExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StartupExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CloseoutExecutionSummaryUniqueId"); + + b.HasIndex("StartupExecutionSummaryUniqueId"); + + b.ToTable("CampaignExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandId") + .HasColumnType("TEXT"); + + b.Property("CommandName") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceName") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("State") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StepExecutionStatusUniqueId") + .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("LoggingType") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceLoggingSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceState", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceStates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.RemoteDeviceConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("RemoteDevices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.SerialConnection", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BaudRate") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PortName") + .HasColumnType("TEXT"); + + b.Property("SerialId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("SerialConnection"); + }); + + modelBuilder.Entity("Ares.Datamodel.DeviceErrorHandlingConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Handling") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("ErrorHandlingConfigs", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CommandExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LocaltimeOffset") + .HasColumnType("TEXT"); + + b.Property("StepExecutionSummaryId") + .HasColumnType("TEXT"); + + b.Property("TimeFinished") + .HasColumnType("TEXT"); + + b.Property("TimeStarted") + .HasColumnType("TEXT"); + + b.Property("Timezone") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryId") + .IsUnique(); + + b.HasIndex("CommandExecutionSummaryId") + .IsUnique(); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("StepExecutionSummaryId") + .IsUnique(); + + b.ToTable("ExecutionInfos", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ResultOutputPath") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignExecutionSummaryUniqueId"); + + b.ToTable("ExperimentExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentResultId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Result") + .HasColumnType("TEXT"); + + b.Property("TemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentResultId") + .IsUnique(); + + b.HasIndex("TemplateUniqueId"); + + b.ToTable("ExperimentOverviews", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Maximum") + .HasColumnType("REAL"); + + b.Property("Minimum") + .HasColumnType("REAL"); + + b.Property("ParameterMetadataUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ParameterMetadataUniqueId"); + + b.ToTable("Limits"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerServiceCapabilitiesUniqueId") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerServiceCapabilitiesUniqueId"); + + b.ToTable("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ParameterUniqueId") + .HasColumnType("TEXT"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("ParameterUniqueId"); + + b.HasIndex("PlannerId"); + + b.ToTable("PlannerAllocations", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerServices", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerInfoId") + .HasColumnType("TEXT"); + + b.Property("ServiceName") + .HasColumnType("TEXT"); + + b.Property("SettingsSchema") + .HasColumnType("TEXT"); + + b.Property("TimeoutSeconds") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("PlannerInfoId") + .IsUnique(); + + b.ToTable("PlannerServiceCapabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerInfos"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerSettings", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerSettings"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerTransaction", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("PlannerId") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("PlannerType") + .HasColumnType("TEXT"); + + b.Property("PlannerVersion") + .HasColumnType("TEXT"); + + b.Property("PlanningRequest") + .HasColumnType("TEXT"); + + b.Property("PlanningResponse") + .HasColumnType("TEXT"); + + b.Property("TimeRequestSent") + .HasColumnType("TEXT"); + + b.Property("TimeResponseReceived") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("PlannerTransactions"); + }); + + modelBuilder.Entity("Ares.Datamodel.Project", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.Property("StepName") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("StepExecutionStatuses", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentExecutionSummaryUniqueId") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("StepId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentExecutionSummaryUniqueId"); + + b.ToTable("StepExecutionSummaries", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("CampaignTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateId") + .IsUnique(); + + b.ToTable("CommandMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("OutputVarName") + .HasColumnType("TEXT"); + + b.Property("StepTemplateUniqueId") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("StepTemplateUniqueId"); + + b.ToTable("CommandTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AnalyzerId") + .HasColumnType("TEXT"); + + b.Property("CampaignCloseoutId") + .HasColumnType("TEXT"); + + b.Property("CampaignExperimentId") + .HasColumnType("TEXT"); + + b.Property("CampaignStartupId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Resolved") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignCloseoutId") + .IsUnique(); + + b.HasIndex("CampaignExperimentId") + .IsUnique(); + + b.HasIndex("CampaignStartupId") + .IsUnique(); + + b.ToTable("ExperimentTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataSchema") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.HasKey("UniqueId"); + + b.ToTable("OutputMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentOverviewUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("SourceJson") + .HasColumnType("TEXT") + .HasColumnName("Source"); + + b.HasKey("UniqueId"); + + b.HasIndex("CommandTemplateUniqueId"); + + b.HasIndex("ExperimentOverviewUniqueId"); + + b.ToTable("Parameters", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampaignTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("CommandMetadataUniqueId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("InitialValue") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NotPlannable") + .HasColumnType("INTEGER"); + + b.Property("OutputName") + .HasColumnType("TEXT"); + + b.Property("ParameterId") + .HasColumnType("TEXT"); + + b.Property("PlannerDescription") + .HasColumnType("TEXT"); + + b.Property("PlannerName") + .HasColumnType("TEXT"); + + b.Property("Schema") + .HasColumnType("TEXT"); + + b.Property("Unit") + .HasColumnType("TEXT"); + + b.Property("UseDefault") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.HasIndex("CampaignTemplateUniqueId"); + + b.HasIndex("CommandMetadataUniqueId"); + + b.HasIndex("ParameterId") + .IsUnique(); + + b.ToTable("ParameterMetadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentTemplateUniqueId") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsParallel") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.HasIndex("ExperimentTemplateUniqueId"); + + b.ToTable("StepTemplates", (string)null); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.DeviceVisualizationConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ChartTitle") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("GridH") + .HasColumnType("INTEGER"); + + b.Property("GridW") + .HasColumnType("INTEGER"); + + b.Property("GridX") + .HasColumnType("INTEGER"); + + b.Property("GridY") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("NumberDisplayPoints") + .HasColumnType("INTEGER"); + + b.Property("Paths") + .HasColumnType("TEXT"); + + b.Property("PollingRate") + .HasColumnType("INTEGER"); + + b.Property("ShowDataLabels") + .HasColumnType("INTEGER"); + + b.Property("ShowMarkers") + .HasColumnType("INTEGER"); + + b.Property("Style") + .HasColumnType("INTEGER"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceVisualizationConfigs"); + }); + + modelBuilder.Entity("Ares.Datamodel.Visualizing.Local.VisualizationPath", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AssociatedDeviceId") + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DataType") + .HasColumnType("INTEGER"); + + b.Property("IsPlottable") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("VisualizationPath"); + }); + + modelBuilder.Entity("Ares.Services.DriverInfo", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("DisplayName") + .HasColumnType("TEXT"); + + b.Property("DriverId") + .HasColumnType("TEXT"); + + b.Property("FileSizeBytes") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("DeviceDrivers"); + }); + + modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithOne("AnalysisOverview") + .HasForeignKey("Ares.Datamodel.AnalysisOverview", "ExperimentOverviewId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerCapabilities", b => + { + b.HasOne("Ares.Datamodel.Analyzing.AnalyzerInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Analyzing.AnalyzerCapabilities", "AnalyzerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "CloseoutExecutionSummary") + .WithMany() + .HasForeignKey("CloseoutExecutionSummaryUniqueId"); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", "StartupExecutionSummary") + .WithMany() + .HasForeignKey("StartupExecutionSummaryUniqueId"); + + b.Navigation("CloseoutExecutionSummary"); + + b.Navigation("StartupExecutionSummary"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionStatus", b => + { + b.HasOne("Ares.Datamodel.StepExecutionStatus", null) + .WithMany("CommandExecutionStatuses") + .HasForeignKey("StepExecutionStatusUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithMany("CommandSummaries") + .HasForeignKey("StepExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandResult", b => + { + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("Result") + .HasForeignKey("Ares.Datamodel.CommandResult", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceCommandDescriptor", b => + { + b.HasOne("Ares.Datamodel.Device.DeviceInfo", null) + .WithMany("Commands") + .HasForeignKey("DeviceInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceConfig", b => + { + b.HasOne("Ares.Datamodel.Device.SerialConnection", "SerialInfo") + .WithMany() + .HasForeignKey("SerialInfoUniqueId"); + + b.Navigation("SerialInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExecutionInfo", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CampaignExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.CommandExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "CommandExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "ExperimentResultId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.StepExecutionSummary", null) + .WithOne("ExecutionInfo") + .HasForeignKey("Ares.Datamodel.ExecutionInfo", "StepExecutionSummaryId") + .OnDelete(DeleteBehavior.ClientCascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.CampaignExecutionSummary", null) + .WithMany("ExperimentSummaries") + .HasForeignKey("CampaignExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithOne("ExperimentOverview") + .HasForeignKey("Ares.Datamodel.ExperimentOverview", "ExperimentResultId"); + + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateUniqueId"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Ares.Datamodel.Limits", b => + { + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", null) + .WithMany("Constraints") + .HasForeignKey("ParameterMetadataUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.Planner", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceCapabilities", null) + .WithMany("AvailablePlanners") + .HasForeignKey("PlannerServiceCapabilitiesUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerAllocation", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannerAllocations") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ares.Datamodel.Templates.ParameterMetadata", "Parameter") + .WithMany() + .HasForeignKey("ParameterUniqueId"); + + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", "Planner") + .WithMany() + .HasForeignKey("PlannerId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.Navigation("Parameter"); + + b.Navigation("Planner"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.HasOne("Ares.Datamodel.Planning.PlannerServiceInfo", null) + .WithOne("Capabilities") + .HasForeignKey("Ares.Datamodel.Planning.PlannerServiceCapabilities", "PlannerInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.HasOne("Ares.Datamodel.ExperimentExecutionSummary", null) + .WithMany("StepSummaries") + .HasForeignKey("ExperimentExecutionSummaryUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.CommandMetadata", "CommandTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.StepTemplate", null) + .WithMany("CommandTemplates") + .HasForeignKey("StepTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("CloseoutTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignCloseoutId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("ExperimentTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignExperimentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithOne("StartupTemplate") + .HasForeignKey("Ares.Datamodel.Templates.ExperimentTemplate", "CampaignStartupId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.OutputMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithOne("OutputMetadata") + .HasForeignKey("Ares.Datamodel.Templates.OutputMetadata", "UniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.HasOne("Ares.Datamodel.Templates.CommandTemplate", null) + .WithMany("Parameters") + .HasForeignKey("CommandTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ares.Datamodel.ExperimentOverview", null) + .WithMany("Parameters") + .HasForeignKey("ExperimentOverviewUniqueId"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.HasOne("Ares.Datamodel.Templates.CampaignTemplate", null) + .WithMany("PlannableParameters") + .HasForeignKey("CampaignTemplateUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.CommandMetadata", null) + .WithMany("ParameterMetadatas") + .HasForeignKey("CommandMetadataUniqueId") + .OnDelete(DeleteBehavior.ClientCascade); + + b.HasOne("Ares.Datamodel.Templates.Parameter", null) + .WithOne("Metadata") + .HasForeignKey("Ares.Datamodel.Templates.ParameterMetadata", "ParameterId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.HasOne("Ares.Datamodel.Templates.ExperimentTemplate", null) + .WithMany("StepTemplates") + .HasForeignKey("ExperimentTemplateUniqueId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ares.Datamodel.Analyzing.AnalyzerInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.CommandExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("Result"); + }); + + modelBuilder.Entity("Ares.Datamodel.Device.DeviceInfo", b => + { + b.Navigation("Commands"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentExecutionSummary", b => + { + b.Navigation("ExecutionInfo"); + + b.Navigation("ExperimentOverview"); + + b.Navigation("StepSummaries"); + }); + + modelBuilder.Entity("Ares.Datamodel.ExperimentOverview", b => + { + b.Navigation("AnalysisOverview"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceCapabilities", b => + { + b.Navigation("AvailablePlanners"); + }); + + modelBuilder.Entity("Ares.Datamodel.Planning.PlannerServiceInfo", b => + { + b.Navigation("Capabilities"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionStatus", b => + { + b.Navigation("CommandExecutionStatuses"); + }); + + modelBuilder.Entity("Ares.Datamodel.StepExecutionSummary", b => + { + b.Navigation("CommandSummaries"); + + b.Navigation("ExecutionInfo"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CampaignTemplate", b => + { + b.Navigation("CloseoutTemplate"); + + b.Navigation("ExperimentTemplate"); + + b.Navigation("PlannableParameters"); + + b.Navigation("PlannerAllocations"); + + b.Navigation("StartupTemplate"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandMetadata", b => + { + b.Navigation("OutputMetadata"); + + b.Navigation("ParameterMetadatas"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.CommandTemplate", b => + { + b.Navigation("Metadata"); + + b.Navigation("Parameters"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ExperimentTemplate", b => + { + b.Navigation("StepTemplates"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.Parameter", b => + { + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.ParameterMetadata", b => + { + b.Navigation("Constraints"); + }); + + modelBuilder.Entity("Ares.Datamodel.Templates.StepTemplate", b => + { + b.Navigation("CommandTemplates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.cs b/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.cs new file mode 100644 index 00000000..6fd0b59e --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260603203652_error-handling-updates_AresDbContext.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresDb +{ + /// + public partial class errorhandlingupdates_AresDbContext : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AresGeneralSettingsConfig", + columns: table => new + { + UniqueId = table.Column(type: "TEXT", nullable: false), + ExperimentRetryLimit = table.Column(type: "INTEGER", nullable: false), + CommandRetryLimit = table.Column(type: "INTEGER", nullable: false), + RetryCooldown = table.Column(type: "TEXT", nullable: true), + CommandLatency = 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_AresGeneralSettingsConfig", x => x.UniqueId); + }); + + migrationBuilder.CreateTable( + name: "ErrorHandlingConfigs", + columns: table => new + { + UniqueId = table.Column(type: "TEXT", nullable: false), + Code = table.Column(type: "INTEGER", nullable: false), + Handling = table.Column(type: "INTEGER", nullable: false), + 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_ErrorHandlingConfigs", x => x.UniqueId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AresGeneralSettingsConfig"); + + migrationBuilder.DropTable( + name: "ErrorHandlingConfigs"); + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.Designer.cs b/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.Designer.cs new file mode 100644 index 00000000..ec5ab7ef --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_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("20260603203654_error-handling-updates_AresIdentityContext")] + partial class errorhandlingupdates_AresIdentityContext + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); + + modelBuilder.Entity("AresService.Data.AresUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("AresService.Data.AresUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.cs b/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.cs new file mode 100644 index 00000000..7a2419f3 --- /dev/null +++ b/AresService.Migrations.Sqlite/Migrations/20260603203654_error-handling-updates_AresIdentityContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AresService.Migrations.Sqlite.Migrations.AresIdentity +{ + /// + public partial class errorhandlingupdates_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 6672b035..80a83411 100644 --- a/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs +++ b/AresService.Migrations.Sqlite/Migrations/AresDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class AresDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); modelBuilder.Entity("Ares.Datamodel.AnalysisOverview", b => { @@ -272,6 +272,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("CampaignTags"); }); + modelBuilder.Entity("Ares.Datamodel.AresGeneralSettingsConfig", b => + { + b.Property("UniqueId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLatency") + .HasColumnType("TEXT"); + + b.Property("CommandRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("CreationTime") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("ExperimentRetryLimit") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasDefaultValueSql("DATETIME('now')"); + + b.Property("RetryCooldown") + .HasColumnType("TEXT"); + + b.HasKey("UniqueId"); + + b.ToTable("AresGeneralSettingsConfig", (string)null); + }); + modelBuilder.Entity("Ares.Datamodel.CampaignExecutionSummary", b => { b.Property("UniqueId") @@ -705,6 +738,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") diff --git a/AresService.Migrations.Sqlite/Migrations/AresIdentityContextModelSnapshot.cs b/AresService.Migrations.Sqlite/Migrations/AresIdentityContextModelSnapshot.cs index b8b95329..03a9f7c9 100644 --- a/AresService.Migrations.Sqlite/Migrations/AresIdentityContextModelSnapshot.cs +++ b/AresService.Migrations.Sqlite/Migrations/AresIdentityContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class AresIdentityContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); modelBuilder.Entity("AresService.Data.AresUser", b => { From f5552770cac3c1e37cdbe710061a03d0157049fd Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 12:06:34 -0400 Subject: [PATCH 20/25] Fixed some tests --- Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs b/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs index 11e3c8ef..7e883a4f 100644 --- a/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs +++ b/Ares.Core.Tests/Execution/CommandVariableResolutionTests.cs @@ -8,6 +8,8 @@ using Ares.Datamodel.Device; using Ares.Datamodel.Templates; using Ares.Device; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Moq; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -17,11 +19,13 @@ namespace Ares.Core.Tests.Execution; internal class CommandVariableResolutionTests { private ISystemSettingsManager _systemSettingsManager; + private IDbContextFactory _dbContextFactory; [OneTimeSetUp] public void OneTimeSetUp() { - _systemSettingsManager = new Mock().Object; + _dbContextFactory = new Mock>().Object; + _systemSettingsManager = new Mock().Object; } [Test] From e0ef9996db993a9d37955b7e8a9d1a24dd35d914 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 14:29:04 -0400 Subject: [PATCH 21/25] Fixed the waiting for user not working --- UI/Features/Execution/Execution.razor | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/UI/Features/Execution/Execution.razor b/UI/Features/Execution/Execution.razor index 0263d176..6675cdd9 100644 --- a/UI/Features/Execution/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -74,6 +74,25 @@
+
+ +
+ + + User Confirmation Required + + + ARES requires user approval to continue experimenting. Continue by hitting "Confirm" or play. You can also stop your experiment by hitting the stop button. + + + + + + + +
+
+ @code { From cb18639230529bfe6c985edcbd013cdd9b397f41 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 4 Jun 2026 15:31:02 -0400 Subject: [PATCH 22/25] Fixed duplicate entity config. Fixed visual bug in execution screen. --- .../Shared/ErrorHandlingEntityConfiguration.cs | 7 ------- Ares.Core/Execution/Executors/CampaignExecutor.cs | 5 +++-- 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs diff --git a/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs b/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs deleted file mode 100644 index 57222659..00000000 --- a/Ares.Core/EntityConfigurations/Shared/ErrorHandlingEntityConfiguration.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Ares.Datamodel; - -namespace Ares.Core.EntityConfigurations.Shared; - -internal class ErrorHandlingEntityConfiguration : AresEntityTypeBaseConfiguration -{ -} diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 09e01807..890c8ffe 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -345,8 +345,9 @@ private async Task ExecuteExperimentLoop(string campaignPath, List Date: Wed, 10 Jun 2026 14:54:01 -0400 Subject: [PATCH 23/25] Provide status code to plan requests --- Ares.Core/Ares.Core.csproj | 2 +- .../Execution/Executors/CampaignExecutor.cs | 61 +++++++++++++++++-- Ares.Core/Planning/IPlanningHelper.cs | 1 + Ares.Core/Planning/PlanningHelper.cs | 2 + AresScript/AresScript.csproj | 2 +- DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj | 2 +- DemoRemoteDevice/DemoRemoteDevice.csproj | 2 +- DemoRemotePlanner/DemoRemotePlanner.csproj | 2 +- UI/UI.csproj | 2 +- 9 files changed, 66 insertions(+), 10 deletions(-) diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index bdb670c2..19323477 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 890c8ffe..6fcb3332 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -13,6 +13,7 @@ using Ares.Core.Settings; using Ares.Datamodel; using Ares.Datamodel.Analyzing; +using Ares.Datamodel.Planning; using Ares.Datamodel.Templates; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; @@ -45,6 +46,7 @@ public class CampaignExecutor : ICampaignExecutor private ExperimentTemplate? _currentExperimentTemplate = null; private int _experimentCount = 0; private TaskCompletionSource? _userDecisionSource; + private CommandStatusCode? _latestStatusCode = null; internal CampaignExecutor(ICommandComposer experimentComposer, IPlanningHelper planningHelper, @@ -280,13 +282,23 @@ private async Task ExecuteExperimentLoop(string campaignPath, List s.CommandSummaries.Where(c => !c.Result.Success)).FirstOrDefault(); if(!_currentSummary.StepSummaries.Any()) + { currentState = ExecutionState.Failed; + _latestStatusCode = CommandStatusCode.CommandFailed; + } + if(failedCommandSummary is not null) + { currentState = await HandleError(failedCommandSummary); + _latestStatusCode = failedCommandSummary.StatusCode; + } else + { currentState = ExecutionState.Analyzing; + _latestStatusCode = CommandStatusCode.CommandSuccess; + } break; @@ -311,7 +323,46 @@ private async Task ExecuteExperimentLoop(string campaignPath, List ExecuteExperimentLoop(string campaignPath, List HandleError(CommandExecutionSummary cmdSummar } } - private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, ExecutionControlToken token) + private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, PlanStatusCode code, ExecutionControlToken token) { Status.PlannerState = PlannerState.PlanningInProgress; _executionStatusSubject.OnNext(Status); @@ -502,6 +553,7 @@ private async Task PlanExperiment(List analyses, ExperimentTempl currentExperimentTemplate.GetAllPlannedParameters(), analyses, experimentSummaries.Select(es => es.ExperimentOverview), + code, token.CancellationToken); if(!resolveSuccess) @@ -707,7 +759,8 @@ private async Task GenerateExperimentExecutor(Experime metadata, experimentTemplate.GetAllPlannedParameters(), analyses, - previousExperiments, + previousExperiments, + PlanStatusCode.PlanAccepted, cancellationToken); if(!resolveSuccess) diff --git a/Ares.Core/Planning/IPlanningHelper.cs b/Ares.Core/Planning/IPlanningHelper.cs index 07e8226d..2062365f 100644 --- a/Ares.Core/Planning/IPlanningHelper.cs +++ b/Ares.Core/Planning/IPlanningHelper.cs @@ -23,5 +23,6 @@ Task TryResolveParameters(IEnumerable plannerAllocation IEnumerable parameters, IEnumerable seedAnalyses, IEnumerable seedExperiments, + PlanStatusCode code, CancellationToken cancellationToken); } diff --git a/Ares.Core/Planning/PlanningHelper.cs b/Ares.Core/Planning/PlanningHelper.cs index 1a917c6c..81e706f9 100644 --- a/Ares.Core/Planning/PlanningHelper.cs +++ b/Ares.Core/Planning/PlanningHelper.cs @@ -34,6 +34,7 @@ public async Task TryResolveParameters(IEnumerable plan IEnumerable parameters, IEnumerable seedAnalyses, IEnumerable seedExperiments, + PlanStatusCode statusCode, CancellationToken cancellationToken) { var parameterArray = parameters.ToArray(); @@ -72,6 +73,7 @@ public async Task TryResolveParameters(IEnumerable plan var planRequest = new PlanningRequest(); planRequest.PlanningParameters.AddRange(plannableParameters.Select(parameter => ConvertToPlanningParameter(parameter, seedExperiments))); planRequest.AnalysisResults.AddRange(seedAnalysesArr.Select(a => (double)a.Result)); + planRequest.PreviousPlanStatusCode = statusCode; planRequest.Metadata = metadata; planTransaction.PlanningRequest = planRequest; diff --git a/AresScript/AresScript.csproj b/AresScript/AresScript.csproj index 13560ec0..e5caf7f7 100644 --- a/AresScript/AresScript.csproj +++ b/AresScript/AresScript.csproj @@ -9,7 +9,7 @@ - + diff --git a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj index 07b76735..d6617032 100644 --- a/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj +++ b/DemoRemoteAnalyzer/DemoRemoteAnalyzer.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemoteDevice/DemoRemoteDevice.csproj b/DemoRemoteDevice/DemoRemoteDevice.csproj index d23b37aa..2e02e779 100644 --- a/DemoRemoteDevice/DemoRemoteDevice.csproj +++ b/DemoRemoteDevice/DemoRemoteDevice.csproj @@ -7,7 +7,7 @@ - + diff --git a/DemoRemotePlanner/DemoRemotePlanner.csproj b/DemoRemotePlanner/DemoRemotePlanner.csproj index 07b76735..d6617032 100644 --- a/DemoRemotePlanner/DemoRemotePlanner.csproj +++ b/DemoRemotePlanner/DemoRemotePlanner.csproj @@ -7,7 +7,7 @@ - + diff --git a/UI/UI.csproj b/UI/UI.csproj index bf1612ae..e36a70e0 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -29,7 +29,7 @@ - + From 824d734a0ee01c0a44d6f7ae0a6b4dc979803820 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Wed, 17 Jun 2026 13:45:05 -0400 Subject: [PATCH 24/25] Working on campaign executor --- .../Execution/Executors/CampaignExecutor.cs | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/Ares.Core/Execution/Executors/CampaignExecutor.cs b/Ares.Core/Execution/Executors/CampaignExecutor.cs index 7a66f6e7..f50b1f03 100644 --- a/Ares.Core/Execution/Executors/CampaignExecutor.cs +++ b/Ares.Core/Execution/Executors/CampaignExecutor.cs @@ -47,6 +47,7 @@ public class CampaignExecutor : ICampaignExecutor private ExperimentTemplate? _currentExperimentTemplate = null; private int _experimentCount = 0; private TaskCompletionSource? _userDecisionSource; + private PlanStatusCode _latestPlanStatusCode = PlanStatusCode.PlanStatusUnspecified; internal CampaignExecutor(ICommandComposer experimentComposer, IPlanningHelper planningHelper, @@ -208,7 +209,11 @@ private async Task NotifyCampaignStart() return (true, startupSummary); } - private async Task ExecuteExperimentLoop(string campaignPath, List analyses, List experimentSummaries, ExecutionControlToken token, ExperimentExecutionSummary startupSummary) + private async Task ExecuteExperimentLoop(string campaignPath, + List analyses, + List experimentSummaries, + ExecutionControlToken token, + ExperimentExecutionSummary startupSummary) { var currentPhase = ExperimentPhase.Initialize; var currentExperimentPath = ""; @@ -379,9 +384,17 @@ private async Task ExecuteCurrentExperiment(string currentExper .SelectMany(s => s.CommandSummaries) .FirstOrDefault(c => !c.Result.Success); - return failedCommandSummary is null - ? ExperimentPhase.Analyze - : await HandleError(failedCommandSummary, token); + if(failedCommandSummary is null) + { + _latestPlanStatusCode = PlanStatusCode.PlanAccepted; + return ExperimentPhase.Analyze; + } + + else + { + UpdatePlanStatus(failedCommandSummary.StatusCode); + return await HandleError(failedCommandSummary, token); + } } private async Task AnalyzeCurrentExperiment(ExperimentExecutionSummary startupSummary, List analyses, List experimentSummaries, ExecutionControlToken token) @@ -461,6 +474,7 @@ private async Task HandleError(CommandExecutionSummary cmdSumma var decisionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _userDecisionSource = decisionSource; + try { errorHandling = await decisionSource.Task.WaitAsync(token.CancellationToken); @@ -498,6 +512,15 @@ private async Task HandleError(CommandExecutionSummary cmdSumma } } + private void UpdatePlanStatus(CommandStatusCode failCode) + { + if(failCode == CommandStatusCode.OutOfRange || failCode == CommandStatusCode.ParametersUnachievable) + _latestPlanStatusCode = PlanStatusCode.PlanUnachievable; + + else + _latestPlanStatusCode = PlanStatusCode.PlanFailed; + } + private async Task PlanExperiment(List analyses, ExperimentTemplate currentExperimentTemplate, List experimentSummaries, ExecutionControlToken token) { Status.PlannerState = PlannerState.PlanningInProgress; @@ -518,7 +541,7 @@ private async Task PlanExperiment(List analyses, ExperimentTempl currentExperimentTemplate.GetAllPlannedParameters(), analyses, experimentSummaries.Select(es => es.ExperimentOverview), - PlanStatusCode.PlanAccepted, + _latestPlanStatusCode, token.CancellationToken); if(!resolveSuccess) From 153cdc6ab6e48a9ea784933e9f192e5abcb9efd6 Mon Sep 17 00:00:00 2001 From: Nick Kleiner Date: Thu, 18 Jun 2026 14:25:09 -0400 Subject: [PATCH 25/25] Updated toolkit version --- Ares.Core/Ares.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ares.Core/Ares.Core.csproj b/Ares.Core/Ares.Core.csproj index 9c20c83e..e55780d2 100644 --- a/Ares.Core/Ares.Core.csproj +++ b/Ares.Core/Ares.Core.csproj @@ -27,7 +27,7 @@ - +