From 778fe3cd0a75264ef40b11c74e1953465272b28c Mon Sep 17 00:00:00 2001 From: Arnas Babeckis Date: Mon, 9 Feb 2026 01:08:31 -0500 Subject: [PATCH 01/17] Started moving toward VSA. Reorganized monaco related items into their own location. Moved some MFC related things. Moved layouts. --- .../Devices}/Mfc/MfcConfigEditView.razor | 184 +++++----- .../Devices}/Mfc/MfcConfigEditView.razor.css | 80 ++--- .../Devices}/Mfc/MfcSettingsList.razor | 1 + .../Devices}/Mfc/MfcSettingsView.razor | 340 +++++++++--------- .../Devices}/Mfc/MfcSettingsView.razor.css | 40 +-- .../Devices/Mfc/MfcUnitControl.razor | 284 +++++++-------- .../Devices/Mfc/MfcUnitControl.razor.css | 226 ++++++------ .../Settings}/DeviceSettingsContainer.razor | 262 +++++++------- .../DeviceSettingsContainer.razor.css | 104 +++--- .../Settings}/DeviceSettingsLayout.razor | 67 ++-- .../Settings/EmptyDeviceSettingsPage.razor} | 10 +- .../Devices/Shared}/DeviceStates.razor | 44 +-- UI/{Pages => Features/Safety}/Safety.razor | 0 .../Settings/SettingsLayout.razor | 48 +-- .../Settings/SettingsLayout.razor.css | 6 +- .../Interfaces}/IUiNotificationService.cs | 0 .../Interops}/CompletionItemExtensions.cs | 0 .../Interops}/MonacoCompletionProvider.cs | 0 .../Interops}/MonacoDiagnosticsProvider.cs | 0 .../Monaco/Interops}/MonacoHoverProvider.cs | 0 .../Interops}/MonacoSemanticTokensProvider.cs | 0 .../Scripts}/ares-autocomplete-setup.ts | 0 .../Monaco/Scripts}/ares-diagnostics-setup.ts | 0 .../Monaco/Scripts}/ares-hover-setup.ts | 0 .../Monaco/Scripts}/ares-semantic-setup.ts | 0 .../Monaco/Scripts}/ares-setup.ts | 0 .../areslang.language-configuration.ts | 0 .../Monaco/Scripts}/areslang.monarch.ts | 0 .../Monaco}/Scripts/globals.d.ts | 0 UI/{NotFoundPage.razor => NotFound.razor} | 0 .../Shared/Settings/PlaceholderSettings.razor | 22 -- .../Settings/PlaceholderSettings.razor.css | 7 - UI/Routes.razor | 7 +- UI/Scripts/monaco/README.md | 51 --- .../CampaignEdit/CampaignEditContext.cs | 2 +- UI/UI.csproj | 18 + UI/tsconfig.json | 6 +- 37 files changed, 872 insertions(+), 937 deletions(-) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Mfc/MfcConfigEditView.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Mfc/MfcConfigEditView.razor.css (92%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Mfc/MfcSettingsList.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Mfc/MfcSettingsView.razor (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Mfc/MfcSettingsView.razor.css (94%) rename UI/{Pages/Shared => Features}/Devices/Mfc/MfcUnitControl.razor (97%) rename UI/{Pages/Shared => Features}/Devices/Mfc/MfcUnitControl.razor.css (93%) rename UI/{Pages/Shared/Settings/Device => Features/Devices/Settings}/DeviceSettingsContainer.razor (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices/Settings}/DeviceSettingsContainer.razor.css (94%) rename UI/{Pages/Shared/Settings/Device => Features/Devices/Settings}/DeviceSettingsLayout.razor (96%) rename UI/{Pages/Shared/Settings/Device/EmptyDevicePage.razor => Features/Devices/Settings/EmptyDeviceSettingsPage.razor} (93%) rename UI/{Pages => Features/Devices/Shared}/DeviceStates.razor (96%) rename UI/{Pages => Features/Safety}/Safety.razor (100%) rename UI/{Pages/Shared => Features}/Settings/SettingsLayout.razor (96%) rename UI/{Pages/Shared => Features}/Settings/SettingsLayout.razor.css (96%) rename UI/{Services/Notification => Infrastructure/Interfaces}/IUiNotificationService.cs (100%) rename UI/{JsInterops => Infrastructure/Monaco/Interops}/CompletionItemExtensions.cs (100%) rename UI/{JsInterops => Infrastructure/Monaco/Interops}/MonacoCompletionProvider.cs (100%) rename UI/{JsInterops => Infrastructure/Monaco/Interops}/MonacoDiagnosticsProvider.cs (100%) rename UI/{JsInterops => Infrastructure/Monaco/Interops}/MonacoHoverProvider.cs (100%) rename UI/{JsInterops => Infrastructure/Monaco/Interops}/MonacoSemanticTokensProvider.cs (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/ares-autocomplete-setup.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/ares-diagnostics-setup.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/ares-hover-setup.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/ares-semantic-setup.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/ares-setup.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/areslang.language-configuration.ts (100%) rename UI/{Scripts/monaco => Infrastructure/Monaco/Scripts}/areslang.monarch.ts (100%) rename UI/{ => Infrastructure/Monaco}/Scripts/globals.d.ts (100%) rename UI/{NotFoundPage.razor => NotFound.razor} (100%) delete mode 100644 UI/Pages/Shared/Settings/PlaceholderSettings.razor delete mode 100644 UI/Pages/Shared/Settings/PlaceholderSettings.razor.css delete mode 100644 UI/Scripts/monaco/README.md diff --git a/UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor b/UI/Features/Devices/Mfc/MfcConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor rename to UI/Features/Devices/Mfc/MfcConfigEditView.razor index 2dc5c42b..57df88bf 100644 --- a/UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor +++ b/UI/Features/Devices/Mfc/MfcConfigEditView.razor @@ -1,92 +1,92 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@using UI.Backend.ViewModels.Settings.Device.Mfc; -@using Ares.Alicat.Mfc.Config; - - -
- - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - - - - - - @if (IdsLoading) - { -
- -
- } - -
- - Simulated -
-
- - Has Valve -
-
- - - -
-@code { - - [Parameter] - public Action OnValidSubmit { get; set; } = delegate { - }; - - private bool IdsLoading { get; set; } - - private async Task SimulatedChanged(bool val) - { - await ReloadIds(); - } - - private async Task PortChanged(ChangeEventArgs obj) - { - if (obj.Value is not string stringValue) - throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); - ViewModel!.Port = stringValue; - await ReloadIds(); - } - - private async Task ReloadIds() - { - IdsLoading = true; - StateHasChanged(); - await ViewModel!.UpdateAvailableIds(); - IdsLoading = false; - StateHasChanged(); - } -} +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Backend.ViewModels.Settings.Device.Mfc; +@using Ares.Alicat.Mfc.Config; + + +
+ + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + + + + + + @if (IdsLoading) + { +
+ +
+ } + +
+ + Simulated +
+
+ + Has Valve +
+
+ + + +
+@code { + + [Parameter] + public Action OnValidSubmit { get; set; } = delegate { + }; + + private bool IdsLoading { get; set; } + + private async Task SimulatedChanged(bool val) + { + await ReloadIds(); + } + + private async Task PortChanged(ChangeEventArgs obj) + { + if (obj.Value is not string stringValue) + throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); + ViewModel!.Port = stringValue; + await ReloadIds(); + } + + private async Task ReloadIds() + { + IdsLoading = true; + StateHasChanged(); + await ViewModel!.UpdateAvailableIds(); + IdsLoading = false; + StateHasChanged(); + } +} diff --git a/UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor.css b/UI/Features/Devices/Mfc/MfcConfigEditView.razor.css similarity index 92% rename from UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor.css rename to UI/Features/Devices/Mfc/MfcConfigEditView.razor.css index 9b4dfd1f..8de1fc05 100644 --- a/UI/Pages/Shared/Settings/Device/Mfc/MfcConfigEditView.razor.css +++ b/UI/Features/Devices/Mfc/MfcConfigEditView.razor.css @@ -1,41 +1,41 @@ -.mfc-property-grid { - display: grid; - grid-template: - "name ." 1fr - "port ." 1fr - "simulated ." 1fr - "id spinner" 1fr - "type ." 1fr - "valve ." 1fr - / auto 40px -} - -.mfc-name { - grid-area: name; -} - -.mfc-port { - grid-area: port; -} - -.mfc-id { - grid-area: id; -} - -.mfc-type { - grid-area: type; -} - -.mfc-simulated { - grid-area: simulated; -} - -.mfc-valve { - grid-area: valve; -} - -.mfc-spinner { - grid-area: spinner; - align-self: center; - justify-self: center; +.mfc-property-grid { + display: grid; + grid-template: + "name ." 1fr + "port ." 1fr + "simulated ." 1fr + "id spinner" 1fr + "type ." 1fr + "valve ." 1fr + / auto 40px +} + +.mfc-name { + grid-area: name; +} + +.mfc-port { + grid-area: port; +} + +.mfc-id { + grid-area: id; +} + +.mfc-type { + grid-area: type; +} + +.mfc-simulated { + grid-area: simulated; +} + +.mfc-valve { + grid-area: valve; +} + +.mfc-spinner { + grid-area: spinner; + align-self: center; + justify-self: center; } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsList.razor b/UI/Features/Devices/Mfc/MfcSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsList.razor rename to UI/Features/Devices/Mfc/MfcSettingsList.razor index 60da5027..5fa45b45 100644 --- a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsList.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/alicat" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Mfc +@using UI.Features.Devices.Settings @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor b/UI/Features/Devices/Mfc/MfcSettingsView.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor rename to UI/Features/Devices/Mfc/MfcSettingsView.razor index eee5dde3..f3ee7301 100644 --- a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsView.razor @@ -1,171 +1,171 @@ -@using Ares.Alicat.Mfc.Config -@using UI.Backend.ViewModels.Settings.Device.Mfc -@using Ares.Alicat.Mfc.Messaging -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - -
-
- Port - - @ViewModel!.MfcConfig.PortName -
- - @if (ViewModel!.DeviceActive) - { -
- @if (_changing) - { -
-
- -
-
- } -
- Current Gas - @ViewModel!.CurrentGas - @if (ViewModel!.AvailableGases is not null) - { - - - } - - Current Id - @ViewModel!.CurrentId - @if (ViewModel!.AvailableIds is not null) - { - - - } - @if (ViewModel!.MfcConfig.MfcType == MfcType.Basis2) - { - Setpoint Source - @ViewModel!.SelectedSetpointSource - - @if (ViewModel!.SetpointSourceUpdating) - { - - - - } - } -
-
- } - else if(_initActive) - { - - } -
-
- -@code { - private bool _initActive; - private bool _changing; - - private string Name => ViewModel!.MfcConfig.Name + (ViewModel!.MfcConfig.Simulated ? " -- (Simulated)" : ""); - - protected override async Task OnInitializedAsync() - { - _initActive = true; - StateHasChanged(); - await ViewModel!.Init(); - _initActive = false; - StateHasChanged(); - } - - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit MFC", ds => - @); - - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - } - - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.MfcConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } - - private async Task ChangeIdClicked() - { - _changing = true; - await ViewModel!.ChangeId(); - await ViewModel!.Init(); - _changing = false; - StateHasChanged(); - } - - private async Task ChangeGasClicked() - { - _changing = true; - try - { - await ViewModel!.ChangeGas(); - } - catch(TimeoutException) - { - NotificationService.Error("Gas change command timed out."); - } - await ViewModel!.Init(); - _changing = false; - StateHasChanged(); - } - - private string SelectedSetpointSourceValue - { - get => ViewModel?.SelectedSetpointSource.ToString() ?? SetpointSource.UnknownSource.ToString(); - set - { - if (ViewModel is null) - return; - - if (!Enum.TryParse(value, out var parsed)) - return; - - if (ViewModel.SetpointSourceUpdating || ViewModel.SelectedSetpointSource == parsed) - return; - - _ = InvokeAsync(() => ChangeSetpointSourceAsync(parsed)); - } - } - - private async Task ChangeSetpointSourceAsync(SetpointSource source) - { - await ViewModel!.UpdateSetpointSource(source); - StateHasChanged(); - } +@using Ares.Alicat.Mfc.Config +@using UI.Backend.ViewModels.Settings.Device.Mfc +@using Ares.Alicat.Mfc.Messaging +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + +
+
+ Port - + @ViewModel!.MfcConfig.PortName +
+ + @if (ViewModel!.DeviceActive) + { +
+ @if (_changing) + { +
+
+ +
+
+ } +
+ Current Gas + @ViewModel!.CurrentGas + @if (ViewModel!.AvailableGases is not null) + { + + + } + + Current Id + @ViewModel!.CurrentId + @if (ViewModel!.AvailableIds is not null) + { + + + } + @if (ViewModel!.MfcConfig.MfcType == MfcType.Basis2) + { + Setpoint Source + @ViewModel!.SelectedSetpointSource + + @if (ViewModel!.SetpointSourceUpdating) + { + + + + } + } +
+
+ } + else if(_initActive) + { + + } +
+
+ +@code { + private bool _initActive; + private bool _changing; + + private string Name => ViewModel!.MfcConfig.Name + (ViewModel!.MfcConfig.Simulated ? " -- (Simulated)" : ""); + + protected override async Task OnInitializedAsync() + { + _initActive = true; + StateHasChanged(); + await ViewModel!.Init(); + _initActive = false; + StateHasChanged(); + } + + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit MFC", ds => + @); + + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + } + + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.MfcConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } + + private async Task ChangeIdClicked() + { + _changing = true; + await ViewModel!.ChangeId(); + await ViewModel!.Init(); + _changing = false; + StateHasChanged(); + } + + private async Task ChangeGasClicked() + { + _changing = true; + try + { + await ViewModel!.ChangeGas(); + } + catch(TimeoutException) + { + NotificationService.Error("Gas change command timed out."); + } + await ViewModel!.Init(); + _changing = false; + StateHasChanged(); + } + + private string SelectedSetpointSourceValue + { + get => ViewModel?.SelectedSetpointSource.ToString() ?? SetpointSource.UnknownSource.ToString(); + set + { + if (ViewModel is null) + return; + + if (!Enum.TryParse(value, out var parsed)) + return; + + if (ViewModel.SetpointSourceUpdating || ViewModel.SelectedSetpointSource == parsed) + return; + + _ = InvokeAsync(() => ChangeSetpointSourceAsync(parsed)); + } + } + + private async Task ChangeSetpointSourceAsync(SetpointSource source) + { + await ViewModel!.UpdateSetpointSource(source); + StateHasChanged(); + } } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor.css b/UI/Features/Devices/Mfc/MfcSettingsView.razor.css similarity index 94% rename from UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor.css rename to UI/Features/Devices/Mfc/MfcSettingsView.razor.css index fe1beb40..fc38112c 100644 --- a/UI/Pages/Shared/Settings/Device/Mfc/MfcSettingsView.razor.css +++ b/UI/Features/Devices/Mfc/MfcSettingsView.razor.css @@ -1,21 +1,21 @@ -.mfc-settings-grid { - display: grid; - grid-template-columns: 1fr; - position: relative; -} - -.mfc-settings-col1 { - display: grid; - align-items: center; - grid-template-columns: 1fr 1fr auto auto; - grid-column-gap: 5px; - max-width: 450px; -} - -.disabled-overlay { - width: 100%; - height: 100%; - z-index: 10; - position: absolute; - background: rgba(0, 0, 0, 0.25); +.mfc-settings-grid { + display: grid; + grid-template-columns: 1fr; + position: relative; +} + +.mfc-settings-col1 { + display: grid; + align-items: center; + grid-template-columns: 1fr 1fr auto auto; + grid-column-gap: 5px; + max-width: 450px; +} + +.disabled-overlay { + width: 100%; + height: 100%; + z-index: 10; + position: absolute; + background: rgba(0, 0, 0, 0.25); } \ No newline at end of file diff --git a/UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor b/UI/Features/Devices/Mfc/MfcUnitControl.razor similarity index 97% rename from UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor rename to UI/Features/Devices/Mfc/MfcUnitControl.razor index 2632bbb2..9ad4a6bc 100644 --- a/UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor +++ b/UI/Features/Devices/Mfc/MfcUnitControl.razor @@ -1,143 +1,143 @@ -@using Ares.Alicat.Mfc.Messaging -@using UI.Backend.Devices -@using UI.Backend.ViewModels.Devices.Mfc - -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
-
- [@ViewModel!.AssumedId] -
-
- @ViewModel!.MfcName -
- @if (ViewModel!.HasValidData) - { -
- @(ViewModel!.SelectedGas) -
-
- @if (ViewModel!.AbsolutePressure is not null) - { -
Absolute Pressure
-
@($"{ViewModel!.AbsolutePressure}")
- - } - - @if (ViewModel!.Temperature is not null) - { -
Temperature
-
@($"{ViewModel!.Temperature}")
- } - - @if (ViewModel!.VolumetricFlow is not null) - { -
Volumetric Flow
-
@($"{ViewModel!.VolumetricFlow}")
- } - - @if (ViewModel!.MassFlow is not null) - { -
Mass Flow
-
@($"{ViewModel!.MassFlow}")
- - } - - @if (ViewModel!.Setpoint is not null && ViewModel!.HasValve) - { -
Setpoint
-
@($"{ViewModel!.Setpoint}")
-
New Setpoint
- - - } - - @if(ViewModel!.ValveDrive is not null) - { -
Valve Drive
-
@($"{ViewModel!.ValveDrive}")
- } -
- @if (ViewModel!.Setpoint is not null && ViewModel!.HasValve) - { -
- Valve Hold Control -
- - -
-
- } -
- @foreach (var stat in ViewModel!.StatusCodes.Items) - { -
@stat.ToString().ToUpper()
- } -
- } - else - { - Loading MFC - - } -
- -@code { - private bool _waitingOnHoldCurrent; - private bool _waitingOnHoldClose; - private bool _cancellingHold; - - private bool ValveInteracting => _cancellingHold || _waitingOnHoldClose || _waitingOnHoldCurrent; - private bool ValveHolding => ViewModel!.StatusCodes.Items.Contains(Status.Hld); - - private async Task CancelHoldClicked() - { - _cancellingHold = true; - await ViewModel!.CancelValveHold(); - _cancellingHold = false; - StateHasChanged(); - } - - private async Task HoldCurrentClicked() - { - _waitingOnHoldCurrent = true; - await ViewModel!.HoldValvesAtCurrentPosition(); - _waitingOnHoldCurrent = false; - StateHasChanged(); - } - - private async Task HoldCloseClicked() - { - _waitingOnHoldClose = true; - await ViewModel!.HoldValvesClose(); - _waitingOnHoldClose = false; - StateHasChanged(); - } +@using Ares.Alicat.Mfc.Messaging +@using UI.Backend.Devices +@using UI.Backend.ViewModels.Devices.Mfc + +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+
+ [@ViewModel!.AssumedId] +
+
+ @ViewModel!.MfcName +
+ @if (ViewModel!.HasValidData) + { +
+ @(ViewModel!.SelectedGas) +
+
+ @if (ViewModel!.AbsolutePressure is not null) + { +
Absolute Pressure
+
@($"{ViewModel!.AbsolutePressure}")
+ + } + + @if (ViewModel!.Temperature is not null) + { +
Temperature
+
@($"{ViewModel!.Temperature}")
+ } + + @if (ViewModel!.VolumetricFlow is not null) + { +
Volumetric Flow
+
@($"{ViewModel!.VolumetricFlow}")
+ } + + @if (ViewModel!.MassFlow is not null) + { +
Mass Flow
+
@($"{ViewModel!.MassFlow}")
+ + } + + @if (ViewModel!.Setpoint is not null && ViewModel!.HasValve) + { +
Setpoint
+
@($"{ViewModel!.Setpoint}")
+
New Setpoint
+ + + } + + @if(ViewModel!.ValveDrive is not null) + { +
Valve Drive
+
@($"{ViewModel!.ValveDrive}")
+ } +
+ @if (ViewModel!.Setpoint is not null && ViewModel!.HasValve) + { +
+ Valve Hold Control +
+ + +
+
+ } +
+ @foreach (var stat in ViewModel!.StatusCodes.Items) + { +
@stat.ToString().ToUpper()
+ } +
+ } + else + { + Loading MFC + + } +
+ +@code { + private bool _waitingOnHoldCurrent; + private bool _waitingOnHoldClose; + private bool _cancellingHold; + + private bool ValveInteracting => _cancellingHold || _waitingOnHoldClose || _waitingOnHoldCurrent; + private bool ValveHolding => ViewModel!.StatusCodes.Items.Contains(Status.Hld); + + private async Task CancelHoldClicked() + { + _cancellingHold = true; + await ViewModel!.CancelValveHold(); + _cancellingHold = false; + StateHasChanged(); + } + + private async Task HoldCurrentClicked() + { + _waitingOnHoldCurrent = true; + await ViewModel!.HoldValvesAtCurrentPosition(); + _waitingOnHoldCurrent = false; + StateHasChanged(); + } + + private async Task HoldCloseClicked() + { + _waitingOnHoldClose = true; + await ViewModel!.HoldValvesClose(); + _waitingOnHoldClose = false; + StateHasChanged(); + } } \ No newline at end of file diff --git a/UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor.css b/UI/Features/Devices/Mfc/MfcUnitControl.razor.css similarity index 93% rename from UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor.css rename to UI/Features/Devices/Mfc/MfcUnitControl.razor.css index 91fbd190..1338c244 100644 --- a/UI/Pages/Shared/Devices/Mfc/MfcUnitControl.razor.css +++ b/UI/Features/Devices/Mfc/MfcUnitControl.razor.css @@ -1,114 +1,114 @@ -.mfc-container { - display: grid; - grid-template: "header" - "state" - "valve-control" - "status-codes" 1fr - / auto; -} - -.mfc-gas { - grid-area: header; - font-size: 1.5em; - font-weight: bolder; -} - -.mfc-title { - grid-area: header; - align-self: start; - justify-self: center; - font-weight: bold; -} - -.mfc-state { - grid-area: state; - padding: 5px; - display: grid; - grid-template-columns: repeat(3, auto); - grid-gap: 5px; - align-items: center; -} - -.mfc-temperature { - grid-row: 1; -} - -.mfc-absolute-pressure { - grid-row: 2; -} - -.mfc-volumetric-flow { - grid-row: 3; -} - -.mfc-mass-flow { - grid-row: 4; -} - -.mfc-id { - grid-area: header; - justify-self: end; - font-size: .85em; -} - -.mfc-setpoint { - grid-row: 6; -} - -.mfc-setpoint-control { - grid-row: 7; -} - -.mfc-valve-drive { - grid-row: 5; -} - -.mfc-setpoint-input { - max-width: 100px; - justify-self: stretch; -} - -.mfc-valve-hold { - grid-area: valve-hold; -} - -.field-descriptor { - font-size: 0.8em; - opacity: 0.8; -} - -.valve-control { - grid-area: valve-control; - border: 1px solid rgba(224, 255, 255, 0.2); - padding: 10px; - border-radius: 10px; - box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.3); -} - -.valve-legend { - font-size: 0.8em; -} - -.status-codes { - grid-area: status-codes; - align-items: center; -} - -.valve-control-grid { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: 36px; - grid-column-gap: 5px; -} - -.button-spinner { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.status-display { - border: 2px solid orange; - font-weight: bold; +.mfc-container { + display: grid; + grid-template: "header" + "state" + "valve-control" + "status-codes" 1fr + / auto; +} + +.mfc-gas { + grid-area: header; + font-size: 1.5em; + font-weight: bolder; +} + +.mfc-title { + grid-area: header; + align-self: start; + justify-self: center; + font-weight: bold; +} + +.mfc-state { + grid-area: state; + padding: 5px; + display: grid; + grid-template-columns: repeat(3, auto); + grid-gap: 5px; + align-items: center; +} + +.mfc-temperature { + grid-row: 1; +} + +.mfc-absolute-pressure { + grid-row: 2; +} + +.mfc-volumetric-flow { + grid-row: 3; +} + +.mfc-mass-flow { + grid-row: 4; +} + +.mfc-id { + grid-area: header; + justify-self: end; + font-size: .85em; +} + +.mfc-setpoint { + grid-row: 6; +} + +.mfc-setpoint-control { + grid-row: 7; +} + +.mfc-valve-drive { + grid-row: 5; +} + +.mfc-setpoint-input { + max-width: 100px; + justify-self: stretch; +} + +.mfc-valve-hold { + grid-area: valve-hold; +} + +.field-descriptor { + font-size: 0.8em; + opacity: 0.8; +} + +.valve-control { + grid-area: valve-control; + border: 1px solid rgba(224, 255, 255, 0.2); + padding: 10px; + border-radius: 10px; + box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.3); +} + +.valve-legend { + font-size: 0.8em; +} + +.status-codes { + grid-area: status-codes; + align-items: center; +} + +.valve-control-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 36px; + grid-column-gap: 5px; +} + +.button-spinner { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.status-display { + border: 2px solid orange; + font-weight: bold; } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor b/UI/Features/Devices/Settings/DeviceSettingsContainer.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor rename to UI/Features/Devices/Settings/DeviceSettingsContainer.razor index 4822dab2..c2d8c8bf 100644 --- a/UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor +++ b/UI/Features/Devices/Settings/DeviceSettingsContainer.razor @@ -1,131 +1,131 @@ -@using Ares.Datamodel.Device - -
-
@Name
- @if (DeviceOperationalStatusGetter is not null) - { -
- - @if (_deviceOperationalStatus is not null) - { -
-
- @_deviceOperationalStatus.OperationalState.ToString() -
- @if(_deviceOperationalStatus.OperationalState == OperationalState.Active) - { -
- @_deviceOperationalStatus.Message -
- } - else if(_deviceOperationalStatus.OperationalState == OperationalState.Inactive) - { -
- Couldn't connect to @Name. Is it running? -
- } - else - { -
- Error when connecting to device! Check notification history. -
- } - -
- } - else - { - - } -
- } -
- - - - - -
-
-
- @ChildContent -
-
- -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } - - [Parameter] - public Func? RemoveCallback { get; set; } - - [Parameter] - public Func? SettingsCallback { get; set; } - - [Parameter] - public Func? EditCallback { get; set; } - - [Parameter] - public Func? ActivateCallback { get; set; } - - [Parameter] - public Func>? DeviceOperationalStatusGetter { get; set; } - - [Parameter] - public Func? ShowDeviceInfoCallback { get; set; } - - [Parameter] - public string Name { get; set; } = "No name?"; - - private DeviceOperationalStatus? _deviceOperationalStatus; - - protected override async Task OnInitializedAsync() - { - if (DeviceOperationalStatusGetter is not null) - { - _deviceOperationalStatus = await DeviceOperationalStatusGetter(); - StateHasChanged(); - } - } - - private Task EditClicked() - { - return EditCallback is not null ? EditCallback() : Task.CompletedTask; - } - - private Task SettingsClicked() - { - return SettingsCallback is not null ? SettingsCallback() : Task.CompletedTask; - } - - private Task RemoveClicked() - { - return RemoveCallback is not null ? RemoveCallback() : Task.CompletedTask; - } - - private Task InfoClicked() - { - return ShowDeviceInfoCallback is not null ? ShowDeviceInfoCallback() : Task.CompletedTask; - } - - private async Task UpdateDeviceOperationalStatus() - { - if (DeviceOperationalStatusGetter is null) - return; - - _deviceOperationalStatus = null; - _deviceOperationalStatus = await DeviceOperationalStatusGetter(); - } - - private async Task ActivateClicked() - { - if (ActivateCallback is null) - return; - - await ActivateCallback(); - await UpdateDeviceOperationalStatus(); - } - -} +@using Ares.Datamodel.Device + +
+
@Name
+ @if (DeviceOperationalStatusGetter is not null) + { +
+ + @if (_deviceOperationalStatus is not null) + { +
+
+ @_deviceOperationalStatus.OperationalState.ToString() +
+ @if(_deviceOperationalStatus.OperationalState == OperationalState.Active) + { +
+ @_deviceOperationalStatus.Message +
+ } + else if(_deviceOperationalStatus.OperationalState == OperationalState.Inactive) + { +
+ Couldn't connect to @Name. Is it running? +
+ } + else + { +
+ Error when connecting to device! Check notification history. +
+ } + +
+ } + else + { + + } +
+ } +
+ + + + + +
+
+
+ @ChildContent +
+
+ +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter] + public Func? RemoveCallback { get; set; } + + [Parameter] + public Func? SettingsCallback { get; set; } + + [Parameter] + public Func? EditCallback { get; set; } + + [Parameter] + public Func? ActivateCallback { get; set; } + + [Parameter] + public Func>? DeviceOperationalStatusGetter { get; set; } + + [Parameter] + public Func? ShowDeviceInfoCallback { get; set; } + + [Parameter] + public string Name { get; set; } = "No name?"; + + private DeviceOperationalStatus? _deviceOperationalStatus; + + protected override async Task OnInitializedAsync() + { + if (DeviceOperationalStatusGetter is not null) + { + _deviceOperationalStatus = await DeviceOperationalStatusGetter(); + StateHasChanged(); + } + } + + private Task EditClicked() + { + return EditCallback is not null ? EditCallback() : Task.CompletedTask; + } + + private Task SettingsClicked() + { + return SettingsCallback is not null ? SettingsCallback() : Task.CompletedTask; + } + + private Task RemoveClicked() + { + return RemoveCallback is not null ? RemoveCallback() : Task.CompletedTask; + } + + private Task InfoClicked() + { + return ShowDeviceInfoCallback is not null ? ShowDeviceInfoCallback() : Task.CompletedTask; + } + + private async Task UpdateDeviceOperationalStatus() + { + if (DeviceOperationalStatusGetter is null) + return; + + _deviceOperationalStatus = null; + _deviceOperationalStatus = await DeviceOperationalStatusGetter(); + } + + private async Task ActivateClicked() + { + if (ActivateCallback is null) + return; + + await ActivateCallback(); + await UpdateDeviceOperationalStatus(); + } + +} diff --git a/UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor.css b/UI/Features/Devices/Settings/DeviceSettingsContainer.razor.css similarity index 94% rename from UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor.css rename to UI/Features/Devices/Settings/DeviceSettingsContainer.razor.css index 1fa41b42..ee9b07da 100644 --- a/UI/Pages/Shared/Settings/Device/DeviceSettingsContainer.razor.css +++ b/UI/Features/Devices/Settings/DeviceSettingsContainer.razor.css @@ -1,52 +1,52 @@ -.device-settings-grid { - display: grid; - background: rgba(224, 255, 255, 0.1); - padding: 10px; - /*border: 1px solid rgba(224, 255, 255, 0.3);*/ - border-radius: 5px; - grid-template: - "name status actions" auto - "separator separator separator" auto - "content content content" auto - / 1fr 1fr auto; - box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.7); -} - -.device-settings-content { - grid-area: content; -} - -.device-status { - grid-area: status; - font-size: 0.75em; -} - -.device-status .device-status-active { - color: green; - } - -.device-status .device-status-inactive { - color: grey; - } - -.device-status .device-status-error { - color: darkred; - } - -.device-settings-name { - grid-area: name; -} - -.device-settings-actions { - grid-area: actions; - justify-self: right; -} - -.device-settings-separator { - grid-area: separator; - margin: 5px; -} - -.device-action-button { - margin: 0 2px; -} +.device-settings-grid { + display: grid; + background: rgba(224, 255, 255, 0.1); + padding: 10px; + /*border: 1px solid rgba(224, 255, 255, 0.3);*/ + border-radius: 5px; + grid-template: + "name status actions" auto + "separator separator separator" auto + "content content content" auto + / 1fr 1fr auto; + box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.7); +} + +.device-settings-content { + grid-area: content; +} + +.device-status { + grid-area: status; + font-size: 0.75em; +} + +.device-status .device-status-active { + color: green; + } + +.device-status .device-status-inactive { + color: grey; + } + +.device-status .device-status-error { + color: darkred; + } + +.device-settings-name { + grid-area: name; +} + +.device-settings-actions { + grid-area: actions; + justify-self: right; +} + +.device-settings-separator { + grid-area: separator; + margin: 5px; +} + +.device-action-button { + margin: 0 2px; +} diff --git a/UI/Pages/Shared/Settings/Device/DeviceSettingsLayout.razor b/UI/Features/Devices/Settings/DeviceSettingsLayout.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/DeviceSettingsLayout.razor rename to UI/Features/Devices/Settings/DeviceSettingsLayout.razor index 84f18e55..7f7f0539 100644 --- a/UI/Pages/Shared/Settings/Device/DeviceSettingsLayout.razor +++ b/UI/Features/Devices/Settings/DeviceSettingsLayout.razor @@ -1,33 +1,34 @@ -@inherits LayoutComponentBase -@layout SettingsLayout - -
-
- -
-
-
-
-
- @Body -
- -@code { - -} +@using UI.Features.Settings +@inherits LayoutComponentBase +@layout SettingsLayout + +
+
+ +
+
+
+
+
+ @Body +
+ +@code { + +} diff --git a/UI/Pages/Shared/Settings/Device/EmptyDevicePage.razor b/UI/Features/Devices/Settings/EmptyDeviceSettingsPage.razor similarity index 93% rename from UI/Pages/Shared/Settings/Device/EmptyDevicePage.razor rename to UI/Features/Devices/Settings/EmptyDeviceSettingsPage.razor index 4f9914f0..b05969ec 100644 --- a/UI/Pages/Shared/Settings/Device/EmptyDevicePage.razor +++ b/UI/Features/Devices/Settings/EmptyDeviceSettingsPage.razor @@ -1,5 +1,5 @@ -@page "/settings/device" -@layout DeviceSettingsLayout - -@code { -} +@page "/settings/device" +@layout DeviceSettingsLayout + +@code { +} diff --git a/UI/Pages/DeviceStates.razor b/UI/Features/Devices/Shared/DeviceStates.razor similarity index 96% rename from UI/Pages/DeviceStates.razor rename to UI/Features/Devices/Shared/DeviceStates.razor index 44b19f02..d68be57e 100644 --- a/UI/Pages/DeviceStates.razor +++ b/UI/Features/Devices/Shared/DeviceStates.razor @@ -1,22 +1,22 @@ -@page "/device_states" -@using Ares.Services -@using UI.Backend.ViewModels; -@using UI.Pages.Shared.DeviceStateLogging; -@inject DialogService DialogService -@inject AresAutomation.AresAutomationClient AutomationClient - - -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - - - -@code { - private async Task OpenExporter() - { - await DialogService.OpenAsync("State Export", dialog => - @ - ); - } -} +@page "/device_states" +@using Ares.Services +@using UI.Backend.ViewModels; +@using UI.Pages.Shared.DeviceStateLogging; +@inject DialogService DialogService +@inject AresAutomation.AresAutomationClient AutomationClient + + +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + + + +@code { + private async Task OpenExporter() + { + await DialogService.OpenAsync("State Export", dialog => + @ + ); + } +} diff --git a/UI/Pages/Safety.razor b/UI/Features/Safety/Safety.razor similarity index 100% rename from UI/Pages/Safety.razor rename to UI/Features/Safety/Safety.razor diff --git a/UI/Pages/Shared/Settings/SettingsLayout.razor b/UI/Features/Settings/SettingsLayout.razor similarity index 96% rename from UI/Pages/Shared/Settings/SettingsLayout.razor rename to UI/Features/Settings/SettingsLayout.razor index 1172213a..a981f1a2 100644 --- a/UI/Pages/Shared/Settings/SettingsLayout.razor +++ b/UI/Features/Settings/SettingsLayout.razor @@ -1,24 +1,24 @@ -@inherits LayoutComponentBase -@layout MainLayout - - -
- @Body -
- -@code { - -} +@inherits LayoutComponentBase +@layout MainLayout + + +
+ @Body +
+ +@code { + +} diff --git a/UI/Pages/Shared/Settings/SettingsLayout.razor.css b/UI/Features/Settings/SettingsLayout.razor.css similarity index 96% rename from UI/Pages/Shared/Settings/SettingsLayout.razor.css rename to UI/Features/Settings/SettingsLayout.razor.css index 165ecf57..ec4b9294 100644 --- a/UI/Pages/Shared/Settings/SettingsLayout.razor.css +++ b/UI/Features/Settings/SettingsLayout.razor.css @@ -1,4 +1,4 @@ -.settings-container { - display: grid; - grid-template-rows: auto max-content; +.settings-container { + display: grid; + grid-template-rows: auto max-content; } \ No newline at end of file diff --git a/UI/Services/Notification/IUiNotificationService.cs b/UI/Infrastructure/Interfaces/IUiNotificationService.cs similarity index 100% rename from UI/Services/Notification/IUiNotificationService.cs rename to UI/Infrastructure/Interfaces/IUiNotificationService.cs diff --git a/UI/JsInterops/CompletionItemExtensions.cs b/UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs similarity index 100% rename from UI/JsInterops/CompletionItemExtensions.cs rename to UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs diff --git a/UI/JsInterops/MonacoCompletionProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs similarity index 100% rename from UI/JsInterops/MonacoCompletionProvider.cs rename to UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs diff --git a/UI/JsInterops/MonacoDiagnosticsProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs similarity index 100% rename from UI/JsInterops/MonacoDiagnosticsProvider.cs rename to UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs diff --git a/UI/JsInterops/MonacoHoverProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs similarity index 100% rename from UI/JsInterops/MonacoHoverProvider.cs rename to UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs diff --git a/UI/JsInterops/MonacoSemanticTokensProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs similarity index 100% rename from UI/JsInterops/MonacoSemanticTokensProvider.cs rename to UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs diff --git a/UI/Scripts/monaco/ares-autocomplete-setup.ts b/UI/Infrastructure/Monaco/Scripts/ares-autocomplete-setup.ts similarity index 100% rename from UI/Scripts/monaco/ares-autocomplete-setup.ts rename to UI/Infrastructure/Monaco/Scripts/ares-autocomplete-setup.ts diff --git a/UI/Scripts/monaco/ares-diagnostics-setup.ts b/UI/Infrastructure/Monaco/Scripts/ares-diagnostics-setup.ts similarity index 100% rename from UI/Scripts/monaco/ares-diagnostics-setup.ts rename to UI/Infrastructure/Monaco/Scripts/ares-diagnostics-setup.ts diff --git a/UI/Scripts/monaco/ares-hover-setup.ts b/UI/Infrastructure/Monaco/Scripts/ares-hover-setup.ts similarity index 100% rename from UI/Scripts/monaco/ares-hover-setup.ts rename to UI/Infrastructure/Monaco/Scripts/ares-hover-setup.ts diff --git a/UI/Scripts/monaco/ares-semantic-setup.ts b/UI/Infrastructure/Monaco/Scripts/ares-semantic-setup.ts similarity index 100% rename from UI/Scripts/monaco/ares-semantic-setup.ts rename to UI/Infrastructure/Monaco/Scripts/ares-semantic-setup.ts diff --git a/UI/Scripts/monaco/ares-setup.ts b/UI/Infrastructure/Monaco/Scripts/ares-setup.ts similarity index 100% rename from UI/Scripts/monaco/ares-setup.ts rename to UI/Infrastructure/Monaco/Scripts/ares-setup.ts diff --git a/UI/Scripts/monaco/areslang.language-configuration.ts b/UI/Infrastructure/Monaco/Scripts/areslang.language-configuration.ts similarity index 100% rename from UI/Scripts/monaco/areslang.language-configuration.ts rename to UI/Infrastructure/Monaco/Scripts/areslang.language-configuration.ts diff --git a/UI/Scripts/monaco/areslang.monarch.ts b/UI/Infrastructure/Monaco/Scripts/areslang.monarch.ts similarity index 100% rename from UI/Scripts/monaco/areslang.monarch.ts rename to UI/Infrastructure/Monaco/Scripts/areslang.monarch.ts diff --git a/UI/Scripts/globals.d.ts b/UI/Infrastructure/Monaco/Scripts/globals.d.ts similarity index 100% rename from UI/Scripts/globals.d.ts rename to UI/Infrastructure/Monaco/Scripts/globals.d.ts diff --git a/UI/NotFoundPage.razor b/UI/NotFound.razor similarity index 100% rename from UI/NotFoundPage.razor rename to UI/NotFound.razor diff --git a/UI/Pages/Shared/Settings/PlaceholderSettings.razor b/UI/Pages/Shared/Settings/PlaceholderSettings.razor deleted file mode 100644 index fe8cc907..00000000 --- a/UI/Pages/Shared/Settings/PlaceholderSettings.razor +++ /dev/null @@ -1,22 +0,0 @@ -@page "/settings/amogus" -@layout SettingsLayout - - - ⠀⠀⠀ ⠀⠀ ⣠⡴⠞⠛⠛⠛⠛⢶⣄⠀⠀
-⠀⠀⠀⢀⡾⠃⠀⠀⠀⠀⠀⠀⠀⠈⢷⡄⠀⠀
-⠀⠀⣼⡷⠶⠒⠒⠒⠶⢶⣄⠀ ⠀⢻⡆⠀⠀⠀
-⢠⡞⠁⠀⠀⠀⠀⠀⠀⠀⢻⡄⠀⠈⣧⠀⠀⠀⠀⠀
-⢸⡅⠀⠀⠀⠀⠀⠀⠀⠀⣼⠃⠀⠀⣿⠛⠛⢻⡆⠀⠀
-⠀⢹⡷⠶⣤⣤⣤⣤⣤⡾⠃⠀⠀⢻⠀⠀⠈⣧⠀⠀
-⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡄⠀⠀⣿⠀⠀⠀
-⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⣿⠀⠀⠀
-⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⣿⠀⠀⠀
-⠀⠸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢷⣤⡴⠟⠀⠀⠀
-⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠶⢤⣤⣤⠶⢾⡿
-⠀⠀⠸⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⡾⠋
-⠀⠀⠀⠈⠛⠶⠶⠶⣤⣀⠀⠀⠀⠀⢠⡶⠞⠛⠒⠛⠋⠁⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠳⠶⠴⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-
-@code { - -} diff --git a/UI/Pages/Shared/Settings/PlaceholderSettings.razor.css b/UI/Pages/Shared/Settings/PlaceholderSettings.razor.css deleted file mode 100644 index 5cb1baed..00000000 --- a/UI/Pages/Shared/Settings/PlaceholderSettings.razor.css +++ /dev/null @@ -1,7 +0,0 @@ -.amogus { - color: red; -} - -.amogus-visor { - color: lightcyan; -} diff --git a/UI/Routes.razor b/UI/Routes.razor index 5ad737e6..a63d56df 100644 --- a/UI/Routes.razor +++ b/UI/Routes.razor @@ -1,11 +1,6 @@ - + - - - - - \ No newline at end of file diff --git a/UI/Scripts/monaco/README.md b/UI/Scripts/monaco/README.md deleted file mode 100644 index 2b3d12c1..00000000 --- a/UI/Scripts/monaco/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Monaco Editor ARES Language Integration - -This directory contains the Monaco Editor language definition for ARES. - -## Integration with BlazorMonaco - -To use this language in a Blazor project using the `BlazorMonaco` package, follow these steps: - -1. **Compile the scripts**: Ensure TypeScript is compiled so the output lives under `wwwroot/js/monaco/`. -2. **Register the language**: Import and call the registration function in your Razor component. - -### Example Razor Component Usage - -```csharp -@inject IJSRuntime JSRuntime - - - -@code { - private MonacoEditor _editor; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - // Import the setup script as a JS Module - // Ensure the path matches your actual file structure in wwwroot - var module = await JSRuntime.InvokeAsync("import", "./js/monaco/ares-setup.js"); - - // Register the language with Monaco - await module.InvokeVoidAsync("registerAresLanguage"); - } - } - - private StandaloneEditorConstructionOptions EditorConstructionOptions(MonacoEditor editor) - { - return new StandaloneEditorConstructionOptions - { - Language = "ares", - Theme = "vs-dark", - AutomaticLayout = true, - Value = "# Welcome to ARES\ndef main():\n return True\n" - }; - } -} -``` - -## Files -- `areslang.monarch.ts`: Tokenization rules (syntax highlighting). -- `areslang.language-configuration.ts`: Language features (brackets, comments, indentation). -- `ares-setup.ts`: Glue script to register the language with the Monaco global instance. diff --git a/UI/Services/CampaignEdit/CampaignEditContext.cs b/UI/Services/CampaignEdit/CampaignEditContext.cs index ee92a511..e676ea48 100644 --- a/UI/Services/CampaignEdit/CampaignEditContext.cs +++ b/UI/Services/CampaignEdit/CampaignEditContext.cs @@ -3,7 +3,7 @@ namespace UI.Services.CampaignEdit; /// -/// Kind of a hacky way to give certain designers a way to reference any needed info from the campaign itself. +/// Kind of a hacky way to give certain designers a way to reference any necessary info from the campaign itself. /// Ex.: Campaign template has plannable parameters, but the command parameter designer does not have a way to get to those /// unless they are passed through all the way from the campaign template to those designers or if we use something /// like this context in scoped context (one scope can edit one campaign at a time anyways). diff --git a/UI/UI.csproj b/UI/UI.csproj index ef23ffef..3fd8a6c5 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -39,7 +39,13 @@ + + + + + + @@ -71,4 +77,16 @@ + + + + + + + + + + + + diff --git a/UI/tsconfig.json b/UI/tsconfig.json index 34081e64..a3d17ec8 100644 --- a/UI/tsconfig.json +++ b/UI/tsconfig.json @@ -4,13 +4,13 @@ "moduleResolution": "bundler", "target": "ES6", "sourceMap": true, - "outDir": "wwwroot/js", - "rootDir": "Scripts", + "outDir": "wwwroot/js/monaco", + "rootDir": "Infrastructure/Monaco/Scripts", "strict": true, "esModuleInterop": true }, "include": [ - "Scripts/**/*" + "Infrastructure/Monaco/Scripts/**/*" ], "exclude": [ "node_modules", From fdf379012f4d9616a2531ad92e683f57440b6e04 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 11:50:44 -0500 Subject: [PATCH 02/17] Moved devices to their own features --- AGENTS.md | 7 +- .../Repos/DeviceControlViewModelRepo.cs | 2 +- .../CampaignEdit/CampaignDesignerViewModel.cs | 1 - .../CampaignEdit/CloseoutDesignerViewModel.cs | 2 +- .../CampaignEdit/CommandDesignerViewModel.cs | 2 +- .../ExperimentDesignerViewModel.cs | 2 +- .../Factories/CloseoutDesignerFactory.cs | 1 - .../Factories/CommandDesignerFactory.cs | 3 +- .../CommandParameterDesignerFactory.cs | 3 +- .../Factories/ExperimentDesignerFactory.cs | 3 +- .../Factories/MetadataPickerFactory.cs | 3 +- .../Factories/ParameterEditorFactory.cs | 3 +- .../PlannableParameterDesignerFactory.cs | 3 +- .../Factories/PlanningDesignerFactory.cs | 3 +- .../Factories/StartupDesignerFactory.cs | 1 - .../Factories/StepDesignerFactory.cs | 3 +- .../PlannableParameterDesignerViewModel.cs | 2 +- .../CampaignEdit/StartupDesignerViewModel.cs | 2 +- .../CampaignEdit/StepDesignerViewModel.cs | 2 +- .../ViewModels/ScriptPlaygroundViewModel.cs | 2 +- .../Logging/LoggingSettingsListViewModel.cs | 2 +- .../Layouts}/MainLayout.razor | 290 ++++++------ .../Layouts}/MainLayout.razor.css | 82 ++-- .../Layouts}/SettingsLayout.razor | 0 .../Layouts}/SettingsLayout.razor.css | 0 UI/{Data => Domain}/ApplicationDbContext.cs | 18 +- .../DeviceStateExporter.razor | 3 +- .../DeviceStateExporterViewModel.cs | 4 +- .../DeviceStates.razor | 1 - .../DeviceStatesViewModel.cs | 58 +-- .../CombinedDeviceGetter.cs | 5 +- .../DeviceStateFilterView.razor | 108 ++--- .../DeviceStateFilterView.razor.css | 20 +- .../DeviceStateFilterViewModel.cs | 171 +++---- .../DeviceStateFilterViewModelFactory.cs | 35 +- .../ICombinedDeviceGetter.cs | 2 +- .../CM3CamDeviceControlViewModelFactory.cs | 3 +- .../CM3Camera/CM3CameraConfigEditView.razor | 2 +- .../CM3CameraConfigEditView.razor.css | 0 .../CM3Camera/CM3CameraConfigEditViewModel.cs | 2 +- .../CM3CameraControlWidgetView.razor | 0 .../CM3Camera/CM3CameraSettingsList.razor | 1 + .../CM3CameraSettingsListViewModel.cs | 1 + .../CM3Camera/CM3CameraSettingsView.razor | 3 +- .../CM3Camera/CM3CameraSettingsViewModel.cs | 12 +- .../CM3CameraUnitControlViewModel.cs | 4 +- .../ChemyxPump/ChemyxPumpConfigEditView.razor | 0 .../ChemyxPumpConfigEditViewModel.cs | 1 - .../ChemyxPumpControlViewModelFactory.cs | 4 +- .../ChemyxPump/ChemyxPumpSettingsList.razor | 3 +- .../ChemyxPumpSettingsListViewModel.cs | 1 + .../ChemyxPump/ChemyxPumpSettingsView.razor | 3 +- .../ChemyxPump/ChemyxPumpSettingsViewModel.cs | 5 +- .../ChemyxPump/ChemyxPumpUnitControl.razor | 0 .../ChemyxPumpUnitControlViewModel.cs | 7 +- .../Devices/ChemyxPump/IndividualPump.razor | 0 .../ChemyxPump/IndividualPumpViewModel.cs | 0 .../Devices/ChemyxPump/Syringe.razor | 0 .../LaserChillerConfigEditView.razor | 0 .../LaserChillerConfigEditViewModel.cs | 0 .../LaserChillerControlWidgetView.razor | 0 .../LaserChillerSettingsList.razor | 1 + .../LaserChillerSettingsListViewModel.cs | 1 + .../LaserChillerSettingsView.razor | 3 +- .../LaserChillerSettingsViewModel.cs | 13 +- .../LaserChillerUnitControlViewModel.cs | 0 .../Devices/Mfc}/CharacterRangeAttribute.cs | 88 ++-- .../Mfc}/MFCDeviceControlViewModelFactory.cs | 3 +- .../Devices}/Mfc/MfcConfigEditViewModel.cs | 273 ++++++------ UI/Features/Devices/Mfc/MfcSettingsList.razor | 4 +- .../Devices}/Mfc/MfcSettingsListViewModel.cs | 121 +++-- UI/Features/Devices/Mfc/MfcSettingsView.razor | 3 +- .../Devices}/Mfc/MfcSettingsViewModel.cs | 2 +- UI/Features/Devices/Mfc/MfcUnitControl.razor | 1 - .../Devices/Mfc/MfcUnitControlViewModel.cs | 416 +++++++++--------- .../Remote/RemoteDeviceConfigEditView.razor | 36 +- .../Remote/RemoteDeviceConfigEditViewModel.cs | 118 ++--- .../RemoteDeviceControlViewModelFactory.cs | 5 +- .../RemoteDeviceSettingsEditorView.razor | 0 .../Remote/RemoteDeviceSettingsList.razor | 0 .../RemoteDeviceSettingsListViewModel.cs | 1 + .../Remote/RemoteDeviceSettingsView.razor | 0 .../Remote/RemoteDeviceSettingsViewModel.cs | 3 +- .../Devices/Remote/RemoteDeviceUnitView.razor | 3 +- .../Remote/RemoteDeviceUnitView.razor.css | 0 .../Remote/RemoteDeviceUnitViewModel.cs | 6 +- .../RestDevice/RestDeviceConfigEditView.razor | 2 +- .../RestDeviceConfigEditViewModel.cs | 2 +- .../RestDevice/RestDeviceSettingsList.razor | 1 + .../RestDeviceSettingsListViewModel.cs | 1 + .../RestDevice/RestDeviceSettingsView.razor | 3 +- .../RestDevice/RestDeviceSettingsViewModel.cs | 10 +- .../RestDevice/RestDeviceUnitControl.razor | 3 +- .../RestDeviceUnitControlViewModel.cs | 3 +- .../SerialRestDeviceConfigEditView.razor | 0 .../SerialRestDeviceConfigEditViewModel.cs | 0 .../SerialRestDeviceSettingsList.razor | 1 + .../SerialRestDeviceSettingsListViewModel.cs | 1 + .../SerialRestDeviceSettingsView.razor | 3 +- .../SerialRestDeviceSettingsViewModel.cs | 118 ++--- .../SerialRestDeviceUnitControl.razor | 3 +- .../SerialRestDeviceUnitControlViewModel.cs | 3 +- .../Devices}/Servo/ServoConfigEditView.razor | 76 ++-- .../Servo/ServoConfigEditViewModel.cs | 148 +++---- .../Servo/ServoControlWidgetView.razor | 3 +- .../Servo/ServoControlWidgetView.razor.css | 4 +- .../ServoDeviceControlViewModelFactory.cs | 4 +- .../Devices}/Servo/ServoSettingsList.razor | 1 + .../Servo/ServoSettingsListViewModel.cs | 115 ++--- .../Devices}/Servo/ServoSettingsView.razor | 3 +- .../Devices}/Servo/ServoSettingsViewModel.cs | 141 +++--- .../Servo}/ServoUnitControlViewModel.cs | 62 +-- .../Devices/Shared}/ConnectionStatus.cs | 24 +- .../Devices/Shared}/DeviceAdapterManager.cs | 246 +++++------ .../Shared}/DeviceAdapterRepository.cs | 102 ++--- .../Devices/Shared}/DeviceDeletedMessage.cs | 2 +- .../Devices/Shared}/DeviceInfoContainer.razor | 0 .../Devices/Shared}/IAresDeviceAdapter.cs | 38 +- .../Devices/Shared}/RemoteDeviceAdapter.cs | 54 +-- .../Shared}/RemoteDeviceAdapterMonitor.cs | 272 ++++++------ .../Settings/DeviceSettingsContainer.razor | 0 .../DeviceSettingsContainer.razor.css | 0 .../Settings/DeviceSettingsLayout.razor | 2 +- .../Settings/EmptyDeviceSettingsPage.razor | 0 .../StepperController/BoolVisualizer.razor | 38 +- .../StepperController/SimpleDataDisplay.razor | 24 +- .../StepperControllerConfigEditView.razor | 176 ++++---- .../StepperControllerConfigEditView.razor.css | 106 ++--- .../StepperControllerConfigEditViewModel.cs | 348 +++++++-------- ...ControllerDeviceControlViewModelFactory.cs | 3 +- .../StepperControllerSettingsListView.razor | 1 + .../StepperControllerSettingsListViewModel.cs | 114 ++--- .../StepperControllerSettingsView.razor | 147 ++++--- .../StepperControllerSettingsView.razor.css | 40 +- .../StepperControllerSettingsViewModel.cs | 206 ++++----- .../StepperControllerViewModel.cs | 268 +++++------ .../StepperControllerWidgetView.razor | 288 ++++++------ .../StepperControllerWidgetView.razor.css | 14 +- .../SyringePumpConfigEditView.razor | 64 +-- .../SyringePumpConfigEditView.razor.css | 58 +-- .../SyringePumpConfigEditViewModel.cs | 164 +++---- ...yringePumpDeviceControlViewModelFactory.cs | 3 +- .../SyringePump/SyringePumpSettingsList.razor | 1 + .../SyringePumpSettingsListViewModel.cs | 115 ++--- .../SyringePump/SyringePumpSettingsView.razor | 83 ++-- .../SyringePumpSettingsViewModel.cs | 145 +++--- .../SyringePump/SyringePumpUnitControl.razor | 192 ++++---- .../SyringePumpUnitControlViewModel.cs | 310 ++++++------- .../Tc0304/DataloggerSettingsList.razor | 1 + .../Tc0304/DataloggerSettingsView.razor | 83 ++-- .../Tc0304/Tc0304ConfigEditView.razor | 74 ++-- .../Tc0304/Tc0304ConfigEditViewModel.cs | 152 +++---- .../Tc0304}/Tc0304ControlWidgetView.razor | 40 +- .../Tc0304}/Tc0304ControlWidgetView.razor.css | 14 +- .../Tc0304DeviceControlViewModelFactory.cs | 3 +- .../Tc0304/Tc0304SettingsListViewModel.cs | 115 ++--- .../Tc0304/Tc0304SettingsViewModel.cs | 157 +++---- .../Tc0304/Tc0304UnitControlViewModel.cs | 180 ++++---- .../TubeFurnaceConfigEditView.razor | 68 +-- .../TubeFurnaceConfigEditViewModel.cs | 200 ++++----- .../TubeFurnace/TubeFurnaceControl.razor | 162 +++---- ...ubeFurnaceDeviceControlViewModelFactory.cs | 3 +- .../TubeFurnaceSettingsListView.razor | 131 +++--- .../TubeFurnaceSettingsListViewModel.cs | 116 ++--- .../TubeFurnace/TubeFurnaceSettingsView.razor | 147 ++++--- .../TubeFurnaceSettingsViewModel.cs | 180 ++++---- .../TubeFurnace/TubeFurnaceViewModel.cs | 202 ++++----- .../ValveControllerConfigEditView.razor | 78 ++-- .../ValveControllerConfigEditViewModel.cs | 152 +++---- ...ControllerDeviceControlViewModelFactory.cs | 4 +- .../ValveControllerSettingsList.razor | 131 +++--- .../ValveControllerSettingsListViewModel.cs | 119 ++--- .../ValveControllerSettingsView.razor | 81 ++-- .../ValveControllerSettingsViewModel.cs | 145 +++--- .../ValveControllerUnitControlViewModel.cs | 70 +-- .../ValveControllerWidgetView.razor | 89 ++-- .../VerdiLaserConfigEditView.razor | 0 .../VerdiLaserConfigEditViewModel.cs | 0 .../VerdiLaserControlWidgetView.razor | 1 + .../VerdiV6Laser/VerdiLaserSettingsList.razor | 1 + .../VerdiLaserSettingsListViewModel.cs | 1 + .../VerdiV6Laser/VerdiLaserSettingsView.razor | 3 +- .../VerdiLaserSettingsViewModel.cs | 13 +- .../VerdiLaserUnitControlViewModel.cs | 4 +- .../Interfaces/IUiNotificationService.cs | 4 +- .../Interops/CompletionItemExtensions.cs | 2 +- .../Interops/MonacoCompletionProvider.cs | 2 +- .../Interops/MonacoDiagnosticsProvider.cs | 2 +- .../Monaco/Interops/MonacoHoverProvider.cs | 7 +- .../Interops/MonacoSemanticTokensProvider.cs | 2 +- UI/NotFound.razor | 2 + UI/Pages/Automation/CampaignDesigner.razor | 1 + UI/Pages/Automation/Execution.razor | 3 +- UI/Pages/Index.razor | 17 +- .../CommandDesignerQuickView.razor | 1 + UI/Pages/Shared/ScriptEditor.razor | 2 +- .../Layout/AnalyzerSettingsLayout.razor | 3 +- .../Logging/LoggingSettingsList.razor | 1 + .../Layout/PlannerSettingsLayout.razor | 3 +- UI/Program.cs | 2 +- UI/Routes.razor | 3 +- UI/ServiceCollectionExtensions.cs | 20 +- UI/ServiceStarter.cs | 15 +- .../NotificationReceivingService.cs | 1 + .../RadzenUiNotificationService.cs | 1 + .../UiNotificationServiceExtensions.cs | 2 + .../ServerHealthNotificationService.cs | 1 + UI/UI.csproj | 21 +- UI/_Imports.razor | 2 + 209 files changed, 4728 insertions(+), 4673 deletions(-) rename UI/{Pages/Shared => Components/Layouts}/MainLayout.razor (97%) rename UI/{Pages/Shared => Components/Layouts}/MainLayout.razor.css (93%) rename UI/{Features/Settings => Components/Layouts}/SettingsLayout.razor (100%) rename UI/{Features/Settings => Components/Layouts}/SettingsLayout.razor.css (100%) rename UI/{Data => Domain}/ApplicationDbContext.cs (88%) rename UI/{Pages/Shared/DeviceStateLogging => Features/DeviceStateExport}/DeviceStateExporter.razor (98%) rename UI/{Backend/ViewModels/DeviceStateLogging => Features/DeviceStateExport}/DeviceStateExporterViewModel.cs (92%) rename UI/Features/{Devices/Shared => DeviceStateExport}/DeviceStates.razor (92%) rename UI/{Backend/ViewModels => Features/DeviceStateExport}/DeviceStatesViewModel.cs (96%) rename UI/{Backend/ViewModels => Features}/DeviceStateLogging/CombinedDeviceGetter.cs (95%) rename UI/{Pages/Shared => Features}/DeviceStateLogging/DeviceStateFilterView.razor (97%) rename UI/{Pages/Shared => Features}/DeviceStateLogging/DeviceStateFilterView.razor.css (94%) rename UI/{Backend/ViewModels => Features}/DeviceStateLogging/DeviceStateFilterViewModel.cs (97%) rename UI/{Backend/ViewModels => Features}/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs (85%) rename UI/{Backend/ViewModels => Features}/DeviceStateLogging/ICombinedDeviceGetter.cs (72%) rename UI/{Backend/Factories => Features/Devices/CM3Camera}/CM3CamDeviceControlViewModelFactory.cs (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/CM3Camera/CM3CameraConfigEditView.razor (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/CM3Camera/CM3CameraConfigEditView.razor.css (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/CM3Camera/CM3CameraConfigEditViewModel.cs (98%) rename UI/{Pages/Shared => Features}/Devices/CM3Camera/CM3CameraControlWidgetView.razor (100%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/CM3Camera/CM3CameraSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/CM3Camera/CM3CameraSettingsListViewModel.cs (98%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/CM3Camera/CM3CameraSettingsView.razor (89%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/CM3Camera/CM3CameraSettingsViewModel.cs (96%) rename UI/{Backend/ViewModels => Features}/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs (94%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpConfigEditView.razor (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpConfigEditViewModel.cs (98%) rename UI/{Backend/Factories => Features/Devices/ChemyxPump}/ChemyxPumpControlViewModelFactory.cs (94%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpSettingsListViewModel.cs (98%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpSettingsView.razor (90%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ChemyxPump/ChemyxPumpSettingsViewModel.cs (94%) rename UI/{Pages/Shared => Features}/Devices/ChemyxPump/ChemyxPumpUnitControl.razor (100%) rename UI/{Backend/ViewModels => Features}/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs (82%) rename UI/{Pages/Shared => Features}/Devices/ChemyxPump/IndividualPump.razor (100%) rename UI/{Backend/ViewModels => Features}/Devices/ChemyxPump/IndividualPumpViewModel.cs (100%) rename UI/{Pages/Shared => Features}/Devices/ChemyxPump/Syringe.razor (100%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/LaserChiller/LaserChillerConfigEditView.razor (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/LaserChiller/LaserChillerConfigEditViewModel.cs (100%) rename UI/{Pages/Shared => Features}/Devices/LaserChiller/LaserChillerControlWidgetView.razor (100%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/LaserChiller/LaserChillerSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/LaserChiller/LaserChillerSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/LaserChiller/LaserChillerSettingsView.razor (90%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/LaserChiller/LaserChillerSettingsViewModel.cs (94%) rename UI/{Backend/ViewModels => Features}/Devices/LaserChiller/LaserChillerUnitControlViewModel.cs (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices/Mfc}/CharacterRangeAttribute.cs (92%) rename UI/{Backend/Factories => Features/Devices/Mfc}/MFCDeviceControlViewModelFactory.cs (95%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Mfc/MfcConfigEditViewModel.cs (96%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Mfc/MfcSettingsListViewModel.cs (95%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Mfc/MfcSettingsViewModel.cs (99%) rename UI/{Backend/ViewModels => Features}/Devices/Mfc/MfcUnitControlViewModel.cs (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Remote/RemoteDeviceConfigEditView.razor (98%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Remote/RemoteDeviceConfigEditViewModel.cs (94%) rename UI/{Backend/Factories => Features/Devices/Remote}/RemoteDeviceControlViewModelFactory.cs (93%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Remote/RemoteDeviceSettingsEditorView.razor (100%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Remote/RemoteDeviceSettingsList.razor (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Remote/RemoteDeviceSettingsListViewModel.cs (98%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Remote/RemoteDeviceSettingsView.razor (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Remote/RemoteDeviceSettingsViewModel.cs (99%) rename UI/{Pages/Shared => Features}/Devices/Remote/RemoteDeviceUnitView.razor (96%) rename UI/{Pages/Shared => Features}/Devices/Remote/RemoteDeviceUnitView.razor.css (100%) rename UI/{Backend/ViewModels => Features}/Devices/Remote/RemoteDeviceUnitViewModel.cs (92%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/RestDevice/RestDeviceConfigEditView.razor (86%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/RestDevice/RestDeviceConfigEditViewModel.cs (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/RestDevice/RestDeviceSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/RestDevice/RestDeviceSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/RestDevice/RestDeviceSettingsView.razor (90%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/RestDevice/RestDeviceSettingsViewModel.cs (96%) rename UI/{Pages/Shared => Features}/Devices/RestDevice/RestDeviceUnitControl.razor (90%) rename UI/{Backend/ViewModels => Features}/Devices/RestDevice/RestDeviceUnitControlViewModel.cs (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceConfigEditView.razor (100%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceConfigEditViewModel.cs (100%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceSettingsList.razor (98%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs (98%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceSettingsView.razor (89%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs (94%) rename UI/{Pages/Shared => Features}/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor (93%) rename UI/{Backend/ViewModels => Features}/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Servo/ServoConfigEditView.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Servo/ServoConfigEditViewModel.cs (96%) rename UI/{Pages/Shared => Features}/Devices/Servo/ServoControlWidgetView.razor (90%) rename UI/{Pages/Shared => Features}/Devices/Servo/ServoControlWidgetView.razor.css (94%) rename UI/{Backend/Factories => Features/Devices/Servo}/ServoDeviceControlViewModelFactory.cs (93%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Servo/ServoSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Servo/ServoSettingsListViewModel.cs (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Servo/ServoSettingsView.razor (91%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Servo/ServoSettingsViewModel.cs (93%) rename UI/{Backend/ViewModels/Devices/HerkulexDRS => Features/Devices/Servo}/ServoUnitControlViewModel.cs (81%) rename UI/{Backend/Devices => Features/Devices/Shared}/ConnectionStatus.cs (87%) rename UI/{Backend/Devices => Features/Devices/Shared}/DeviceAdapterManager.cs (95%) rename UI/{Backend/Devices => Features/Devices/Shared}/DeviceAdapterRepository.cs (94%) rename UI/{Backend/Devices => Features/Devices/Shared}/DeviceDeletedMessage.cs (56%) rename UI/{Pages/Shared/Settings/Device => Features/Devices/Shared}/DeviceInfoContainer.razor (100%) rename UI/{Backend/Devices => Features/Devices/Shared}/IAresDeviceAdapter.cs (90%) rename UI/{Backend/Devices => Features/Devices/Shared}/RemoteDeviceAdapter.cs (99%) rename UI/{Backend/Devices => Features/Devices/Shared}/RemoteDeviceAdapterMonitor.cs (96%) rename UI/Features/Devices/{ => Shared}/Settings/DeviceSettingsContainer.razor (100%) rename UI/Features/Devices/{ => Shared}/Settings/DeviceSettingsContainer.razor.css (100%) rename UI/Features/Devices/{ => Shared}/Settings/DeviceSettingsLayout.razor (98%) rename UI/Features/Devices/{ => Shared}/Settings/EmptyDeviceSettingsPage.razor (100%) rename UI/{Pages/Shared => Features}/Devices/StepperController/BoolVisualizer.razor (94%) rename UI/{Pages/Shared => Features}/Devices/StepperController/SimpleDataDisplay.razor (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/StepperController/StepperControllerConfigEditView.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/StepperController/StepperControllerConfigEditView.razor.css (92%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/StepperController/StepperControllerConfigEditViewModel.cs (97%) rename UI/{Backend/Factories => Features/Devices/StepperController}/StepperControllerDeviceControlViewModelFactory.cs (94%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/StepperController/StepperControllerSettingsListView.razor (98%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/StepperController/StepperControllerSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/StepperController/StepperControllerSettingsView.razor (96%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/StepperController/StepperControllerSettingsView.razor.css (95%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/StepperController/StepperControllerSettingsViewModel.cs (96%) rename UI/{Backend/ViewModels => Features}/Devices/StepperController/StepperControllerViewModel.cs (94%) rename UI/{Pages/Shared => Features}/Devices/StepperController/StepperControllerWidgetView.razor (97%) rename UI/{Pages/Shared => Features}/Devices/StepperController/StepperControllerWidgetView.razor.css (94%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SyringePump/SyringePumpConfigEditView.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SyringePump/SyringePumpConfigEditView.razor.css (92%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SyringePump/SyringePumpConfigEditViewModel.cs (96%) rename UI/{Backend/Factories => Features/Devices/SyringePump}/SyringePumpDeviceControlViewModelFactory.cs (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SyringePump/SyringePumpSettingsList.razor (98%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SyringePump/SyringePumpSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/SyringePump/SyringePumpSettingsView.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/SyringePump/SyringePumpSettingsViewModel.cs (93%) rename UI/{Pages/Shared => Features}/Devices/SyringePump/SyringePumpUnitControl.razor (97%) rename UI/{Backend/ViewModels => Features}/Devices/SyringePump/SyringePumpUnitControlViewModel.cs (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Tc0304/DataloggerSettingsList.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Tc0304/DataloggerSettingsView.razor (88%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/Tc0304/Tc0304ConfigEditView.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Tc0304/Tc0304ConfigEditViewModel.cs (96%) rename UI/{Pages/Shared/Devices/DataLogger => Features/Devices/Tc0304}/Tc0304ControlWidgetView.razor (97%) rename UI/{Pages/Shared/Devices/DataLogger => Features/Devices/Tc0304}/Tc0304ControlWidgetView.razor.css (94%) rename UI/{Backend/Factories => Features/Devices/Tc0304}/Tc0304DeviceControlViewModelFactory.cs (94%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Tc0304/Tc0304SettingsListViewModel.cs (96%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/Tc0304/Tc0304SettingsViewModel.cs (93%) rename UI/{Backend/ViewModels => Features}/Devices/Tc0304/Tc0304UnitControlViewModel.cs (93%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceConfigEditView.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceConfigEditViewModel.cs (96%) rename UI/{Pages/Shared => Features}/Devices/TubeFurnace/TubeFurnaceControl.razor (96%) rename UI/{Backend/Factories => Features/Devices/TubeFurnace}/TubeFurnaceDeviceControlViewModelFactory.cs (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceSettingsListView.razor (96%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceSettingsView.razor (96%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/TubeFurnace/TubeFurnaceSettingsViewModel.cs (96%) rename UI/{Backend/ViewModels => Features}/Devices/TubeFurnace/TubeFurnaceViewModel.cs (95%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ValveController/ValveControllerConfigEditView.razor (97%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ValveController/ValveControllerConfigEditViewModel.cs (96%) rename UI/{Backend/Factories => Features/Devices/ValveController}/ValveControllerDeviceControlViewModelFactory.cs (93%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ValveController/ValveControllerSettingsList.razor (96%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ValveController/ValveControllerSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/ValveController/ValveControllerSettingsView.razor (87%) rename UI/{Backend/ViewModels/Settings/Device => Features/Devices}/ValveController/ValveControllerSettingsViewModel.cs (93%) rename UI/{Backend/ViewModels => Features}/Devices/ValveController/ValveControllerUnitControlViewModel.cs (86%) rename UI/{Pages/Shared => Features}/Devices/ValveController/ValveControllerWidgetView.razor (92%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/VerdiV6Laser/VerdiLaserConfigEditView.razor (100%) rename UI/{Backend/ViewModels/Settings/Device/VerdiLaser => Features/Devices/VerdiV6Laser}/VerdiLaserConfigEditViewModel.cs (100%) rename UI/{Pages/Shared => Features}/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/VerdiV6Laser/VerdiLaserSettingsList.razor (97%) rename UI/{Backend/ViewModels/Settings/Device/VerdiLaser => Features/Devices/VerdiV6Laser}/VerdiLaserSettingsListViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Device => Features/Devices}/VerdiV6Laser/VerdiLaserSettingsView.razor (90%) rename UI/{Backend/ViewModels/Settings/Device/VerdiLaser => Features/Devices/VerdiV6Laser}/VerdiLaserSettingsViewModel.cs (94%) rename UI/{Backend/ViewModels/Devices/VerdiLaser => Features/Devices/VerdiV6Laser}/VerdiLaserUnitControlViewModel.cs (95%) diff --git a/AGENTS.md b/AGENTS.md index 64f7dfd9..1a2d3503 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,4 +4,9 @@ This solution-level file points to project-specific agent guidance. - AresScript guidance: `ARES/AresScript/AGENTS.md` - Namespace style guidance: prefer namespace imports (`using` / `@using`) and concise type names across all files. Avoid fully qualified type names unless needed to resolve unavoidable namespace/type conflicts. -- Do not add extra empty lines at the end of a file after modifying it \ No newline at end of file +- Do not add extra empty lines at the end of a file after modifying it +- These commands will need network access and inside your sandbox you always have to run those commands +with `with_escalated_permissions: true` on the `shell` tool call and include a one-sentence justification +(e.g., "Need network access for npm install/build"). +Ensure to include the `with_escalated_permissions` for all builds, restores, migrations, installs, tests, etc +where network access is required otherwise the command will hang. \ No newline at end of file diff --git a/UI/Backend/Repos/DeviceControlViewModelRepo.cs b/UI/Backend/Repos/DeviceControlViewModelRepo.cs index bf4eec63..df820fce 100644 --- a/UI/Backend/Repos/DeviceControlViewModelRepo.cs +++ b/UI/Backend/Repos/DeviceControlViewModelRepo.cs @@ -1,7 +1,7 @@ using CommunityToolkit.Mvvm.Messaging; using DynamicData; -using UI.Backend.Devices; using UI.Backend.ViewModels; +using UI.Features.Devices.Shared; namespace UI.Backend.Repos { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs index 7e4ae828..470d0673 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs @@ -3,7 +3,6 @@ using ReactiveUI; using ReactiveUI.SourceGenerators; using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; -using UI.Backend.ViewModels.Factories; using UI.Services.CampaignEdit; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs index 6bfb100f..43ffc9c3 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs @@ -3,7 +3,7 @@ using Radzen; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs index b5bd100e..8dee55ef 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs @@ -3,7 +3,7 @@ using Ares.Services.Device; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs index 44077efc..10f05e0a 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs @@ -3,7 +3,7 @@ using Radzen; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs index 160eb94d..d766e547 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs @@ -1,7 +1,6 @@ using Ares.Datamodel.Templates; using Ares.Services; using Radzen; -using UI.Backend.ViewModels.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs index 6e9d9bff..eef4a2c8 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs @@ -1,8 +1,7 @@ using Ares.Datamodel.Templates; using Ares.Services.Device; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class CommandDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs index 2e1b1f0f..247094b0 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs @@ -1,9 +1,8 @@ using Ares.Datamodel.Templates; using UI.Backend.Helpers; -using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Services.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class CommandParameterDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs index 97160b43..7dd56fe1 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs @@ -1,9 +1,8 @@ using Ares.Datamodel.Templates; using Ares.Services; using Radzen; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class ExperimentDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs index 86c7f17d..38ddb3eb 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs @@ -1,8 +1,7 @@ using Ares.Datamodel.Templates; using Ares.Services.Device; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class MetadataPickerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs index c97a8b00..0f2d0217 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs @@ -1,8 +1,7 @@ using Ares.Datamodel.Templates; using UI.Backend.Helpers; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class ParameterEditorFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs index eb963bd6..c02026c2 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs @@ -1,7 +1,6 @@ using Ares.Datamodel.Templates; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class PlannableParameterDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs index f2a2735c..2c0e3dd5 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs @@ -1,11 +1,10 @@ using Ares.Datamodel.Templates; using Ares.Services; using Google.Protobuf.WellKnownTypes; -using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Services.Notification; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class PlanningDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs index ab240ea3..003d480b 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs @@ -1,7 +1,6 @@ using Ares.Datamodel.Templates; using Ares.Services; using Radzen; -using UI.Backend.ViewModels.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs index da7015fd..4a6d7132 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs @@ -1,7 +1,6 @@ using Ares.Datamodel.Templates; -using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Factories; +namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; public class StepDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs index 34c4e94a..543f4698 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs @@ -3,7 +3,7 @@ using DynamicData; using ReactiveUI; using UI.Backend.Extensions; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs index dafb2772..af10b02b 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs @@ -3,7 +3,7 @@ using Radzen; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs b/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs index 91674fbe..1b05a94a 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs +++ b/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs @@ -1,6 +1,6 @@ using Ares.Datamodel.Templates; using ReactiveUI; -using UI.Backend.ViewModels.Factories; +using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs b/UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs index 3c424ca1..ecb22119 100644 --- a/UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs +++ b/UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs @@ -4,7 +4,7 @@ using Grpc.Core; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.JsInterops; +using UI.Infrastructure.Monaco.Interops; namespace UI.Backend.ViewModels; diff --git a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs b/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs index e6fba9ce..9dc78ab6 100644 --- a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs +++ b/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs @@ -4,7 +4,7 @@ using Ares.Services.Device; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.DeviceStateLogging; +using UI.Features.DeviceStateLogging; using UI.Services.Notification; namespace UI.Backend.ViewModels.Settings.Logging; diff --git a/UI/Pages/Shared/MainLayout.razor b/UI/Components/Layouts/MainLayout.razor similarity index 97% rename from UI/Pages/Shared/MainLayout.razor rename to UI/Components/Layouts/MainLayout.razor index 303743b2..6cdff32a 100644 --- a/UI/Pages/Shared/MainLayout.razor +++ b/UI/Components/Layouts/MainLayout.razor @@ -1,146 +1,146 @@ -@using Ares.Services -@using Google.Protobuf.WellKnownTypes -@using UI.Services.Dialog -@using UI.Services.Notification -@inherits LayoutComponentBase - -@inject IUiDialogService dialogService -@inject AresSafetyService.AresSafetyServiceClient safetyServiceClient -@inject INotificationReceivingService notificationService - - - - -ARES - -
- - - - - -
- @* - *@ -
-
- - - - - - - - - - - - - @* Coming Soon *@ - - @* *@ - - - - - - - - - - -
- -
- - - - - - - @* Coming Soon *@ - @* *@ - - -
-
- -
-
- @Body -
- -
- @*
- -
- -
-
-
*@ -
-
- -@code { - public bool SidebarExpanded { get; set; } = true; - public bool BodyExpanded { get; set; } - - private void SidebarExpandClick() - { - SidebarExpanded = !SidebarExpanded; - BodyExpanded = !BodyExpanded; - } - - async Task ShowEmergencyStopDialog() - { - var confirmDialogOptions = new UiConfirmOptions { OkButtonText = "Yes", CancelButtonText = "No" }; - var result = await dialogService.ConfirmAsync("Are you sure you want to trigger an emergency stop?", "Emergency Stop Confirmation", confirmDialogOptions); - - if(result) - { - var emergencyStopResponse = await safetyServiceClient.RequestEmergencyStopAsync(new EmergencyStopRequest()); - AresNotification notification; - - if(emergencyStopResponse.Status == EmergencyStopStatus.Success) - { - notification = new AresNotification - { - Loiter = true, - Message = emergencyStopResponse.StatusMessage, - Title = "Successfully Processed Emergency Shutdown Request", - NotificationSeverity = Severity.Success, - Timestamp = DateTime.UtcNow.ToTimestamp() - }; - } - - else - { - notification = new AresNotification - { - Loiter = true, - Message = emergencyStopResponse.StatusMessage, - Title = "Failed to Process Emergency Shutdown Request!", - NotificationSeverity = Severity.Danger, - Timestamp = DateTime.UtcNow.ToTimestamp() - }; - } - - notificationService.PushNotification(notification); - } - - } - +@using Ares.Services +@using Google.Protobuf.WellKnownTypes +@using UI.Services.Dialog +@using UI.Services.Notification +@inherits LayoutComponentBase + +@inject IUiDialogService dialogService +@inject AresSafetyService.AresSafetyServiceClient safetyServiceClient +@inject INotificationReceivingService notificationService + + + + +ARES + +
+ + + + + +
+ @* + *@ +
+
+ + + + + + + + + + + + + @* Coming Soon *@ + + @* *@ + + + + + + + + + + +
+ +
+ + + + + + + @* Coming Soon *@ + @* *@ + + +
+
+ +
+
+ @Body +
+ +
+ @*
+ +
+ +
+
+
*@ +
+
+ +@code { + public bool SidebarExpanded { get; set; } = true; + public bool BodyExpanded { get; set; } + + private void SidebarExpandClick() + { + SidebarExpanded = !SidebarExpanded; + BodyExpanded = !BodyExpanded; + } + + async Task ShowEmergencyStopDialog() + { + var confirmDialogOptions = new UiConfirmOptions { OkButtonText = "Yes", CancelButtonText = "No" }; + var result = await dialogService.ConfirmAsync("Are you sure you want to trigger an emergency stop?", "Emergency Stop Confirmation", confirmDialogOptions); + + if(result) + { + var emergencyStopResponse = await safetyServiceClient.RequestEmergencyStopAsync(new EmergencyStopRequest()); + AresNotification notification; + + if(emergencyStopResponse.Status == EmergencyStopStatus.Success) + { + notification = new AresNotification + { + Loiter = true, + Message = emergencyStopResponse.StatusMessage, + Title = "Successfully Processed Emergency Shutdown Request", + NotificationSeverity = Severity.Success, + Timestamp = DateTime.UtcNow.ToTimestamp() + }; + } + + else + { + notification = new AresNotification + { + Loiter = true, + Message = emergencyStopResponse.StatusMessage, + Title = "Failed to Process Emergency Shutdown Request!", + NotificationSeverity = Severity.Danger, + Timestamp = DateTime.UtcNow.ToTimestamp() + }; + } + + notificationService.PushNotification(notification); + } + + } + } \ No newline at end of file diff --git a/UI/Pages/Shared/MainLayout.razor.css b/UI/Components/Layouts/MainLayout.razor.css similarity index 93% rename from UI/Pages/Shared/MainLayout.razor.css rename to UI/Components/Layouts/MainLayout.razor.css index 6ab56faa..79824cc7 100644 --- a/UI/Pages/Shared/MainLayout.razor.css +++ b/UI/Components/Layouts/MainLayout.razor.css @@ -1,42 +1,42 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.hamburger-button { - position: absolute; - z-index: 10; - padding-left: 0.9rem; -} - -.main-layout-grid { - display: grid; - grid-template: - "sidebar content" 1fr - "sidebar footer" auto - /auto 1fr; -} - -.ares-sidebar { - grid-area: sidebar; -} - -.ares-footer { - grid-area: footer; -} - -.ares-content { - grid-area: content; - overflow: auto; - display: flex; - flex-direction: column; +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.hamburger-button { + position: absolute; + z-index: 10; + padding-left: 0.9rem; +} + +.main-layout-grid { + display: grid; + grid-template: + "sidebar content" 1fr + "sidebar footer" auto + /auto 1fr; +} + +.ares-sidebar { + grid-area: sidebar; +} + +.ares-footer { + grid-area: footer; +} + +.ares-content { + grid-area: content; + overflow: auto; + display: flex; + flex-direction: column; } \ No newline at end of file diff --git a/UI/Features/Settings/SettingsLayout.razor b/UI/Components/Layouts/SettingsLayout.razor similarity index 100% rename from UI/Features/Settings/SettingsLayout.razor rename to UI/Components/Layouts/SettingsLayout.razor diff --git a/UI/Features/Settings/SettingsLayout.razor.css b/UI/Components/Layouts/SettingsLayout.razor.css similarity index 100% rename from UI/Features/Settings/SettingsLayout.razor.css rename to UI/Components/Layouts/SettingsLayout.razor.css diff --git a/UI/Data/ApplicationDbContext.cs b/UI/Domain/ApplicationDbContext.cs similarity index 88% rename from UI/Data/ApplicationDbContext.cs rename to UI/Domain/ApplicationDbContext.cs index 9006400b..9a22c470 100644 --- a/UI/Data/ApplicationDbContext.cs +++ b/UI/Domain/ApplicationDbContext.cs @@ -1,12 +1,12 @@ using Microsoft.EntityFrameworkCore; - -namespace UI.Data -{ + +namespace UI.Domain +{ public class ApplicationDbContext : DbContext - { - public ApplicationDbContext(DbContextOptions options) - : base(options) - { - } - } + { + public ApplicationDbContext(DbContextOptions options) + : base(options) + { + } + } } diff --git a/UI/Pages/Shared/DeviceStateLogging/DeviceStateExporter.razor b/UI/Features/DeviceStateExport/DeviceStateExporter.razor similarity index 98% rename from UI/Pages/Shared/DeviceStateLogging/DeviceStateExporter.razor rename to UI/Features/DeviceStateExport/DeviceStateExporter.razor index 9f59e4a2..474e377e 100644 --- a/UI/Pages/Shared/DeviceStateLogging/DeviceStateExporter.razor +++ b/UI/Features/DeviceStateExport/DeviceStateExporter.razor @@ -1,6 +1,7 @@ @using Ares.Services -@using UI.Backend.ViewModels.DeviceStateLogging; @using UI.Backend.Extensions; +@using UI.Backend.ViewModels.DeviceStateLogging; +@using UI.Features.DeviceStateLogging @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject IJSRuntime JS diff --git a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateExporterViewModel.cs b/UI/Features/DeviceStateExport/DeviceStateExporterViewModel.cs similarity index 92% rename from UI/Backend/ViewModels/DeviceStateLogging/DeviceStateExporterViewModel.cs rename to UI/Features/DeviceStateExport/DeviceStateExporterViewModel.cs index dc615aa1..03c885ac 100644 --- a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateExporterViewModel.cs +++ b/UI/Features/DeviceStateExport/DeviceStateExporterViewModel.cs @@ -1,8 +1,10 @@ using Ares.Services; using Microsoft.OpenApi; using ReactiveUI; +using UI.Backend.ViewModels.DeviceStateLogging; +using UI.Features.DeviceStateLogging; -namespace UI.Backend.ViewModels.DeviceStateLogging; +namespace UI.Features.DeviceStateExport; public class DeviceStateExporterViewModel : ReactiveObject { diff --git a/UI/Features/Devices/Shared/DeviceStates.razor b/UI/Features/DeviceStateExport/DeviceStates.razor similarity index 92% rename from UI/Features/Devices/Shared/DeviceStates.razor rename to UI/Features/DeviceStateExport/DeviceStates.razor index d68be57e..deefabfa 100644 --- a/UI/Features/Devices/Shared/DeviceStates.razor +++ b/UI/Features/DeviceStateExport/DeviceStates.razor @@ -1,7 +1,6 @@ @page "/device_states" @using Ares.Services @using UI.Backend.ViewModels; -@using UI.Pages.Shared.DeviceStateLogging; @inject DialogService DialogService @inject AresAutomation.AresAutomationClient AutomationClient diff --git a/UI/Backend/ViewModels/DeviceStatesViewModel.cs b/UI/Features/DeviceStateExport/DeviceStatesViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/DeviceStatesViewModel.cs rename to UI/Features/DeviceStateExport/DeviceStatesViewModel.cs index 61a4fe14..a1c9689a 100644 --- a/UI/Backend/ViewModels/DeviceStatesViewModel.cs +++ b/UI/Features/DeviceStateExport/DeviceStatesViewModel.cs @@ -1,29 +1,29 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels; - -public partial class DeviceStatesViewModel : ReactiveObject -{ - readonly AresDevices.AresDevicesClient _devicesClient; - - public DeviceStatesViewModel(AresDevices.AresDevicesClient devicesClient) - { - _devicesClient = devicesClient; - _devicesClient - .ListAresDevicesAsync(new Empty()).ResponseAsync - .ContinueWith(task => AvailableDevices = task.Result.AresDevices); - } - public string? SelectedDeviceName { get; set; } - - public void ChooseDevice(string deviceName) - { - - } - - [Reactive] - public partial IEnumerable? AvailableDevices { get; private set; } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace UI.Backend.ViewModels; + +public partial class DeviceStatesViewModel : ReactiveObject +{ + readonly AresDevices.AresDevicesClient _devicesClient; + + public DeviceStatesViewModel(AresDevices.AresDevicesClient devicesClient) + { + _devicesClient = devicesClient; + _devicesClient + .ListAresDevicesAsync(new Empty()).ResponseAsync + .ContinueWith(task => AvailableDevices = task.Result.AresDevices); + } + public string? SelectedDeviceName { get; set; } + + public void ChooseDevice(string deviceName) + { + + } + + [Reactive] + public partial IEnumerable? AvailableDevices { get; private set; } +} diff --git a/UI/Backend/ViewModels/DeviceStateLogging/CombinedDeviceGetter.cs b/UI/Features/DeviceStateLogging/CombinedDeviceGetter.cs similarity index 95% rename from UI/Backend/ViewModels/DeviceStateLogging/CombinedDeviceGetter.cs rename to UI/Features/DeviceStateLogging/CombinedDeviceGetter.cs index c75d5f44..225228ca 100644 --- a/UI/Backend/ViewModels/DeviceStateLogging/CombinedDeviceGetter.cs +++ b/UI/Features/DeviceStateLogging/CombinedDeviceGetter.cs @@ -1,5 +1,4 @@ -using Ares.Datamodel.Device.Remote; -using Ares.Messages.DeviceStates; +using Ares.Messages.DeviceStates; using Ares.Messages.DeviceStates.Mfc; using Ares.Messages.DeviceStates.SyringePump; using Ares.Messages.DeviceStates.Tc0304; @@ -8,7 +7,7 @@ using Ares.Services.Device; using Google.Protobuf.WellKnownTypes; -namespace UI.Backend.ViewModels.DeviceStateLogging; +namespace UI.Features.DeviceStateLogging; public class CombinedDeviceGetter : ICombinedDeviceGetter { diff --git a/UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor b/UI/Features/DeviceStateLogging/DeviceStateFilterView.razor similarity index 97% rename from UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor rename to UI/Features/DeviceStateLogging/DeviceStateFilterView.razor index 4add61bc..ecbd83ba 100644 --- a/UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor +++ b/UI/Features/DeviceStateLogging/DeviceStateFilterView.razor @@ -1,54 +1,54 @@ -@using Ares.Services -@using UI.Backend.ViewModels.DeviceStateLogging; - -@inject AresAutomation.AresAutomationClient AutomationClient - -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
- @if (ViewModel?.AvailableDevices is not null) - { -
- - - -
- } - else - { - Loading Devices... - } - - - - - - - - - - - - - - - - - - @if (ViewModel?.Campaigns is not null) - { - - } - else - { - Loading Campaigns... - } -
-@code { - private void IntervalChanged(double val) - { - ViewModel!.Interval = TimeSpan.FromMilliseconds(val); - } -} +@using Ares.Services +@using UI.Backend.ViewModels.DeviceStateLogging; + +@inject AresAutomation.AresAutomationClient AutomationClient + +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+ @if (ViewModel?.AvailableDevices is not null) + { +
+ + + +
+ } + else + { + Loading Devices... + } + + + + + + + + + + + + + + + + + + @if (ViewModel?.Campaigns is not null) + { + + } + else + { + Loading Campaigns... + } +
+@code { + private void IntervalChanged(double val) + { + ViewModel!.Interval = TimeSpan.FromMilliseconds(val); + } +} diff --git a/UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor.css b/UI/Features/DeviceStateLogging/DeviceStateFilterView.razor.css similarity index 94% rename from UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor.css rename to UI/Features/DeviceStateLogging/DeviceStateFilterView.razor.css index 980a8184..1083f3e7 100644 --- a/UI/Pages/Shared/DeviceStateLogging/DeviceStateFilterView.razor.css +++ b/UI/Features/DeviceStateLogging/DeviceStateFilterView.razor.css @@ -1,11 +1,11 @@ -.filter-grid { - display: grid; - align-items: center; - grid-row-gap: 10px; - grid-gap: 10px; - grid-template-columns: auto auto 1fr; -} - -.device-picker { - grid-column: 1 / span 3; +.filter-grid { + display: grid; + align-items: center; + grid-row-gap: 10px; + grid-gap: 10px; + grid-template-columns: auto auto 1fr; +} + +.device-picker { + grid-column: 1 / span 3; } \ No newline at end of file diff --git a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModel.cs b/UI/Features/DeviceStateLogging/DeviceStateFilterViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModel.cs rename to UI/Features/DeviceStateLogging/DeviceStateFilterViewModel.cs index d005a414..7e5a3425 100644 --- a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModel.cs +++ b/UI/Features/DeviceStateLogging/DeviceStateFilterViewModel.cs @@ -1,85 +1,86 @@ -using Ares.Datamodel; -using Ares.Datamodel.Device; -using Ares.Messages.DeviceStates; -using Ares.Services; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.DeviceStateLogging; - -public partial class DeviceStateFilterViewModel : ReactiveObject -{ - readonly AresAutomation.AresAutomationClient _automationClient; - - public DeviceStateFilterViewModel( - AresAutomation.AresAutomationClient automationClient, - ICombinedDeviceGetter deviceGetter) - { - _automationClient = automationClient; - _automationClient.GetAvailableCampaignExecutionSummariesAsync(new Empty()).ResponseAsync - .ContinueWith(task => UpdateCampaigns(task.Result)); - deviceGetter.GetAvailableDevices() - .ContinueWith(task => AvailableDevices = task.Result); - - var currentTime = DateTime.Now; - // probably don't need millisecond precision - var truncatedTime = new DateTime(currentTime.Ticks - (currentTime.Ticks % TimeSpan.TicksPerMinute)); - StartTime = truncatedTime - TimeSpan.FromHours(1); - EndTime = truncatedTime; - } - - private void UpdateCampaigns(AvailableCampaignExecutionSummariesResponse response) - { - Campaigns = response.AvailableCampaignSummaries.Select(result => new CampaignExecutionSummaryMetadata { SummaryId = result.SummaryId, CampaignName = $"result.CampaignName-{result.CompletionTime}", CompletionTime = result.CompletionTime }); - } - - [Reactive] - public partial IEnumerable? AvailableDevices { get; private set; } - - public IEnumerable? SelectedDevices { get; set; } - - [Reactive] - public partial IEnumerable? Campaigns { get; private set; } - - public async Task UpdateExperiments(string? campaignResultId) - { - Experiments = null; - var campaignResult = await _automationClient.GetCampaignSummaryAsync( - new CampaignExecutionSummaryRequest { SummaryId = campaignResultId }); - Experiments = campaignResult.ExperimentSummaries; - } - - [Reactive] - public partial IEnumerable? Experiments { get; private set; } - - public string? SelectedExperimentId { get; set; } - - public DateTime StartTime { get; set; } - - public DateTime EndTime { get; set; } - - public TimeSpan Interval { get; set; } - - public bool UseStartTime { get; set; } - public bool UseEndTime { get; set; } - public bool UseExperiment { get; set; } - - public DeviceStateRequestFilter GetStateRequestFilter() - { - var request = new DeviceStateRequestFilter - { - Start = UseStartTime ? StartTime.ToUniversalTime().ToTimestamp() : default, - End = UseEndTime ? EndTime.ToUniversalTime().ToTimestamp() : default, - CompletedExperimentId = UseExperiment ? SelectedExperimentId : string.Empty, - Interval = Interval.ToDuration() - }; - - if(SelectedDevices is not null) - request.DeviceIds.AddRange(SelectedDevices.Select(d => d.DeviceId)); - - return request; - } -} - - +using Ares.Datamodel; +using Ares.Datamodel.Device; +using Ares.Messages.DeviceStates; +using Ares.Services; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.DeviceStateLogging; + +namespace UI.Backend.ViewModels.DeviceStateLogging; + +public partial class DeviceStateFilterViewModel : ReactiveObject +{ + readonly AresAutomation.AresAutomationClient _automationClient; + + public DeviceStateFilterViewModel( + AresAutomation.AresAutomationClient automationClient, + ICombinedDeviceGetter deviceGetter) + { + _automationClient = automationClient; + _automationClient.GetAvailableCampaignExecutionSummariesAsync(new Empty()).ResponseAsync + .ContinueWith(task => UpdateCampaigns(task.Result)); + deviceGetter.GetAvailableDevices() + .ContinueWith(task => AvailableDevices = task.Result); + + var currentTime = DateTime.Now; + // probably don't need millisecond precision + var truncatedTime = new DateTime(currentTime.Ticks - (currentTime.Ticks % TimeSpan.TicksPerMinute)); + StartTime = truncatedTime - TimeSpan.FromHours(1); + EndTime = truncatedTime; + } + + private void UpdateCampaigns(AvailableCampaignExecutionSummariesResponse response) + { + Campaigns = response.AvailableCampaignSummaries.Select(result => new CampaignExecutionSummaryMetadata { SummaryId = result.SummaryId, CampaignName = $"result.CampaignName-{result.CompletionTime}", CompletionTime = result.CompletionTime }); + } + + [Reactive] + public partial IEnumerable? AvailableDevices { get; private set; } + + public IEnumerable? SelectedDevices { get; set; } + + [Reactive] + public partial IEnumerable? Campaigns { get; private set; } + + public async Task UpdateExperiments(string? campaignResultId) + { + Experiments = null; + var campaignResult = await _automationClient.GetCampaignSummaryAsync( + new CampaignExecutionSummaryRequest { SummaryId = campaignResultId }); + Experiments = campaignResult.ExperimentSummaries; + } + + [Reactive] + public partial IEnumerable? Experiments { get; private set; } + + public string? SelectedExperimentId { get; set; } + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public TimeSpan Interval { get; set; } + + public bool UseStartTime { get; set; } + public bool UseEndTime { get; set; } + public bool UseExperiment { get; set; } + + public DeviceStateRequestFilter GetStateRequestFilter() + { + var request = new DeviceStateRequestFilter + { + Start = UseStartTime ? StartTime.ToUniversalTime().ToTimestamp() : default, + End = UseEndTime ? EndTime.ToUniversalTime().ToTimestamp() : default, + CompletedExperimentId = UseExperiment ? SelectedExperimentId : string.Empty, + Interval = Interval.ToDuration() + }; + + if(SelectedDevices is not null) + request.DeviceIds.AddRange(SelectedDevices.Select(d => d.DeviceId)); + + return request; + } +} + + diff --git a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs b/UI/Features/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs similarity index 85% rename from UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs rename to UI/Features/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs index 080afd9c..fffb3873 100644 --- a/UI/Backend/ViewModels/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs +++ b/UI/Features/DeviceStateLogging/DeviceStateFilterViewModelFactory.cs @@ -1,17 +1,18 @@ -using Ares.Services; - -namespace UI.Backend.ViewModels.DeviceStateLogging; - -public class DeviceStateFilterViewModelFactory -{ - private readonly AresAutomation.AresAutomationClient _automationClient; - readonly ICombinedDeviceGetter _deviceGetter; - - public DeviceStateFilterViewModelFactory(AresAutomation.AresAutomationClient automationClient, ICombinedDeviceGetter deviceGetter) - { - _deviceGetter = deviceGetter; - _automationClient = automationClient; - } - - public DeviceStateFilterViewModel Create() => new DeviceStateFilterViewModel(_automationClient, _deviceGetter); -} +using Ares.Services; +using UI.Backend.ViewModels.DeviceStateLogging; + +namespace UI.Features.DeviceStateLogging; + +public class DeviceStateFilterViewModelFactory +{ + private readonly AresAutomation.AresAutomationClient _automationClient; + readonly ICombinedDeviceGetter _deviceGetter; + + public DeviceStateFilterViewModelFactory(AresAutomation.AresAutomationClient automationClient, ICombinedDeviceGetter deviceGetter) + { + _deviceGetter = deviceGetter; + _automationClient = automationClient; + } + + public DeviceStateFilterViewModel Create() => new DeviceStateFilterViewModel(_automationClient, _deviceGetter); +} diff --git a/UI/Backend/ViewModels/DeviceStateLogging/ICombinedDeviceGetter.cs b/UI/Features/DeviceStateLogging/ICombinedDeviceGetter.cs similarity index 72% rename from UI/Backend/ViewModels/DeviceStateLogging/ICombinedDeviceGetter.cs rename to UI/Features/DeviceStateLogging/ICombinedDeviceGetter.cs index 63314aec..b3ba7f5d 100644 --- a/UI/Backend/ViewModels/DeviceStateLogging/ICombinedDeviceGetter.cs +++ b/UI/Features/DeviceStateLogging/ICombinedDeviceGetter.cs @@ -1,6 +1,6 @@ using Ares.Messages.DeviceStates; -namespace UI.Backend.ViewModels.DeviceStateLogging; +namespace UI.Features.DeviceStateLogging; public interface ICombinedDeviceGetter { diff --git a/UI/Backend/Factories/CM3CamDeviceControlViewModelFactory.cs b/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs similarity index 95% rename from UI/Backend/Factories/CM3CamDeviceControlViewModelFactory.cs rename to UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs index 1f2027ba..0584ee36 100644 --- a/UI/Backend/Factories/CM3CamDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs @@ -2,11 +2,12 @@ using DynamicData; using FlirCM3.Services; using Google.Protobuf.WellKnownTypes; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.CM3Camera; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.CM3Camera; public class CM3CamDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraConfigEditView.razor b/UI/Features/Devices/CM3Camera/CM3CameraConfigEditView.razor similarity index 95% rename from UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraConfigEditView.razor rename to UI/Features/Devices/CM3Camera/CM3CameraConfigEditView.razor index 45a7ab31..451ed3ef 100644 --- a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraConfigEditView.razor +++ b/UI/Features/Devices/CM3Camera/CM3CameraConfigEditView.razor @@ -1,4 +1,4 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraConfigEditView.razor.css b/UI/Features/Devices/CM3Camera/CM3CameraConfigEditView.razor.css similarity index 100% rename from UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraConfigEditView.razor.css rename to UI/Features/Devices/CM3Camera/CM3CameraConfigEditView.razor.css diff --git a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraConfigEditViewModel.cs b/UI/Features/Devices/CM3Camera/CM3CameraConfigEditViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraConfigEditViewModel.cs rename to UI/Features/Devices/CM3Camera/CM3CameraConfigEditViewModel.cs index 0cef4fa9..8d32b6bc 100644 --- a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraConfigEditViewModel.cs +++ b/UI/Features/Devices/CM3Camera/CM3CameraConfigEditViewModel.cs @@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations; using Ares.Services.Device; -namespace UI.Backend.ViewModels.Settings.Device.CM3Camera +namespace UI.Features.Devices.CM3Camera { public class FlirCM3ConfigEditViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Devices/CM3Camera/CM3CameraControlWidgetView.razor b/UI/Features/Devices/CM3Camera/CM3CameraControlWidgetView.razor similarity index 100% rename from UI/Pages/Shared/Devices/CM3Camera/CM3CameraControlWidgetView.razor rename to UI/Features/Devices/CM3Camera/CM3CameraControlWidgetView.razor diff --git a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsList.razor b/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsList.razor rename to UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor index 32f21100..d143a1b8 100644 --- a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsList.razor +++ b/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/cm3camera" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.CM3Camera +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsListViewModel.cs b/UI/Features/Devices/CM3Camera/CM3CameraSettingsListViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsListViewModel.cs rename to UI/Features/Devices/CM3Camera/CM3CameraSettingsListViewModel.cs index 8bc6ce60..8e568fd1 100644 --- a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsListViewModel.cs +++ b/UI/Features/Devices/CM3Camera/CM3CameraSettingsListViewModel.cs @@ -6,6 +6,7 @@ using FlirCM3.Services; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.Devices.CM3Camera; namespace UI.Backend.ViewModels.Settings.Device.CM3Camera { diff --git a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsView.razor b/UI/Features/Devices/CM3Camera/CM3CameraSettingsView.razor similarity index 89% rename from UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsView.razor rename to UI/Features/Devices/CM3Camera/CM3CameraSettingsView.razor index bbafab3c..7572c291 100644 --- a/UI/Pages/Shared/Settings/Device/CM3Camera/CM3CameraSettingsView.razor +++ b/UI/Features/Devices/CM3Camera/CM3CameraSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsViewModel.cs b/UI/Features/Devices/CM3Camera/CM3CameraSettingsViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsViewModel.cs rename to UI/Features/Devices/CM3Camera/CM3CameraSettingsViewModel.cs index 93c2d8dc..c68fa7d2 100644 --- a/UI/Backend/ViewModels/Settings/Device/CM3Camera/CM3CameraSettingsViewModel.cs +++ b/UI/Features/Devices/CM3Camera/CM3CameraSettingsViewModel.cs @@ -5,9 +5,9 @@ using FlirCM3.Services; using Grpc.Core; using ReactiveUI; -using UI.Backend.Devices; +using UI.Features.Devices.Shared; -namespace UI.Backend.ViewModels.Settings.Device.CM3Camera; +namespace UI.Features.Devices.CM3Camera; public class CM3CameraSettingsViewModel : ReactiveObject { @@ -47,10 +47,10 @@ public Task GetDeviceOperationalStatus() public async Task Save() { var cameraConfig = EditViewModel.Save(); - var updateRequest = new UpdateCameraRequest - { - CameraId = cameraConfig.Id, - Config = cameraConfig + var updateRequest = new UpdateCameraRequest + { + CameraId = cameraConfig.Id, + Config = cameraConfig }; await _cameraClient.UpdateCM3CameraAsync(updateRequest); diff --git a/UI/Backend/ViewModels/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs b/UI/Features/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs rename to UI/Features/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs index ff018de0..53a3f2fc 100644 --- a/UI/Backend/ViewModels/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs +++ b/UI/Features/Devices/CM3Camera/CM3CameraUnitControlViewModel.cs @@ -1,6 +1,6 @@ -using FlirCM3.Services; +using FlirCM3.Services; using ReactiveUI.SourceGenerators; -using UI.Pages.Shared.Devices.CM3Camera; +using UI.Features.Devices.CM3Camera; namespace UI.Backend.ViewModels.Devices.CM3Camera; diff --git a/UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpConfigEditView.razor b/UI/Features/Devices/ChemyxPump/ChemyxPumpConfigEditView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpConfigEditView.razor rename to UI/Features/Devices/ChemyxPump/ChemyxPumpConfigEditView.razor diff --git a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpConfigEditViewModel.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpConfigEditViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpConfigEditViewModel.cs rename to UI/Features/Devices/ChemyxPump/ChemyxPumpConfigEditViewModel.cs index 5ffe6c93..b2bd07c6 100644 --- a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpConfigEditViewModel.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpConfigEditViewModel.cs @@ -2,7 +2,6 @@ using ChemyxPumpPlugin.Config; using ChemyxPumpPlugin.Services; using Google.Protobuf.WellKnownTypes; -using HerkulexDRS.Config; using ReactiveUI; using ReactiveUI.SourceGenerators; using System.ComponentModel.DataAnnotations; diff --git a/UI/Backend/Factories/ChemyxPumpControlViewModelFactory.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs similarity index 94% rename from UI/Backend/Factories/ChemyxPumpControlViewModelFactory.cs rename to UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs index b79773be..096c569a 100644 --- a/UI/Backend/Factories/ChemyxPumpControlViewModelFactory.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs @@ -2,11 +2,11 @@ using ChemyxPumpPlugin.Services; using DynamicData; using Google.Protobuf.WellKnownTypes; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; -using UI.Backend.ViewModels.Devices.ChemyxPump; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.ChemyxPump; public class ChemyxPumpControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsList.razor b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsList.razor rename to UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor index 85abc5f3..a8132af2 100644 --- a/UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsList.razor +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/chemyx" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.ChemyxPump +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService @@ -62,4 +63,4 @@ else } private bool SavingInProgress { get; set; } -} \ No newline at end of file +} diff --git a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsListViewModel.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsListViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsListViewModel.cs rename to UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsListViewModel.cs index 4b1f9d36..05b924bb 100644 --- a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsListViewModel.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsListViewModel.cs @@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.Devices.ChemyxPump; namespace UI.Backend.ViewModels.Settings.Device.ChemyxPump; diff --git a/UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsView.razor b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsView.razor similarity index 90% rename from UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsView.razor rename to UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsView.razor index 0bc6eb58..36c47b7b 100644 --- a/UI/Pages/Shared/Settings/Device/ChemyxPump/ChemyxPumpSettingsView.razor +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsViewModel.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsViewModel.cs rename to UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsViewModel.cs index c391a812..3e04665f 100644 --- a/UI/Backend/ViewModels/Settings/Device/ChemyxPump/ChemyxPumpSettingsViewModel.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsViewModel.cs @@ -5,9 +5,10 @@ using CommunityToolkit.Mvvm.Messaging; using Grpc.Core; using ReactiveUI; -using UI.Backend.Devices; +using UI.Backend.ViewModels.Settings.Device.ChemyxPump; +using UI.Features.Devices.Shared; -namespace UI.Backend.ViewModels.Settings.Device.ChemyxPump; +namespace UI.Features.Devices.ChemyxPump; public class ChemyxPumpSettingsViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Devices/ChemyxPump/ChemyxPumpUnitControl.razor b/UI/Features/Devices/ChemyxPump/ChemyxPumpUnitControl.razor similarity index 100% rename from UI/Pages/Shared/Devices/ChemyxPump/ChemyxPumpUnitControl.razor rename to UI/Features/Devices/ChemyxPump/ChemyxPumpUnitControl.razor diff --git a/UI/Backend/ViewModels/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs similarity index 82% rename from UI/Backend/ViewModels/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs rename to UI/Features/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs index 1bd0936a..e661cda3 100644 --- a/UI/Backend/ViewModels/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpUnitControlViewModel.cs @@ -1,7 +1,8 @@ -using ChemyxPumpPlugin.Services; -using UI.Pages.Shared.Devices.ChemyxPump; +using ChemyxPumpPlugin.Services; +using UI.Backend.ViewModels; +using UI.Backend.ViewModels.Devices.ChemyxPump; -namespace UI.Backend.ViewModels.Devices.ChemyxPump; +namespace UI.Features.Devices.ChemyxPump; public class ChemyxPumpUnitControlViewModel : DeviceUnitControlViewModel { diff --git a/UI/Pages/Shared/Devices/ChemyxPump/IndividualPump.razor b/UI/Features/Devices/ChemyxPump/IndividualPump.razor similarity index 100% rename from UI/Pages/Shared/Devices/ChemyxPump/IndividualPump.razor rename to UI/Features/Devices/ChemyxPump/IndividualPump.razor diff --git a/UI/Backend/ViewModels/Devices/ChemyxPump/IndividualPumpViewModel.cs b/UI/Features/Devices/ChemyxPump/IndividualPumpViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Devices/ChemyxPump/IndividualPumpViewModel.cs rename to UI/Features/Devices/ChemyxPump/IndividualPumpViewModel.cs diff --git a/UI/Pages/Shared/Devices/ChemyxPump/Syringe.razor b/UI/Features/Devices/ChemyxPump/Syringe.razor similarity index 100% rename from UI/Pages/Shared/Devices/ChemyxPump/Syringe.razor rename to UI/Features/Devices/ChemyxPump/Syringe.razor diff --git a/UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerConfigEditView.razor b/UI/Features/Devices/LaserChiller/LaserChillerConfigEditView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerConfigEditView.razor rename to UI/Features/Devices/LaserChiller/LaserChillerConfigEditView.razor diff --git a/UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerConfigEditViewModel.cs b/UI/Features/Devices/LaserChiller/LaserChillerConfigEditViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerConfigEditViewModel.cs rename to UI/Features/Devices/LaserChiller/LaserChillerConfigEditViewModel.cs diff --git a/UI/Pages/Shared/Devices/LaserChiller/LaserChillerControlWidgetView.razor b/UI/Features/Devices/LaserChiller/LaserChillerControlWidgetView.razor similarity index 100% rename from UI/Pages/Shared/Devices/LaserChiller/LaserChillerControlWidgetView.razor rename to UI/Features/Devices/LaserChiller/LaserChillerControlWidgetView.razor diff --git a/UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsList.razor b/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsList.razor rename to UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor index 5a6b6938..c13b5f8b 100644 --- a/UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsList.razor +++ b/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/laserchiller" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.LaserChiller +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsListViewModel.cs b/UI/Features/Devices/LaserChiller/LaserChillerSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsListViewModel.cs rename to UI/Features/Devices/LaserChiller/LaserChillerSettingsListViewModel.cs index c6e3272d..06d8ecbf 100644 --- a/UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsListViewModel.cs +++ b/UI/Features/Devices/LaserChiller/LaserChillerSettingsListViewModel.cs @@ -6,6 +6,7 @@ using LaserChiller; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.Devices.LaserChiller; namespace UI.Backend.ViewModels.Settings.Device.LaserChiller; diff --git a/UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsView.razor b/UI/Features/Devices/LaserChiller/LaserChillerSettingsView.razor similarity index 90% rename from UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsView.razor rename to UI/Features/Devices/LaserChiller/LaserChillerSettingsView.razor index 2ab234fa..67dd8941 100644 --- a/UI/Pages/Shared/Settings/Device/LaserChiller/LaserChillerSettingsView.razor +++ b/UI/Features/Devices/LaserChiller/LaserChillerSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsViewModel.cs b/UI/Features/Devices/LaserChiller/LaserChillerSettingsViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsViewModel.cs rename to UI/Features/Devices/LaserChiller/LaserChillerSettingsViewModel.cs index bbddc2d5..74f49c1e 100644 --- a/UI/Backend/ViewModels/Settings/Device/LaserChiller/LaserChillerSettingsViewModel.cs +++ b/UI/Features/Devices/LaserChiller/LaserChillerSettingsViewModel.cs @@ -5,9 +5,10 @@ using CommunityToolkit.Mvvm.Messaging; using Grpc.Core; using ReactiveUI; -using UI.Backend.Devices; +using UI.Backend.ViewModels.Settings.Device.LaserChiller; +using UI.Features.Devices.Shared; -namespace UI.Backend.ViewModels.Settings.Device.LaserChiller; +namespace UI.Features.Devices.LaserChiller; public class LaserChillerSettingsViewModel : ReactiveObject { @@ -47,10 +48,10 @@ public Task GetDeviceOperationalStatus() public async Task Save() { var laserConfig = EditViewModel.Save(); - var updateRequest = new UpdateChillerRequest - { - ChillerId = _deviceConfig.UniqueId, - Config = laserConfig + var updateRequest = new UpdateChillerRequest + { + ChillerId = _deviceConfig.UniqueId, + Config = laserConfig }; await _chillerClient.UpdateChillerAsync(updateRequest); diff --git a/UI/Backend/ViewModels/Devices/LaserChiller/LaserChillerUnitControlViewModel.cs b/UI/Features/Devices/LaserChiller/LaserChillerUnitControlViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Devices/LaserChiller/LaserChillerUnitControlViewModel.cs rename to UI/Features/Devices/LaserChiller/LaserChillerUnitControlViewModel.cs diff --git a/UI/Backend/ViewModels/Settings/Device/CharacterRangeAttribute.cs b/UI/Features/Devices/Mfc/CharacterRangeAttribute.cs similarity index 92% rename from UI/Backend/ViewModels/Settings/Device/CharacterRangeAttribute.cs rename to UI/Features/Devices/Mfc/CharacterRangeAttribute.cs index 5a232ade..739439e4 100644 --- a/UI/Backend/ViewModels/Settings/Device/CharacterRangeAttribute.cs +++ b/UI/Features/Devices/Mfc/CharacterRangeAttribute.cs @@ -1,44 +1,44 @@ -using System.ComponentModel.DataAnnotations; - -namespace UI.Backend.ViewModels.Settings.Device; - -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] -public class CharacterRangeAttribute : ValidationAttribute -{ - private readonly char _max; - private readonly char _min; - - public CharacterRangeAttribute(char min, char max) - { - _min = min; - _max = max; - } - - protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) - { - switch (value) - { - case string str when string.IsNullOrEmpty(str): - return ValidationResult.Success; - case string str when str.Length != 1: - return new ValidationResult("Input must be a single character."); - case string str: - { - var character = str.First(); - return CheckChar(character); - } - case char chr: - return CheckChar(chr); - } - - return new ValidationResult("Bork"); - } - - private ValidationResult? CheckChar(char character) - { - if (character >= _min && character <= _max) - return ValidationResult.Success; - - return new ValidationResult($"The character must be between {_min} and {_max}"); - } -} +using System.ComponentModel.DataAnnotations; + +namespace UI.Features.Devices.Mfc; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] +public class CharacterRangeAttribute : ValidationAttribute +{ + private readonly char _max; + private readonly char _min; + + public CharacterRangeAttribute(char min, char max) + { + _min = min; + _max = max; + } + + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) + { + switch (value) + { + case string str when string.IsNullOrEmpty(str): + return ValidationResult.Success; + case string str when str.Length != 1: + return new ValidationResult("Input must be a single character."); + case string str: + { + var character = str.First(); + return CheckChar(character); + } + case char chr: + return CheckChar(chr); + } + + return new ValidationResult("Bork"); + } + + private ValidationResult? CheckChar(char character) + { + if (character >= _min && character <= _max) + return ValidationResult.Success; + + return new ValidationResult($"The character must be between {_min} and {_max}"); + } +} diff --git a/UI/Backend/Factories/MFCDeviceControlViewModelFactory.cs b/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs similarity index 95% rename from UI/Backend/Factories/MFCDeviceControlViewModelFactory.cs rename to UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs index b5d59c50..44f558c5 100644 --- a/UI/Backend/Factories/MFCDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs @@ -3,11 +3,12 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using System.Reactive.Linq; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.Mfc; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.Mfc; public class MFCDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcConfigEditViewModel.cs b/UI/Features/Devices/Mfc/MfcConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/Mfc/MfcConfigEditViewModel.cs rename to UI/Features/Devices/Mfc/MfcConfigEditViewModel.cs index bb18d12a..bd9a01de 100644 --- a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcConfigEditViewModel.cs +++ b/UI/Features/Devices/Mfc/MfcConfigEditViewModel.cs @@ -1,136 +1,137 @@ -using System.ComponentModel.DataAnnotations; -using Ares.Alicat.Mfc.Config; -using Ares.Alicat.Mfc.Messaging; -using Ares.Services.Device; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using Enum = System.Enum; - -namespace UI.Backend.ViewModels.Settings.Device.Mfc; - -public partial class MfcConfigEditViewModel : ReactiveObject -{ - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly MfcRpc.MfcRpcClient _mfcClient; - private readonly MfcConfig _mfcConfig; - private readonly ILogger _logger; - private string? _name; - - public MfcConfigEditViewModel(MfcRpc.MfcRpcClient mfcClient, - AresDevices.AresDevicesClient devicesClient, - MfcConfig mfcConfig, - ILogger logger - ) - { - _mfcClient = mfcClient; - _devicesClient = devicesClient; - _mfcConfig = mfcConfig; - _logger = logger; - _ = UpdateAvailableSerialPorts(); - _name = _mfcConfig.Name; - Id = _mfcConfig.Id; - Port = _mfcConfig.PortName; - Simulated = _mfcConfig.Simulated; - HasValve = _mfcConfig.HasValve; - SelectedMfcType = _mfcConfig.MfcType == MfcType.None ? MfcType.Normal : _mfcConfig.MfcType; - SetpointSource = SetpointSource.UnknownSource; - } - - public MfcConfigEditViewModel(MfcRpc.MfcRpcClient mfcClient, AresDevices.AresDevicesClient devicesClient, ILogger logger) - { - _mfcClient = mfcClient; - _devicesClient = devicesClient; - _logger = logger; - _mfcConfig = new MfcConfig(); - _ = UpdateAvailableSerialPorts(); - NewConfig = true; - SetpointSource = SetpointSource.UnknownSource; - } - - [Required] - public string? Name - { - get => _name; - - set - { - if(!NewConfig) - return; - - _name = value; - } - } - - [Required] - public string? Port { get; set; } - - public bool NewConfig { get; } - - [Required] - [CharacterRange('A', 'Z', ErrorMessage = "Id must be any capital letter from A to Z")] - [StringLength(1, ErrorMessage = "Id must be one character")] - public string? Id { get; set; } - - public bool HasValve { get; set; } = true; - - public MfcType[] AvailableMfcTypes { get; } = System.Enum.GetValues(); - - public MfcType SelectedMfcType { get; set; } = MfcType.Normal; - - [Reactive] - public partial SetpointSource SetpointSource { get; private set; } - - public SetpointSource[] AvailableSetpointSources { get; } = - Enum.GetValues().Except([SetpointSource.UnknownSource]).ToArray(); - - public bool Simulated { get; set; } - - [Reactive] - public partial IEnumerable? AvailableIds { get; private set; } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public bool Modified => _mfcConfig.Id != Id || _mfcConfig.Name != Name || _mfcConfig.PortName != Port || _mfcConfig.Simulated != Simulated || _mfcConfig.HasValve != HasValve || _mfcConfig.MfcType != SelectedMfcType; - - public async Task UpdateSetpointSource(SetpointSource source) - { - if (SetpointSource == source) - return; - - try - { - await _mfcClient.SetSetpointSourceAsync(new SetSetpointSourceRequest { Id = _mfcConfig.Id, Source = source }); - var newSource = await _mfcClient.GetSetpointSourceAsync(new DeviceRequest { DeviceId = _mfcConfig.Id }); - SetpointSource = newSource.Source; - } - catch (Exception e) - { - SetpointSource = SetpointSource.UnknownSource; - _logger.LogError(e, "Failed to update setpoint source for alicat {}", _mfcConfig.Name); - } - } - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - public async Task UpdateAvailableIds() - { - AvailableIds = null; - Id = null; - if(Port is null) - return; - - var ids = await _mfcClient.GetAvailableIdsAsync(new GetAvailableIdsRequest { PortName = Port, Simulated = Simulated }); - AvailableIds = ids.Ids.Select(s => s.First()); - } - - public MfcConfig Save() - => Modified ? new MfcConfig { Id = Id, Name = Name, PortName = Port, Simulated = Simulated, HasValve = HasValve, MfcType = SelectedMfcType } : _mfcConfig; -} +using System.ComponentModel.DataAnnotations; +using Ares.Alicat.Mfc.Config; +using Ares.Alicat.Mfc.Messaging; +using Ares.Services.Device; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.Devices.Mfc; +using Enum = System.Enum; + +namespace UI.Backend.ViewModels.Settings.Device.Mfc; + +public partial class MfcConfigEditViewModel : ReactiveObject +{ + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly MfcRpc.MfcRpcClient _mfcClient; + private readonly MfcConfig _mfcConfig; + private readonly ILogger _logger; + private string? _name; + + public MfcConfigEditViewModel(MfcRpc.MfcRpcClient mfcClient, + AresDevices.AresDevicesClient devicesClient, + MfcConfig mfcConfig, + ILogger logger + ) + { + _mfcClient = mfcClient; + _devicesClient = devicesClient; + _mfcConfig = mfcConfig; + _logger = logger; + _ = UpdateAvailableSerialPorts(); + _name = _mfcConfig.Name; + Id = _mfcConfig.Id; + Port = _mfcConfig.PortName; + Simulated = _mfcConfig.Simulated; + HasValve = _mfcConfig.HasValve; + SelectedMfcType = _mfcConfig.MfcType == MfcType.None ? MfcType.Normal : _mfcConfig.MfcType; + SetpointSource = SetpointSource.UnknownSource; + } + + public MfcConfigEditViewModel(MfcRpc.MfcRpcClient mfcClient, AresDevices.AresDevicesClient devicesClient, ILogger logger) + { + _mfcClient = mfcClient; + _devicesClient = devicesClient; + _logger = logger; + _mfcConfig = new MfcConfig(); + _ = UpdateAvailableSerialPorts(); + NewConfig = true; + SetpointSource = SetpointSource.UnknownSource; + } + + [Required] + public string? Name + { + get => _name; + + set + { + if(!NewConfig) + return; + + _name = value; + } + } + + [Required] + public string? Port { get; set; } + + public bool NewConfig { get; } + + [Required] + [CharacterRange('A', 'Z', ErrorMessage = "Id must be any capital letter from A to Z")] + [StringLength(1, ErrorMessage = "Id must be one character")] + public string? Id { get; set; } + + public bool HasValve { get; set; } = true; + + public MfcType[] AvailableMfcTypes { get; } = System.Enum.GetValues(); + + public MfcType SelectedMfcType { get; set; } = MfcType.Normal; + + [Reactive] + public partial SetpointSource SetpointSource { get; private set; } + + public SetpointSource[] AvailableSetpointSources { get; } = + Enum.GetValues().Except([SetpointSource.UnknownSource]).ToArray(); + + public bool Simulated { get; set; } + + [Reactive] + public partial IEnumerable? AvailableIds { get; private set; } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public bool Modified => _mfcConfig.Id != Id || _mfcConfig.Name != Name || _mfcConfig.PortName != Port || _mfcConfig.Simulated != Simulated || _mfcConfig.HasValve != HasValve || _mfcConfig.MfcType != SelectedMfcType; + + public async Task UpdateSetpointSource(SetpointSource source) + { + if (SetpointSource == source) + return; + + try + { + await _mfcClient.SetSetpointSourceAsync(new SetSetpointSourceRequest { Id = _mfcConfig.Id, Source = source }); + var newSource = await _mfcClient.GetSetpointSourceAsync(new DeviceRequest { DeviceId = _mfcConfig.Id }); + SetpointSource = newSource.Source; + } + catch (Exception e) + { + SetpointSource = SetpointSource.UnknownSource; + _logger.LogError(e, "Failed to update setpoint source for alicat {}", _mfcConfig.Name); + } + } + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + public async Task UpdateAvailableIds() + { + AvailableIds = null; + Id = null; + if(Port is null) + return; + + var ids = await _mfcClient.GetAvailableIdsAsync(new GetAvailableIdsRequest { PortName = Port, Simulated = Simulated }); + AvailableIds = ids.Ids.Select(s => s.First()); + } + + public MfcConfig Save() + => Modified ? new MfcConfig { Id = Id, Name = Name, PortName = Port, Simulated = Simulated, HasValve = HasValve, MfcType = SelectedMfcType } : _mfcConfig; +} diff --git a/UI/Features/Devices/Mfc/MfcSettingsList.razor b/UI/Features/Devices/Mfc/MfcSettingsList.razor index 5fa45b45..c7f403d8 100644 --- a/UI/Features/Devices/Mfc/MfcSettingsList.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsList.razor @@ -1,7 +1,7 @@ @page "/settings/device/alicat" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Mfc -@using UI.Features.Devices.Settings +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService @@ -63,4 +63,4 @@ else } private bool SavingInProgress { get; set; } -} \ No newline at end of file +} diff --git a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsListViewModel.cs b/UI/Features/Devices/Mfc/MfcSettingsListViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsListViewModel.cs rename to UI/Features/Devices/Mfc/MfcSettingsListViewModel.cs index b2b29ad5..d70b9712 100644 --- a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsListViewModel.cs +++ b/UI/Features/Devices/Mfc/MfcSettingsListViewModel.cs @@ -1,61 +1,60 @@ -using AlicatMFC; -using Ares.Alicat.Mfc.Config; -using Ares.Alicat.Mfc.Messaging; -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Microsoft.Extensions.Logging; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Settings.Device.Mfc; - -public partial class MfcSettingsListViewModel : ReactiveObject -{ - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly MfcRpc.MfcRpcClient _mfcClient; - private readonly IMessenger _messenger; - private readonly ILoggerFactory _loggerFactory; - - public MfcSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, MfcRpc.MfcRpcClient mfcClient, IMessenger messenger, ILoggerFactory loggerFactory) - { - _devicesClient = devicesClient; - _mfcClient = mfcClient; - _messenger = messenger; - _loggerFactory = loggerFactory; - _ = UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new MfcSettingsViewModel(config, _mfcClient, _devicesClient, _loggerFactory, _messenger, OnConfigRemoved)).ToArray(); - SettingsViewModels = viewModels; - } - - public MfcConfigEditViewModel GetNewConfigEditViewModel() - => new(_mfcClient, _devicesClient, _loggerFactory.CreateLogger()); - - private async Task UpdateConfigs() - { - SettingsViewModels = null; - var configs = await _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IMassFlowController).FullName }); - - UpdateViewModels(configs.Configs); - } - - public async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(MfcConfig config) - { - await _mfcClient.AddMfcAsync(config); - await UpdateConfigs(); - } -} +using AlicatMFC; +using Ares.Alicat.Mfc.Config; +using Ares.Alicat.Mfc.Messaging; +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace UI.Backend.ViewModels.Settings.Device.Mfc; + +public partial class MfcSettingsListViewModel : ReactiveObject +{ + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly MfcRpc.MfcRpcClient _mfcClient; + private readonly IMessenger _messenger; + private readonly ILoggerFactory _loggerFactory; + + public MfcSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, MfcRpc.MfcRpcClient mfcClient, IMessenger messenger, ILoggerFactory loggerFactory) + { + _devicesClient = devicesClient; + _mfcClient = mfcClient; + _messenger = messenger; + _loggerFactory = loggerFactory; + _ = UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new MfcSettingsViewModel(config, _mfcClient, _devicesClient, _loggerFactory, _messenger, OnConfigRemoved)).ToArray(); + SettingsViewModels = viewModels; + } + + public MfcConfigEditViewModel GetNewConfigEditViewModel() + => new(_mfcClient, _devicesClient, _loggerFactory.CreateLogger()); + + private async Task UpdateConfigs() + { + SettingsViewModels = null; + var configs = await _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IMassFlowController).FullName }); + + UpdateViewModels(configs.Configs); + } + + public async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(MfcConfig config) + { + await _mfcClient.AddMfcAsync(config); + await UpdateConfigs(); + } +} diff --git a/UI/Features/Devices/Mfc/MfcSettingsView.razor b/UI/Features/Devices/Mfc/MfcSettingsView.razor index f3ee7301..22141f52 100644 --- a/UI/Features/Devices/Mfc/MfcSettingsView.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsView.razor @@ -1,6 +1,7 @@ @using Ares.Alicat.Mfc.Config -@using UI.Backend.ViewModels.Settings.Device.Mfc @using Ares.Alicat.Mfc.Messaging +@using UI.Backend.ViewModels.Settings.Device.Mfc +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsViewModel.cs b/UI/Features/Devices/Mfc/MfcSettingsViewModel.cs similarity index 99% rename from UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsViewModel.cs rename to UI/Features/Devices/Mfc/MfcSettingsViewModel.cs index cbf2376c..57c3c540 100644 --- a/UI/Backend/ViewModels/Settings/Device/Mfc/MfcSettingsViewModel.cs +++ b/UI/Features/Devices/Mfc/MfcSettingsViewModel.cs @@ -7,7 +7,7 @@ using Grpc.Core; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.Devices; +using UI.Features.Devices.Shared; namespace UI.Backend.ViewModels.Settings.Device.Mfc; diff --git a/UI/Features/Devices/Mfc/MfcUnitControl.razor b/UI/Features/Devices/Mfc/MfcUnitControl.razor index 9ad4a6bc..149d1715 100644 --- a/UI/Features/Devices/Mfc/MfcUnitControl.razor +++ b/UI/Features/Devices/Mfc/MfcUnitControl.razor @@ -1,5 +1,4 @@ @using Ares.Alicat.Mfc.Messaging -@using UI.Backend.Devices @using UI.Backend.ViewModels.Devices.Mfc @inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Backend/ViewModels/Devices/Mfc/MfcUnitControlViewModel.cs b/UI/Features/Devices/Mfc/MfcUnitControlViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Devices/Mfc/MfcUnitControlViewModel.cs rename to UI/Features/Devices/Mfc/MfcUnitControlViewModel.cs index 5b622e33..a4b1442d 100644 --- a/UI/Backend/ViewModels/Devices/Mfc/MfcUnitControlViewModel.cs +++ b/UI/Features/Devices/Mfc/MfcUnitControlViewModel.cs @@ -1,208 +1,208 @@ -using Ares.Alicat.Mfc.Messaging; -using DynamicData; -using ReactiveUI.SourceGenerators; -using UI.Pages.Shared.Devices.Mfc; -using UnitsNet; -using UnitsNet.Units; - -namespace UI.Backend.ViewModels.Devices.Mfc; - -public partial class MfcUnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable -{ - private readonly DeviceRequest _deviceRequest; - private readonly MfcRpc.MfcRpcClient _mfcClient; - private readonly CancellationTokenSource _stateStreamCts = new(); - private Task _stateListener = Task.CompletedTask; - - public MfcUnitControlViewModel(string mfcId, string mfcName, MfcRpc.MfcRpcClient mfcClient) : base(mfcId, mfcName) - { - MfcName = mfcName; - _deviceRequest = new DeviceRequest { DeviceId = DeviceId }; - _mfcClient = mfcClient; - ViewType = typeof(MfcUnitControl); - Initialize(); - - DefaultWidth = 19; - } - - public string MfcName { get; } - - [Reactive] - public partial int TargetGas { get; set; } - - [Reactive] - public partial double? TargetSetpoint { get; set; } - - [Reactive] - public partial bool CapturingLiveData { get; private set; } - - [Reactive] - public partial IEnumerable? AvailableGases { get; set; } - - [Reactive] - public partial string? SelectedGas { get; set; } - - [Reactive] - public partial char? AssumedId { get; set; } - - [Reactive] - public partial bool HasValve { get; private set; } - - [Reactive] - public partial Temperature? Temperature { get; set; } - - [Reactive] - public partial Pressure? AbsolutePressure { get; set; } - - [Reactive] - public partial VolumeFlow? VolumetricFlow { get; set; } - - [Reactive] - public partial StandardVolumeFlow? MassFlow { get; set; } - - [Reactive] - public partial StandardVolumeFlow? Setpoint { get; set; } - - [Reactive] - public partial double? ValveDrive { get; set; } - - [Reactive] - public partial bool HasValidData { get; private set; } - - public ISourceList StatusCodes { get; } = new SourceList(); - - public async ValueTask DisposeAsync() - { - _stateStreamCts.Cancel(); - await _stateListener; - _stateStreamCts.Dispose(); - - GC.SuppressFinalize(this); - } - - private void Initialize() - { - ListenForStates(); - } - - public void ListenForStates() - { - try - { - _stateListener = Task.Run(async () => - { - Thread.CurrentThread.Name = $"Mass Flow Controller {DeviceName} State Listener Thread"; - var state = await _mfcClient.GetStateAsync(_deviceRequest); - UpdateState(state); - CapturingLiveData = true; - try - { - while(!_stateStreamCts.Token.IsCancellationRequested) - { - var stateResponse = await _mfcClient.GetStateUpdateAsync(_deviceRequest, null, null, _stateStreamCts.Token); - UpdateState(stateResponse); - await Task.Delay(100); - } - } - catch(Exception) - { - Console.WriteLine($"~~~~~~~ Exception Getting State, Thread will die probably? ~~~~~~~"); - } - - CapturingLiveData = false; - }, - _stateStreamCts.Token); - } - catch(OperationCanceledException) - { - } - } - - private void UpdateState(StateResponse state) - { - AssumedId = state.AssumedId?.FirstOrDefault(); - AvailableGases = state.AvailableGasInfos; - if(state.Data is null) - { - HasValidData = false; - return; - } - HasValidData = true; - - if(state.Data.Temperature is not null) - { - var foundTempUnit = UnitsNet.Temperature.TryParseUnit(state.Data.Temperature.Unit, out var tempUnit); - Temperature = UnitsNet.Temperature.From(state.Data.Temperature.Value, foundTempUnit ? tempUnit : TemperatureUnit.DegreeCelsius); - } - - if(state.Data.AbsolutePressure is not null) - { - var foundAbsolutePressureUnit = Pressure.TryParseUnit(state.Data.AbsolutePressure.Unit, out var pressureUnit); - AbsolutePressure = Pressure.From(state.Data.AbsolutePressure.Value, foundAbsolutePressureUnit ? pressureUnit : PressureUnit.PoundForcePerSquareInch); - } - - if(state.Data.VolumetricFlow is not null) - { - var foundVolumetricFlowUnit = VolumeFlow.TryParseUnit(state.Data.VolumetricFlow.Unit, out var volumeFlowUnit); - VolumetricFlow = VolumeFlow.From(state.Data.VolumetricFlow.Value, foundVolumetricFlowUnit ? volumeFlowUnit : VolumeFlowUnit.CubicCentimeterPerMinute); - } - - if(state.Data.MassFlow is not null) - { - var foundMassFlowUnit = StandardVolumeFlow.TryParseUnit(state.Data.MassFlow.Unit, out var massFlowUnit); - MassFlow = StandardVolumeFlow.From(state.Data.MassFlow.Value, foundMassFlowUnit ? massFlowUnit : StandardVolumeFlowUnit.StandardLiterPerMinute); - } - - if(state.Data.Setpoint is not null) - { - var foundSetPointUnit = StandardVolumeFlow.TryParseUnit(state.Data.Setpoint.Unit, out var setpointUnit); - Setpoint = StandardVolumeFlow.From(state.Data.Setpoint.Value, foundSetPointUnit ? setpointUnit : StandardVolumeFlowUnit.StandardLiterPerMinute); - } - - if(state.Data.HasValveDrive) - { - ValveDrive = state.Data.ValveDrive; - } - - HasValve = state.HasValve; - - ParseStatusCodes(state.Data.StatusCodes); - SelectedGas = state.Data.Gas; - } - - private void ParseStatusCodes(IEnumerable statusCodes) - { - var statusCodesArr = statusCodes.ToArray(); - var removedCodes = StatusCodes.Items.Except(statusCodesArr); - var addedCodes = statusCodesArr.Except(StatusCodes.Items); - StatusCodes.RemoveMany(removedCodes); - StatusCodes.AddRange(addedCodes); - } - - public void SetSetpoint() - { - if(!TargetSetpoint.HasValue) - return; - var setSetpointReq = new SetSetpointRequest { DeviceRequest = _deviceRequest, Setpoint = TargetSetpoint.Value }; - _mfcClient.SetSetpoint(setSetpointReq); - } - - public Task HoldValvesAtCurrentPosition() - => _mfcClient.HoldValvesAtCurrentPositionAsync(_deviceRequest).ResponseAsync; - - public Task CancelValveHold() - => _mfcClient.CancelValveHoldAsync(_deviceRequest).ResponseAsync; - - public Task HoldValvesClose() - => _mfcClient.HoldValvesClosedAsync(_deviceRequest).ResponseAsync; - - public void TareFLow() - { - _mfcClient.TareFlow(_deviceRequest); - } - - public void TareAbsolutePressureWithBarometer() - { - _mfcClient.TareAbsolutePressureWithBarometer(_deviceRequest); - } -} +using Ares.Alicat.Mfc.Messaging; +using DynamicData; +using ReactiveUI.SourceGenerators; +using UI.Features.Devices.Mfc; +using UnitsNet; +using UnitsNet.Units; + +namespace UI.Backend.ViewModels.Devices.Mfc; + +public partial class MfcUnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable +{ + private readonly DeviceRequest _deviceRequest; + private readonly MfcRpc.MfcRpcClient _mfcClient; + private readonly CancellationTokenSource _stateStreamCts = new(); + private Task _stateListener = Task.CompletedTask; + + public MfcUnitControlViewModel(string mfcId, string mfcName, MfcRpc.MfcRpcClient mfcClient) : base(mfcId, mfcName) + { + MfcName = mfcName; + _deviceRequest = new DeviceRequest { DeviceId = DeviceId }; + _mfcClient = mfcClient; + ViewType = typeof(MfcUnitControl); + Initialize(); + + DefaultWidth = 19; + } + + public string MfcName { get; } + + [Reactive] + public partial int TargetGas { get; set; } + + [Reactive] + public partial double? TargetSetpoint { get; set; } + + [Reactive] + public partial bool CapturingLiveData { get; private set; } + + [Reactive] + public partial IEnumerable? AvailableGases { get; set; } + + [Reactive] + public partial string? SelectedGas { get; set; } + + [Reactive] + public partial char? AssumedId { get; set; } + + [Reactive] + public partial bool HasValve { get; private set; } + + [Reactive] + public partial Temperature? Temperature { get; set; } + + [Reactive] + public partial Pressure? AbsolutePressure { get; set; } + + [Reactive] + public partial VolumeFlow? VolumetricFlow { get; set; } + + [Reactive] + public partial StandardVolumeFlow? MassFlow { get; set; } + + [Reactive] + public partial StandardVolumeFlow? Setpoint { get; set; } + + [Reactive] + public partial double? ValveDrive { get; set; } + + [Reactive] + public partial bool HasValidData { get; private set; } + + public ISourceList StatusCodes { get; } = new SourceList(); + + public async ValueTask DisposeAsync() + { + _stateStreamCts.Cancel(); + await _stateListener; + _stateStreamCts.Dispose(); + + GC.SuppressFinalize(this); + } + + private void Initialize() + { + ListenForStates(); + } + + public void ListenForStates() + { + try + { + _stateListener = Task.Run(async () => + { + Thread.CurrentThread.Name = $"Mass Flow Controller {DeviceName} State Listener Thread"; + var state = await _mfcClient.GetStateAsync(_deviceRequest); + UpdateState(state); + CapturingLiveData = true; + try + { + while(!_stateStreamCts.Token.IsCancellationRequested) + { + var stateResponse = await _mfcClient.GetStateUpdateAsync(_deviceRequest, null, null, _stateStreamCts.Token); + UpdateState(stateResponse); + await Task.Delay(100); + } + } + catch(Exception) + { + Console.WriteLine($"~~~~~~~ Exception Getting State, Thread will die probably? ~~~~~~~"); + } + + CapturingLiveData = false; + }, + _stateStreamCts.Token); + } + catch(OperationCanceledException) + { + } + } + + private void UpdateState(StateResponse state) + { + AssumedId = state.AssumedId?.FirstOrDefault(); + AvailableGases = state.AvailableGasInfos; + if(state.Data is null) + { + HasValidData = false; + return; + } + HasValidData = true; + + if(state.Data.Temperature is not null) + { + var foundTempUnit = UnitsNet.Temperature.TryParseUnit(state.Data.Temperature.Unit, out var tempUnit); + Temperature = UnitsNet.Temperature.From(state.Data.Temperature.Value, foundTempUnit ? tempUnit : TemperatureUnit.DegreeCelsius); + } + + if(state.Data.AbsolutePressure is not null) + { + var foundAbsolutePressureUnit = Pressure.TryParseUnit(state.Data.AbsolutePressure.Unit, out var pressureUnit); + AbsolutePressure = Pressure.From(state.Data.AbsolutePressure.Value, foundAbsolutePressureUnit ? pressureUnit : PressureUnit.PoundForcePerSquareInch); + } + + if(state.Data.VolumetricFlow is not null) + { + var foundVolumetricFlowUnit = VolumeFlow.TryParseUnit(state.Data.VolumetricFlow.Unit, out var volumeFlowUnit); + VolumetricFlow = VolumeFlow.From(state.Data.VolumetricFlow.Value, foundVolumetricFlowUnit ? volumeFlowUnit : VolumeFlowUnit.CubicCentimeterPerMinute); + } + + if(state.Data.MassFlow is not null) + { + var foundMassFlowUnit = StandardVolumeFlow.TryParseUnit(state.Data.MassFlow.Unit, out var massFlowUnit); + MassFlow = StandardVolumeFlow.From(state.Data.MassFlow.Value, foundMassFlowUnit ? massFlowUnit : StandardVolumeFlowUnit.StandardLiterPerMinute); + } + + if(state.Data.Setpoint is not null) + { + var foundSetPointUnit = StandardVolumeFlow.TryParseUnit(state.Data.Setpoint.Unit, out var setpointUnit); + Setpoint = StandardVolumeFlow.From(state.Data.Setpoint.Value, foundSetPointUnit ? setpointUnit : StandardVolumeFlowUnit.StandardLiterPerMinute); + } + + if(state.Data.HasValveDrive) + { + ValveDrive = state.Data.ValveDrive; + } + + HasValve = state.HasValve; + + ParseStatusCodes(state.Data.StatusCodes); + SelectedGas = state.Data.Gas; + } + + private void ParseStatusCodes(IEnumerable statusCodes) + { + var statusCodesArr = statusCodes.ToArray(); + var removedCodes = StatusCodes.Items.Except(statusCodesArr); + var addedCodes = statusCodesArr.Except(StatusCodes.Items); + StatusCodes.RemoveMany(removedCodes); + StatusCodes.AddRange(addedCodes); + } + + public void SetSetpoint() + { + if(!TargetSetpoint.HasValue) + return; + var setSetpointReq = new SetSetpointRequest { DeviceRequest = _deviceRequest, Setpoint = TargetSetpoint.Value }; + _mfcClient.SetSetpoint(setSetpointReq); + } + + public Task HoldValvesAtCurrentPosition() + => _mfcClient.HoldValvesAtCurrentPositionAsync(_deviceRequest).ResponseAsync; + + public Task CancelValveHold() + => _mfcClient.CancelValveHoldAsync(_deviceRequest).ResponseAsync; + + public Task HoldValvesClose() + => _mfcClient.HoldValvesClosedAsync(_deviceRequest).ResponseAsync; + + public void TareFLow() + { + _mfcClient.TareFlow(_deviceRequest); + } + + public void TareAbsolutePressureWithBarometer() + { + _mfcClient.TareAbsolutePressureWithBarometer(_deviceRequest); + } +} diff --git a/UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceConfigEditView.razor b/UI/Features/Devices/Remote/RemoteDeviceConfigEditView.razor similarity index 98% rename from UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceConfigEditView.razor rename to UI/Features/Devices/Remote/RemoteDeviceConfigEditView.razor index 5f4d135c..2c02ceea 100644 --- a/UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceConfigEditView.razor +++ b/UI/Features/Devices/Remote/RemoteDeviceConfigEditView.razor @@ -6,10 +6,10 @@
- + - + @@ -26,22 +26,22 @@ -@code { - - private bool ValidateAddress(string address) - { - if (string.IsNullOrEmpty(address)) - { - return false; - } - - var test = Uri.TryCreate(address, UriKind.Absolute, out var _); - if (!test) - { - return false; - } - - return true; +@code { + + private bool ValidateAddress(string address) + { + if (string.IsNullOrEmpty(address)) + { + return false; + } + + var test = Uri.TryCreate(address, UriKind.Absolute, out var _); + if (!test) + { + return false; + } + + return true; } [Parameter] diff --git a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceConfigEditViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceConfigEditViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceConfigEditViewModel.cs rename to UI/Features/Devices/Remote/RemoteDeviceConfigEditViewModel.cs index c84ef03b..035370f3 100644 --- a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceConfigEditViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceConfigEditViewModel.cs @@ -2,66 +2,66 @@ using Ares.Datamodel.Device; using ReactiveUI; -namespace UI.Backend.ViewModels.Settings.Device.Remote; +namespace UI.Features.Devices.Remote; public class RemoteDeviceConfigEditViewModel : ReactiveObject -{ - private readonly RemoteDeviceConfig _originalConfig; - private readonly ObservableAsPropertyHelper _modified; - - private string _name; - private string _address; - - // Constructor for a new configuration - public RemoteDeviceConfigEditViewModel() : this(new RemoteDeviceConfig(), isNew: true) - { - } - - // Constructor for editing an existing configuration - public RemoteDeviceConfigEditViewModel(RemoteDeviceConfig remoteDeviceConfig) : this(remoteDeviceConfig, isNew: false) - { - } - - // Private master constructor to reduce duplication - private RemoteDeviceConfigEditViewModel(RemoteDeviceConfig config, bool isNew) - { - _originalConfig = config ?? throw new ArgumentNullException(nameof(config)); - NewConfig = isNew; - - // Set initial values from the model - _name = _originalConfig.Name; - _address = _originalConfig.Url; - - // A reactive property that tracks if the view model has been modified. - _modified = this.WhenAnyValue( - x => x.Name, - x => x.Address) - .Select(_ => Name != _originalConfig.Name || BuildUrl() != (_originalConfig.Url ?? string.Empty)) - .ToProperty(this, x => x.Modified, initialValue: false); - } - - public string Name - { - get => _name; - set => this.RaiseAndSetIfChanged(ref _name, value); - } - - public string Address - { - get => _address; - set => this.RaiseAndSetIfChanged(ref _address, value); - } - - public bool Modified => _modified.Value; - - public bool NewConfig { get; } - - public RemoteDeviceConfig Save() - => Modified ? new RemoteDeviceConfig { Name = Name, Url = BuildUrl() } : _originalConfig; - - private string BuildUrl() - { - var success = Uri.TryCreate(Address, UriKind.Absolute, out var result); - return result?.ToString().TrimEnd('/') ?? ""; +{ + private readonly RemoteDeviceConfig _originalConfig; + private readonly ObservableAsPropertyHelper _modified; + + private string _name; + private string _address; + + // Constructor for a new configuration + public RemoteDeviceConfigEditViewModel() : this(new RemoteDeviceConfig(), isNew: true) + { + } + + // Constructor for editing an existing configuration + public RemoteDeviceConfigEditViewModel(RemoteDeviceConfig remoteDeviceConfig) : this(remoteDeviceConfig, isNew: false) + { + } + + // Private master constructor to reduce duplication + private RemoteDeviceConfigEditViewModel(RemoteDeviceConfig config, bool isNew) + { + _originalConfig = config ?? throw new ArgumentNullException(nameof(config)); + NewConfig = isNew; + + // Set initial values from the model + _name = _originalConfig.Name; + _address = _originalConfig.Url; + + // A reactive property that tracks if the view model has been modified. + _modified = this.WhenAnyValue( + x => x.Name, + x => x.Address) + .Select(_ => Name != _originalConfig.Name || BuildUrl() != (_originalConfig.Url ?? string.Empty)) + .ToProperty(this, x => x.Modified, initialValue: false); + } + + public string Name + { + get => _name; + set => this.RaiseAndSetIfChanged(ref _name, value); + } + + public string Address + { + get => _address; + set => this.RaiseAndSetIfChanged(ref _address, value); + } + + public bool Modified => _modified.Value; + + public bool NewConfig { get; } + + public RemoteDeviceConfig Save() + => Modified ? new RemoteDeviceConfig { Name = Name, Url = BuildUrl() } : _originalConfig; + + private string BuildUrl() + { + var success = Uri.TryCreate(Address, UriKind.Absolute, out var result); + return result?.ToString().TrimEnd('/') ?? ""; } } diff --git a/UI/Backend/Factories/RemoteDeviceControlViewModelFactory.cs b/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs similarity index 93% rename from UI/Backend/Factories/RemoteDeviceControlViewModelFactory.cs rename to UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs index 375b25ce..0d680f14 100644 --- a/UI/Backend/Factories/RemoteDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs @@ -1,12 +1,13 @@ using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; -using UI.Backend.Devices; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.Remote; +using UI.Features.Devices.Shared; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.Remote; public class RemoteDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsEditorView.razor b/UI/Features/Devices/Remote/RemoteDeviceSettingsEditorView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsEditorView.razor rename to UI/Features/Devices/Remote/RemoteDeviceSettingsEditorView.razor diff --git a/UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsList.razor b/UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsList.razor rename to UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor diff --git a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsListViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsListViewModel.cs rename to UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs index ab35c1b4..1101d351 100644 --- a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsListViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs @@ -6,6 +6,7 @@ using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.Devices.Remote; using UI.Services.Notification; namespace UI.Backend.ViewModels.Settings.Device.Remote; diff --git a/UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsView.razor b/UI/Features/Devices/Remote/RemoteDeviceSettingsView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/Remote/RemoteDeviceSettingsView.razor rename to UI/Features/Devices/Remote/RemoteDeviceSettingsView.razor diff --git a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs similarity index 99% rename from UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsViewModel.cs rename to UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs index 156c26a8..893a4fe2 100644 --- a/UI/Backend/ViewModels/Settings/Device/Remote/RemoteDeviceSettingsViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs @@ -8,7 +8,8 @@ using Grpc.Core; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.Devices; +using UI.Features.Devices.Remote; +using UI.Features.Devices.Shared; using UI.Services.Notification; namespace UI.Backend.ViewModels.Settings.Device.Remote; diff --git a/UI/Pages/Shared/Devices/Remote/RemoteDeviceUnitView.razor b/UI/Features/Devices/Remote/RemoteDeviceUnitView.razor similarity index 96% rename from UI/Pages/Shared/Devices/Remote/RemoteDeviceUnitView.razor rename to UI/Features/Devices/Remote/RemoteDeviceUnitView.razor index d5ca37fd..9ab73464 100644 --- a/UI/Pages/Shared/Devices/Remote/RemoteDeviceUnitView.razor +++ b/UI/Features/Devices/Remote/RemoteDeviceUnitView.razor @@ -1,5 +1,4 @@ -@using UI.Backend.Devices -@using UI.Backend.ViewModels.Devices.Remote +@using UI.Backend.ViewModels.Devices.Remote @inject TooltipService TooltipService @inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Pages/Shared/Devices/Remote/RemoteDeviceUnitView.razor.css b/UI/Features/Devices/Remote/RemoteDeviceUnitView.razor.css similarity index 100% rename from UI/Pages/Shared/Devices/Remote/RemoteDeviceUnitView.razor.css rename to UI/Features/Devices/Remote/RemoteDeviceUnitView.razor.css diff --git a/UI/Backend/ViewModels/Devices/Remote/RemoteDeviceUnitViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceUnitViewModel.cs similarity index 92% rename from UI/Backend/ViewModels/Devices/Remote/RemoteDeviceUnitViewModel.cs rename to UI/Features/Devices/Remote/RemoteDeviceUnitViewModel.cs index be65caa2..0e089029 100644 --- a/UI/Backend/ViewModels/Devices/Remote/RemoteDeviceUnitViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceUnitViewModel.cs @@ -1,7 +1,7 @@ -using Ares.Datamodel; +using Ares.Datamodel; using ReactiveUI.SourceGenerators; -using UI.Backend.Devices; -using UI.Pages.Shared.Devices.Remote; +using UI.Features.Devices.Remote; +using UI.Features.Devices.Shared; namespace UI.Backend.ViewModels.Devices.Remote; diff --git a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceConfigEditView.razor b/UI/Features/Devices/RestDevice/RestDeviceConfigEditView.razor similarity index 86% rename from UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceConfigEditView.razor rename to UI/Features/Devices/RestDevice/RestDeviceConfigEditView.razor index 71acb2b4..ad435b8f 100644 --- a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceConfigEditView.razor +++ b/UI/Features/Devices/RestDevice/RestDeviceConfigEditView.razor @@ -1,4 +1,4 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceConfigEditViewModel.cs b/UI/Features/Devices/RestDevice/RestDeviceConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceConfigEditViewModel.cs rename to UI/Features/Devices/RestDevice/RestDeviceConfigEditViewModel.cs index de40349e..1fd40693 100644 --- a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceConfigEditViewModel.cs +++ b/UI/Features/Devices/RestDevice/RestDeviceConfigEditViewModel.cs @@ -4,7 +4,7 @@ using RestDevice.Config; using RestDevice.Services; -namespace UI.Backend.ViewModels.Settings.Device.RestDevice; +namespace UI.Features.Devices.RestDevice; public class RestDeviceConfigEditViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsList.razor b/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsList.razor rename to UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor index 2c47e196..1106ea9f 100644 --- a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsList.razor +++ b/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/restdevice" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.RestDevice +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsListViewModel.cs b/UI/Features/Devices/RestDevice/RestDeviceSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsListViewModel.cs rename to UI/Features/Devices/RestDevice/RestDeviceSettingsListViewModel.cs index 87032c54..2b757289 100644 --- a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsListViewModel.cs +++ b/UI/Features/Devices/RestDevice/RestDeviceSettingsListViewModel.cs @@ -5,6 +5,7 @@ using RestDevice; using RestDevice.Config; using RestDevice.Services; +using UI.Features.Devices.RestDevice; namespace UI.Backend.ViewModels.Settings.Device.RestDevice; diff --git a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsView.razor b/UI/Features/Devices/RestDevice/RestDeviceSettingsView.razor similarity index 90% rename from UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsView.razor rename to UI/Features/Devices/RestDevice/RestDeviceSettingsView.razor index d92f7f66..62a92d78 100644 --- a/UI/Pages/Shared/Settings/Device/RestDevice/RestDeviceSettingsView.razor +++ b/UI/Features/Devices/RestDevice/RestDeviceSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsViewModel.cs b/UI/Features/Devices/RestDevice/RestDeviceSettingsViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsViewModel.cs rename to UI/Features/Devices/RestDevice/RestDeviceSettingsViewModel.cs index baf7f257..d9889885 100644 --- a/UI/Backend/ViewModels/Settings/Device/RestDevice/RestDeviceSettingsViewModel.cs +++ b/UI/Features/Devices/RestDevice/RestDeviceSettingsViewModel.cs @@ -5,7 +5,7 @@ using RestDevice.Config; using RestDevice.Services; -namespace UI.Backend.ViewModels.Settings.Device.RestDevice; +namespace UI.Features.Devices.RestDevice; public class RestDeviceSettingsViewModel : ReactiveObject { @@ -42,10 +42,10 @@ public async Task GetDeviceOperationalStatus() public async Task Save() { var servoConfig = EditViewModel.Save(); - var updateRequest = new RestDeviceUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = servoConfig + var updateRequest = new RestDeviceUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = servoConfig }; await _client.UpdateRestDeviceAsync(updateRequest); diff --git a/UI/Pages/Shared/Devices/RestDevice/RestDeviceUnitControl.razor b/UI/Features/Devices/RestDevice/RestDeviceUnitControl.razor similarity index 90% rename from UI/Pages/Shared/Devices/RestDevice/RestDeviceUnitControl.razor rename to UI/Features/Devices/RestDevice/RestDeviceUnitControl.razor index 66f8a06c..74cb2999 100644 --- a/UI/Pages/Shared/Devices/RestDevice/RestDeviceUnitControl.razor +++ b/UI/Features/Devices/RestDevice/RestDeviceUnitControl.razor @@ -1,5 +1,4 @@ -@using UI.Backend.ViewModels.Devices.RestDevice -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject TooltipService tooltipService diff --git a/UI/Backend/ViewModels/Devices/RestDevice/RestDeviceUnitControlViewModel.cs b/UI/Features/Devices/RestDevice/RestDeviceUnitControlViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Devices/RestDevice/RestDeviceUnitControlViewModel.cs rename to UI/Features/Devices/RestDevice/RestDeviceUnitControlViewModel.cs index 471a479f..38921acc 100644 --- a/UI/Backend/ViewModels/Devices/RestDevice/RestDeviceUnitControlViewModel.cs +++ b/UI/Features/Devices/RestDevice/RestDeviceUnitControlViewModel.cs @@ -1,6 +1,7 @@ using RestDevice.Services; +using UI.Backend.ViewModels; -namespace UI.Backend.ViewModels.Devices.RestDevice; +namespace UI.Features.Devices.RestDevice; public class RestDeviceUnitControlViewModel : DeviceUnitControlViewModel { diff --git a/UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceConfigEditView.razor b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceConfigEditView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceConfigEditView.razor rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceConfigEditView.razor diff --git a/UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceConfigEditViewModel.cs b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceConfigEditViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceConfigEditViewModel.cs rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceConfigEditViewModel.cs diff --git a/UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsList.razor b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor similarity index 98% rename from UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsList.razor rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor index 9c65498b..000fc930 100644 --- a/UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsList.razor +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/serialrest" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.SerialRestDevice +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs index 6ed480ae..31d31518 100644 --- a/UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsListViewModel.cs @@ -68,6 +68,7 @@ public async Task AddNewConfig(RestSerialConfig config) using RestSerialDevice; using RestSerialDevice.Config; using RestSerialDevice.Services; +using UI.Features.Devices.SerialRestDevice; namespace UI.Backend.ViewModels.Settings.Device.SerialRestDevice; diff --git a/UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsView.razor b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsView.razor similarity index 89% rename from UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsView.razor rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsView.razor index d9858da2..14d8fc7e 100644 --- a/UI/Pages/Shared/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsView.razor +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs index e5e1e601..686927e7 100644 --- a/UI/Backend/ViewModels/Settings/Device/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsViewModel.cs @@ -11,68 +11,68 @@ public class SerialRestDeviceSettingsViewModel using ReactiveUI; using RestSerialDevice.Config; using RestSerialDevice.Services; +using UI.Backend.ViewModels.Settings.Device.SerialRestDevice; - -namespace UI.Backend.ViewModels.Settings.Device.SerialRestDevice; +namespace UI.Features.Devices.SerialRestDevice; public class SerialRestDeviceSettingsViewModel : ReactiveObject -{ - private readonly RestSerialDeviceRpc.RestSerialDeviceRpcClient _client; - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - - public SerialRestDeviceSettingsViewModel(DeviceConfig deviceConfig, - RestSerialDeviceRpc.RestSerialDeviceRpcClient restClient, - AresDevices.AresDevicesClient devicesClient, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - _client = restClient; - _devicesClient = devicesClient; - Config = deviceConfig.ConfigData.Unpack(); - OnRemoveCallback = onRemoveCallback; - EditViewModel = new SerialRestDeviceConfigEditViewModel(_client, _devicesClient, Config); - } - - public async Task GetDeviceOperationalStatus() - { - try - { - return await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }); - } - - catch(RpcException) - { - return new DeviceOperationalStatus() { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Rest Device with a name {Config.Name}" }; - } - } - - public async Task Save() - { - var config = EditViewModel.Save(); - var updateRequest = new GenericSerialRestDeviceUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = config, - }; - - await _client.UpdateGenericSerialDeviceAsync(updateRequest); - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest - { - DeviceId = _deviceConfig.UniqueId - }).ResponseAsync; - - public async Task Remove() - { - await _client.RemoveGenericSerialDeviceAsync(new DeviceRequest() { DeviceId = _deviceConfig.UniqueId }); - await OnRemoveCallback(); - } - - public RestSerialConfig Config { get; set; } - public Func OnRemoveCallback { get; set; } +{ + private readonly RestSerialDeviceRpc.RestSerialDeviceRpcClient _client; + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + + public SerialRestDeviceSettingsViewModel(DeviceConfig deviceConfig, + RestSerialDeviceRpc.RestSerialDeviceRpcClient restClient, + AresDevices.AresDevicesClient devicesClient, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + _client = restClient; + _devicesClient = devicesClient; + Config = deviceConfig.ConfigData.Unpack(); + OnRemoveCallback = onRemoveCallback; + EditViewModel = new SerialRestDeviceConfigEditViewModel(_client, _devicesClient, Config); + } + + public async Task GetDeviceOperationalStatus() + { + try + { + return await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }); + } + + catch(RpcException) + { + return new DeviceOperationalStatus() { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Rest Device with a name {Config.Name}" }; + } + } + + public async Task Save() + { + var config = EditViewModel.Save(); + var updateRequest = new GenericSerialRestDeviceUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = config, + }; + + await _client.UpdateGenericSerialDeviceAsync(updateRequest); + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest + { + DeviceId = _deviceConfig.UniqueId + }).ResponseAsync; + + public async Task Remove() + { + await _client.RemoveGenericSerialDeviceAsync(new DeviceRequest() { DeviceId = _deviceConfig.UniqueId }); + await OnRemoveCallback(); + } + + public RestSerialConfig Config { get; set; } + public Func OnRemoveCallback { get; set; } public SerialRestDeviceConfigEditViewModel EditViewModel { get; set; } } diff --git a/UI/Pages/Shared/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor similarity index 93% rename from UI/Pages/Shared/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor index 734b8264..a4d8bc83 100644 --- a/UI/Pages/Shared/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControl.razor @@ -1,5 +1,4 @@ -@using UI.Backend.ViewModels.Devices.SerialRestDevice; -@using System.Collections.Generic +@using System.Collections.Generic @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject TooltipService tooltipService diff --git a/UI/Backend/ViewModels/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs rename to UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs index 8525ab2a..f762cb48 100644 --- a/UI/Backend/ViewModels/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceUnitControlViewModel.cs @@ -1,6 +1,7 @@ using RestSerialDevice.Services; +using UI.Backend.ViewModels; -namespace UI.Backend.ViewModels.Devices.SerialRestDevice; +namespace UI.Features.Devices.SerialRestDevice; public class SerialRestDeviceUnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable { diff --git a/UI/Pages/Shared/Settings/Device/Servo/ServoConfigEditView.razor b/UI/Features/Devices/Servo/ServoConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Servo/ServoConfigEditView.razor rename to UI/Features/Devices/Servo/ServoConfigEditView.razor index 579ae977..c571fbe1 100644 --- a/UI/Pages/Shared/Settings/Device/Servo/ServoConfigEditView.razor +++ b/UI/Features/Devices/Servo/ServoConfigEditView.razor @@ -1,39 +1,39 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
- - - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - -
- - Simulated -
-
- - - -
-@code { - - [Parameter] - public Action OnValidSubmit { get; set; } = delegate - { - }; - - private void PortChanged(ChangeEventArgs obj) - { - if (obj.Value is not string stringValue) - throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); - - ViewModel!.Port = stringValue; - } +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+ + + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + +
+ + Simulated +
+
+ + + +
+@code { + + [Parameter] + public Action OnValidSubmit { get; set; } = delegate + { + }; + + private void PortChanged(ChangeEventArgs obj) + { + if (obj.Value is not string stringValue) + throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); + + ViewModel!.Port = stringValue; + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/Servo/ServoConfigEditViewModel.cs b/UI/Features/Devices/Servo/ServoConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/Servo/ServoConfigEditViewModel.cs rename to UI/Features/Devices/Servo/ServoConfigEditViewModel.cs index 00cf41ad..45627b47 100644 --- a/UI/Backend/ViewModels/Settings/Device/Servo/ServoConfigEditViewModel.cs +++ b/UI/Features/Devices/Servo/ServoConfigEditViewModel.cs @@ -1,74 +1,74 @@ -using Ares.Services.Device; -using Google.Protobuf.WellKnownTypes; -using HerkulexDRS.Config; -using HerkulexDRS.Services; -using Microsoft.Build.Framework; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Settings.Device.Servo; - -public partial class ServoConfigEditViewModel : ReactiveObject -{ - private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _client; - private readonly ServoConfig _config; - private readonly AresDevices.AresDevicesClient _devicesClient; - private string? _name; - - public ServoConfigEditViewModel(HerkulexDRSRpc.HerkulexDRSRpcClient client, AresDevices.AresDevicesClient devicesClient) - { - _client = client; - _devicesClient = devicesClient; - _config = new ServoConfig(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - } - - public ServoConfigEditViewModel(HerkulexDRSRpc.HerkulexDRSRpcClient client, AresDevices.AresDevicesClient devicesClient, ServoConfig config) - { - _client = client; - _devicesClient = devicesClient; - _config = config; - _ = UpdateAvailableSerialPorts(); - _name = config.Name; - Port = config.PortName; - Simulated = config.Simulated; - } - - [Required] - public string? Name - { - get => _name; - - set - { - if (!NewConfig) - return; - - _name = value; - } - } - - [Required] - public string? Port { get; set; } - - public bool NewConfig { get; set; } - - public bool Simulated { get; set; } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - public ServoConfig Save() - => Modified ? new ServoConfig { Name = Name, PortName = Port, Simulated = Simulated } : _config; -} +using Ares.Services.Device; +using Google.Protobuf.WellKnownTypes; +using HerkulexDRS.Config; +using HerkulexDRS.Services; +using Microsoft.Build.Framework; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace UI.Backend.ViewModels.Settings.Device.Servo; + +public partial class ServoConfigEditViewModel : ReactiveObject +{ + private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _client; + private readonly ServoConfig _config; + private readonly AresDevices.AresDevicesClient _devicesClient; + private string? _name; + + public ServoConfigEditViewModel(HerkulexDRSRpc.HerkulexDRSRpcClient client, AresDevices.AresDevicesClient devicesClient) + { + _client = client; + _devicesClient = devicesClient; + _config = new ServoConfig(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + } + + public ServoConfigEditViewModel(HerkulexDRSRpc.HerkulexDRSRpcClient client, AresDevices.AresDevicesClient devicesClient, ServoConfig config) + { + _client = client; + _devicesClient = devicesClient; + _config = config; + _ = UpdateAvailableSerialPorts(); + _name = config.Name; + Port = config.PortName; + Simulated = config.Simulated; + } + + [Required] + public string? Name + { + get => _name; + + set + { + if (!NewConfig) + return; + + _name = value; + } + } + + [Required] + public string? Port { get; set; } + + public bool NewConfig { get; set; } + + public bool Simulated { get; set; } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + public ServoConfig Save() + => Modified ? new ServoConfig { Name = Name, PortName = Port, Simulated = Simulated } : _config; +} diff --git a/UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor b/UI/Features/Devices/Servo/ServoControlWidgetView.razor similarity index 90% rename from UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor rename to UI/Features/Devices/Servo/ServoControlWidgetView.razor index 2c6ac4f4..4bf0367d 100644 --- a/UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor +++ b/UI/Features/Devices/Servo/ServoControlWidgetView.razor @@ -1,5 +1,4 @@ -@using UI.Backend.ViewModels.Devices.HerkulexDRS -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject TooltipService tooltipService diff --git a/UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor.css b/UI/Features/Devices/Servo/ServoControlWidgetView.razor.css similarity index 94% rename from UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor.css rename to UI/Features/Devices/Servo/ServoControlWidgetView.razor.css index 995a7609..fc0649a9 100644 --- a/UI/Pages/Shared/Devices/Servo/ServoControlWidgetView.razor.css +++ b/UI/Features/Devices/Servo/ServoControlWidgetView.razor.css @@ -1,3 +1,3 @@ -.servobutton { - margin: 5px; +.servobutton { + margin: 5px; } \ No newline at end of file diff --git a/UI/Backend/Factories/ServoDeviceControlViewModelFactory.cs b/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs similarity index 93% rename from UI/Backend/Factories/ServoDeviceControlViewModelFactory.cs rename to UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs index 5d3272da..4b0aa482 100644 --- a/UI/Backend/Factories/ServoDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs @@ -2,11 +2,11 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using HerkulexDRS.Services; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; -using UI.Backend.ViewModels.Devices.HerkulexDRS; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.Servo; public class ServoDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/Servo/ServoSettingsList.razor b/UI/Features/Devices/Servo/ServoSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Servo/ServoSettingsList.razor rename to UI/Features/Devices/Servo/ServoSettingsList.razor index 06d6e57b..0332e3e6 100644 --- a/UI/Pages/Shared/Settings/Device/Servo/ServoSettingsList.razor +++ b/UI/Features/Devices/Servo/ServoSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/servo" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Servo +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsListViewModel.cs b/UI/Features/Devices/Servo/ServoSettingsListViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsListViewModel.cs rename to UI/Features/Devices/Servo/ServoSettingsListViewModel.cs index d2023fd9..58979ef7 100644 --- a/UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsListViewModel.cs +++ b/UI/Features/Devices/Servo/ServoSettingsListViewModel.cs @@ -1,57 +1,58 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using HerkulexDRS; -using HerkulexDRS.Config; -using HerkulexDRS.Services; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Settings.Device.Servo; - -public partial class ServoSettingsListViewModel : ReactiveObject -{ - private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _servoClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public ServoSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, HerkulexDRSRpc.HerkulexDRSRpcClient servoClient, IMessenger messenger) - { - _servoClient = servoClient; - _devicesClient = devicesClient; - _messenger = messenger; - UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new ServoSettingsViewModel(config, _servoClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public ServoConfigEditViewModel GetNewConfigEditViewModel() - => new(_servoClient, _devicesClient); - - private Task UpdateConfigs() - { - SettingsViewModels = null; - return _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IServo).FullName }) - .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); - } - - private async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(ServoConfig config) - { - await _servoClient.AddServoAsync(config); - await UpdateConfigs(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using HerkulexDRS; +using HerkulexDRS.Config; +using HerkulexDRS.Services; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.Devices.Servo; + +namespace UI.Backend.ViewModels.Settings.Device.Servo; + +public partial class ServoSettingsListViewModel : ReactiveObject +{ + private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _servoClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public ServoSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, HerkulexDRSRpc.HerkulexDRSRpcClient servoClient, IMessenger messenger) + { + _servoClient = servoClient; + _devicesClient = devicesClient; + _messenger = messenger; + UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new ServoSettingsViewModel(config, _servoClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public ServoConfigEditViewModel GetNewConfigEditViewModel() + => new(_servoClient, _devicesClient); + + private Task UpdateConfigs() + { + SettingsViewModels = null; + return _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IServo).FullName }) + .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); + } + + private async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(ServoConfig config) + { + await _servoClient.AddServoAsync(config); + await UpdateConfigs(); + } +} diff --git a/UI/Pages/Shared/Settings/Device/Servo/ServoSettingsView.razor b/UI/Features/Devices/Servo/ServoSettingsView.razor similarity index 91% rename from UI/Pages/Shared/Settings/Device/Servo/ServoSettingsView.razor rename to UI/Features/Devices/Servo/ServoSettingsView.razor index 0df10688..f1d7181c 100644 --- a/UI/Pages/Shared/Settings/Device/Servo/ServoSettingsView.razor +++ b/UI/Features/Devices/Servo/ServoSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsViewModel.cs b/UI/Features/Devices/Servo/ServoSettingsViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsViewModel.cs rename to UI/Features/Devices/Servo/ServoSettingsViewModel.cs index 28b1c70f..fd35de7c 100644 --- a/UI/Backend/ViewModels/Settings/Device/Servo/ServoSettingsViewModel.cs +++ b/UI/Features/Devices/Servo/ServoSettingsViewModel.cs @@ -1,70 +1,71 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using HerkulexDRS.Config; -using HerkulexDRS.Services; -using ReactiveUI; -using UI.Backend.Devices; - -namespace UI.Backend.ViewModels.Settings.Device.Servo; - -public class ServoSettingsViewModel : ReactiveObject -{ - private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _servoClient; - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public ServoSettingsViewModel(DeviceConfig deviceConfig, - HerkulexDRSRpc.HerkulexDRSRpcClient servoClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - _servoClient = servoClient; - ServoConfig = deviceConfig.ConfigData.Unpack(); - _devicesClient = devicesClient; - _messenger = messenger; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new ServoConfigEditViewModel(_servoClient, _devicesClient, ServoConfig); - } - - public ServoConfig ServoConfig { get; } - public Func OnRemoveCallback { get; } - public ServoConfigEditViewModel EditViewModel { get; } - - public Task GetDeviceOperationalStatus() - { - try - { - return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - } - - catch(RpcException) - { - return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Servo with a name {ServoConfig.Name}" }); - } - } - - public async Task Save() - { - var servoConfig = EditViewModel.Save(); - await _servoClient.UpdateServoAsync(servoConfig); - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest - { - DeviceId = _deviceConfig.UniqueId - }).ResponseAsync; - - public async Task Remove() - { - await _servoClient.RemoveServoAsync(new HerkulexRequest { HerkulexId = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } -} - +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using HerkulexDRS.Config; +using HerkulexDRS.Services; +using ReactiveUI; +using UI.Backend.ViewModels.Settings.Device.Servo; +using UI.Features.Devices.Shared; + +namespace UI.Features.Devices.Servo; + +public class ServoSettingsViewModel : ReactiveObject +{ + private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _servoClient; + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public ServoSettingsViewModel(DeviceConfig deviceConfig, + HerkulexDRSRpc.HerkulexDRSRpcClient servoClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + _servoClient = servoClient; + ServoConfig = deviceConfig.ConfigData.Unpack(); + _devicesClient = devicesClient; + _messenger = messenger; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new ServoConfigEditViewModel(_servoClient, _devicesClient, ServoConfig); + } + + public ServoConfig ServoConfig { get; } + public Func OnRemoveCallback { get; } + public ServoConfigEditViewModel EditViewModel { get; } + + public Task GetDeviceOperationalStatus() + { + try + { + return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + } + + catch(RpcException) + { + return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Servo with a name {ServoConfig.Name}" }); + } + } + + public async Task Save() + { + var servoConfig = EditViewModel.Save(); + await _servoClient.UpdateServoAsync(servoConfig); + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest + { + DeviceId = _deviceConfig.UniqueId + }).ResponseAsync; + + public async Task Remove() + { + await _servoClient.RemoveServoAsync(new HerkulexRequest { HerkulexId = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } +} + diff --git a/UI/Backend/ViewModels/Devices/HerkulexDRS/ServoUnitControlViewModel.cs b/UI/Features/Devices/Servo/ServoUnitControlViewModel.cs similarity index 81% rename from UI/Backend/ViewModels/Devices/HerkulexDRS/ServoUnitControlViewModel.cs rename to UI/Features/Devices/Servo/ServoUnitControlViewModel.cs index e60471f4..f833ffcf 100644 --- a/UI/Backend/ViewModels/Devices/HerkulexDRS/ServoUnitControlViewModel.cs +++ b/UI/Features/Devices/Servo/ServoUnitControlViewModel.cs @@ -1,31 +1,31 @@ -using HerkulexDRS.Services; -using UI.Pages.Shared.Devices.Servo; - -namespace UI.Backend.ViewModels.Devices.HerkulexDRS; - -public class ServoUnitControlViewModel : DeviceUnitControlViewModel -{ - private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _client; - - public ServoUnitControlViewModel(string deviceId, string deviceName, HerkulexDRSRpc.HerkulexDRSRpcClient client) : base(deviceId, deviceName) - { - _client = client; - ViewType = typeof(ServoControlWidgetView); - DefaultWidth = 20; - } - - public void PistonUp() - { - _client.PistonUp(new DeviceRequest { DeviceId = DeviceId }); - } - - public void PistonDown() - { - _client.PistonDown(new DeviceRequest { DeviceId = DeviceId }); - } - - public void ServoReset() - { - _client.ResetServo(new DeviceRequest { DeviceId = DeviceId }); - } -} +using HerkulexDRS.Services; +using UI.Backend.ViewModels; + +namespace UI.Features.Devices.Servo; + +public class ServoUnitControlViewModel : DeviceUnitControlViewModel +{ + private readonly HerkulexDRSRpc.HerkulexDRSRpcClient _client; + + public ServoUnitControlViewModel(string deviceId, string deviceName, HerkulexDRSRpc.HerkulexDRSRpcClient client) : base(deviceId, deviceName) + { + _client = client; + ViewType = typeof(ServoControlWidgetView); + DefaultWidth = 20; + } + + public void PistonUp() + { + _client.PistonUp(new DeviceRequest { DeviceId = DeviceId }); + } + + public void PistonDown() + { + _client.PistonDown(new DeviceRequest { DeviceId = DeviceId }); + } + + public void ServoReset() + { + _client.ResetServo(new DeviceRequest { DeviceId = DeviceId }); + } +} diff --git a/UI/Backend/Devices/ConnectionStatus.cs b/UI/Features/Devices/Shared/ConnectionStatus.cs similarity index 87% rename from UI/Backend/Devices/ConnectionStatus.cs rename to UI/Features/Devices/Shared/ConnectionStatus.cs index 6e47b682..17fb6297 100644 --- a/UI/Backend/Devices/ConnectionStatus.cs +++ b/UI/Features/Devices/Shared/ConnectionStatus.cs @@ -1,12 +1,12 @@ -namespace UI.Backend.Devices; - -public enum ConnectionStatus -{ - Undefined, - Disconnected, - // we've connected to the service and are receiving updates - // but the service does not have connection to the device itself - ConnectedToService, - // the UI is talking to service and service is talking to the device - ConnectedToDevice, -} +namespace UI.Features.Devices.Shared; + +public enum ConnectionStatus +{ + Undefined, + Disconnected, + // we've connected to the service and are receiving updates + // but the service does not have connection to the device itself + ConnectedToService, + // the UI is talking to service and service is talking to the device + ConnectedToDevice, +} diff --git a/UI/Backend/Devices/DeviceAdapterManager.cs b/UI/Features/Devices/Shared/DeviceAdapterManager.cs similarity index 95% rename from UI/Backend/Devices/DeviceAdapterManager.cs rename to UI/Features/Devices/Shared/DeviceAdapterManager.cs index 3f0f18a2..4665d0f9 100644 --- a/UI/Backend/Devices/DeviceAdapterManager.cs +++ b/UI/Features/Devices/Shared/DeviceAdapterManager.cs @@ -1,123 +1,123 @@ -using Ares.Services.Device; -using Google.Protobuf.WellKnownTypes; - -namespace UI.Backend.Devices; - -public class DeviceAdapterManager( - AresDevices.AresDevicesClient _devicesClient, - DeviceAdapterRepository _deviceAdapterRepository, - ILoggerFactory _loggerFactory, - ILogger _logger) : IAsyncDisposable -{ - private readonly CancellationTokenSource _cts = new(); - private Task? _pollingTask; - private bool _isErrorState; - private IDictionary _monitors = new Dictionary(); - - public void Activate() - { - _pollingTask = Task.Run(() => PollDevicesAsync(_cts.Token)); - } - - private async Task PollDevicesAsync(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - // let's stick to remote devices for now as the built-int devices have their - // own logic in viewmodels - var devices = await _devicesClient.ListRemoteAresDevicesAsync( - new Empty(), - cancellationToken: cancellationToken); - - if (_isErrorState) - { - _logger.LogInformation("Device polling recovered."); - _isErrorState = false; - } - - await UpdateAdaptersFromDeviceList(devices); - } - catch (OperationCanceledException) - { - // This is expected on shutdown. - break; - } - catch (Exception ex) - { - if (!_isErrorState) - { - _logger.LogError(ex, "Error polling remote Ares devices."); - _isErrorState = true; - } - } - - try - { - await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); - } - catch (OperationCanceledException) - { - // This is expected on shutdown. - break; - } - } - } - - private async Task UpdateAdaptersFromDeviceList(ListAresRemoteDevicesResponse devicesResponse) - { - try - { - var remoteIds = devicesResponse.Devices.Select(d => d.UniqueId).ToHashSet(); - var existingIds = _deviceAdapterRepository.Keys.ToHashSet(); - - var newDevices = remoteIds.Except(existingIds); - var removedDevices = existingIds.Except(remoteIds); - - var newAdapters = newDevices - .Select(id => new RemoteDeviceAdapter(_devicesClient, id, _loggerFactory.CreateLogger())).ToArray(); - - var removedAdapters = _deviceAdapterRepository.Items.Where(da => removedDevices.Contains(da.Id)).ToArray(); - foreach (var adapter in removedAdapters) - { - if (adapter is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync(); - } - if (_monitors.Remove(adapter.Id, out var monitor)) - { - monitor.Dispose(); - } - } - - foreach (var adapter in newAdapters) - { - _ = adapter.Activate(); - var monitor = new RemoteDeviceAdapterMonitor(adapter, _loggerFactory.CreateLogger()); - _monitors[adapter.Id] = monitor; - } - - _deviceAdapterRepository.Edit(updater => - { - updater.AddOrUpdate(newAdapters); // add/update - updater.RemoveKeys(removedDevices); // remove stale - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error updating device adapter repository."); - } - } - - public async ValueTask DisposeAsync() - { - await _cts.CancelAsync(); - if (_pollingTask is not null) - { - await _pollingTask; - } - _cts.Dispose(); - GC.SuppressFinalize(this); - } -} +using Ares.Services.Device; +using Google.Protobuf.WellKnownTypes; + +namespace UI.Features.Devices.Shared; + +public class DeviceAdapterManager( + AresDevices.AresDevicesClient _devicesClient, + DeviceAdapterRepository _deviceAdapterRepository, + ILoggerFactory _loggerFactory, + ILogger _logger) : IAsyncDisposable +{ + private readonly CancellationTokenSource _cts = new(); + private Task? _pollingTask; + private bool _isErrorState; + private IDictionary _monitors = new Dictionary(); + + public void Activate() + { + _pollingTask = Task.Run(() => PollDevicesAsync(_cts.Token)); + } + + private async Task PollDevicesAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + // let's stick to remote devices for now as the built-int devices have their + // own logic in viewmodels + var devices = await _devicesClient.ListRemoteAresDevicesAsync( + new Empty(), + cancellationToken: cancellationToken); + + if (_isErrorState) + { + _logger.LogInformation("Device polling recovered."); + _isErrorState = false; + } + + await UpdateAdaptersFromDeviceList(devices); + } + catch (OperationCanceledException) + { + // This is expected on shutdown. + break; + } + catch (Exception ex) + { + if (!_isErrorState) + { + _logger.LogError(ex, "Error polling remote Ares devices."); + _isErrorState = true; + } + } + + try + { + await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); + } + catch (OperationCanceledException) + { + // This is expected on shutdown. + break; + } + } + } + + private async Task UpdateAdaptersFromDeviceList(ListAresRemoteDevicesResponse devicesResponse) + { + try + { + var remoteIds = devicesResponse.Devices.Select(d => d.UniqueId).ToHashSet(); + var existingIds = _deviceAdapterRepository.Keys.ToHashSet(); + + var newDevices = remoteIds.Except(existingIds); + var removedDevices = existingIds.Except(remoteIds); + + var newAdapters = newDevices + .Select(id => new RemoteDeviceAdapter(_devicesClient, id, _loggerFactory.CreateLogger())).ToArray(); + + var removedAdapters = _deviceAdapterRepository.Items.Where(da => removedDevices.Contains(da.Id)).ToArray(); + foreach (var adapter in removedAdapters) + { + if (adapter is IAsyncDisposable asyncDisposable) + { + await asyncDisposable.DisposeAsync(); + } + if (_monitors.Remove(adapter.Id, out var monitor)) + { + monitor.Dispose(); + } + } + + foreach (var adapter in newAdapters) + { + _ = adapter.Activate(); + var monitor = new RemoteDeviceAdapterMonitor(adapter, _loggerFactory.CreateLogger()); + _monitors[adapter.Id] = monitor; + } + + _deviceAdapterRepository.Edit(updater => + { + updater.AddOrUpdate(newAdapters); // add/update + updater.RemoveKeys(removedDevices); // remove stale + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error updating device adapter repository."); + } + } + + public async ValueTask DisposeAsync() + { + await _cts.CancelAsync(); + if (_pollingTask is not null) + { + await _pollingTask; + } + _cts.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/UI/Backend/Devices/DeviceAdapterRepository.cs b/UI/Features/Devices/Shared/DeviceAdapterRepository.cs similarity index 94% rename from UI/Backend/Devices/DeviceAdapterRepository.cs rename to UI/Features/Devices/Shared/DeviceAdapterRepository.cs index 8fe5c29a..a9295cc4 100644 --- a/UI/Backend/Devices/DeviceAdapterRepository.cs +++ b/UI/Features/Devices/Shared/DeviceAdapterRepository.cs @@ -1,51 +1,51 @@ -using DynamicData; -using DynamicData.Kernel; - -namespace UI.Backend.Devices; - -public class DeviceAdapterRepository : ISourceCache, IDisposable -{ - private readonly SourceCache _cache = new(device => device.Id); - - public Func KeySelector => _cache.KeySelector; - - public int Count => _cache.Count; - - public IReadOnlyList Items => _cache.Items; - - public IReadOnlyList Keys => _cache.Keys; - - public IReadOnlyDictionary KeyValues => _cache.KeyValues; - - public IObservable CountChanged => _cache.CountChanged; - - public IObservable> Connect(Func? predicate = null, bool suppressEmptyChangeSets = true) - { - return _cache.Connect(predicate, suppressEmptyChangeSets); - } - - public void Dispose() - { - _cache.Dispose(); - } - - public void Edit(Action> updateAction) - { - _cache.Edit(updateAction); - } - - public Optional Lookup(string key) - { - return _cache.Lookup(key); - } - - public IObservable> Preview(Func? predicate = null) - { - return _cache.Preview(predicate); - } - - public IObservable> Watch(string key) - { - return _cache.Watch(key); - } -} +using DynamicData; +using DynamicData.Kernel; + +namespace UI.Features.Devices.Shared; + +public class DeviceAdapterRepository : ISourceCache, IDisposable +{ + private readonly SourceCache _cache = new(device => device.Id); + + public Func KeySelector => _cache.KeySelector; + + public int Count => _cache.Count; + + public IReadOnlyList Items => _cache.Items; + + public IReadOnlyList Keys => _cache.Keys; + + public IReadOnlyDictionary KeyValues => _cache.KeyValues; + + public IObservable CountChanged => _cache.CountChanged; + + public IObservable> Connect(Func? predicate = null, bool suppressEmptyChangeSets = true) + { + return _cache.Connect(predicate, suppressEmptyChangeSets); + } + + public void Dispose() + { + _cache.Dispose(); + } + + public void Edit(Action> updateAction) + { + _cache.Edit(updateAction); + } + + public Optional Lookup(string key) + { + return _cache.Lookup(key); + } + + public IObservable> Preview(Func? predicate = null) + { + return _cache.Preview(predicate); + } + + public IObservable> Watch(string key) + { + return _cache.Watch(key); + } +} diff --git a/UI/Backend/Devices/DeviceDeletedMessage.cs b/UI/Features/Devices/Shared/DeviceDeletedMessage.cs similarity index 56% rename from UI/Backend/Devices/DeviceDeletedMessage.cs rename to UI/Features/Devices/Shared/DeviceDeletedMessage.cs index de2f8d1b..d75af552 100644 --- a/UI/Backend/Devices/DeviceDeletedMessage.cs +++ b/UI/Features/Devices/Shared/DeviceDeletedMessage.cs @@ -1,3 +1,3 @@ -namespace UI.Backend.Devices; +namespace UI.Features.Devices.Shared; public record DeviceDeletedMessage(string DeviceId); diff --git a/UI/Pages/Shared/Settings/Device/DeviceInfoContainer.razor b/UI/Features/Devices/Shared/DeviceInfoContainer.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/DeviceInfoContainer.razor rename to UI/Features/Devices/Shared/DeviceInfoContainer.razor diff --git a/UI/Backend/Devices/IAresDeviceAdapter.cs b/UI/Features/Devices/Shared/IAresDeviceAdapter.cs similarity index 90% rename from UI/Backend/Devices/IAresDeviceAdapter.cs rename to UI/Features/Devices/Shared/IAresDeviceAdapter.cs index 7211200d..5b074856 100644 --- a/UI/Backend/Devices/IAresDeviceAdapter.cs +++ b/UI/Features/Devices/Shared/IAresDeviceAdapter.cs @@ -1,19 +1,19 @@ -using Ares.Datamodel; -using Ares.Datamodel.Device; - -namespace UI.Backend.Devices; - -public interface IAresDeviceAdapter -{ - string Id { get; } - string Name { get; } - string Description { get; } - string Type { get; } - string Version { get; } - Task Activate(); - Task UpdateConnectionStatus(); - AresStruct? State { get; } - IObservable StateStream { get; } - DeviceOperationalStatus OperationalStatus { get; } - IObservable ConnectionStatusStream { get; } -} +using Ares.Datamodel; +using Ares.Datamodel.Device; + +namespace UI.Features.Devices.Shared; + +public interface IAresDeviceAdapter +{ + string Id { get; } + string Name { get; } + string Description { get; } + string Type { get; } + string Version { get; } + Task Activate(); + Task UpdateConnectionStatus(); + AresStruct? State { get; } + IObservable StateStream { get; } + DeviceOperationalStatus OperationalStatus { get; } + IObservable ConnectionStatusStream { get; } +} diff --git a/UI/Backend/Devices/RemoteDeviceAdapter.cs b/UI/Features/Devices/Shared/RemoteDeviceAdapter.cs similarity index 99% rename from UI/Backend/Devices/RemoteDeviceAdapter.cs rename to UI/Features/Devices/Shared/RemoteDeviceAdapter.cs index 3453704a..698ec4d3 100644 --- a/UI/Backend/Devices/RemoteDeviceAdapter.cs +++ b/UI/Features/Devices/Shared/RemoteDeviceAdapter.cs @@ -5,13 +5,13 @@ using Ares.Services.Device; using Grpc.Core; -namespace UI.Backend.Devices; +namespace UI.Features.Devices.Shared; public sealed class RemoteDeviceAdapter : IAresDeviceAdapter, IAsyncDisposable { private readonly BehaviorSubject _stateSubject = new(new AresStruct()); private readonly BehaviorSubject _connectionStatusSubject = - new BehaviorSubject(ConnectionStatus.Undefined); - + new BehaviorSubject(ConnectionStatus.Undefined); + private readonly AresDevices.AresDevicesClient _devicesClient; private readonly ILogger _logger; private CancellationTokenSource _stateStreamCts = new(); @@ -61,19 +61,19 @@ public async Task Activate() return true; } - public async Task StartStatusUpdate() - { + public async Task StartStatusUpdate() + { await _statusStreamCts.CancelAsync(); _statusStreamCts = new CancellationTokenSource(); var token = _statusStreamCts.Token; - _ = Task.Run(async () => - { - while(!_statusStreamCts.IsCancellationRequested) - { - await UpdateConnectionStatus(); - await Task.Delay(TimeSpan.FromSeconds(5)); - } - }, _statusStreamCts.Token); + _ = Task.Run(async () => + { + while(!_statusStreamCts.IsCancellationRequested) + { + await UpdateConnectionStatus(); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + }, _statusStreamCts.Token); } public async Task StartStateStream() @@ -82,8 +82,8 @@ public async Task StartStateStream() _stateStreamCts = new CancellationTokenSource(); var token = _stateStreamCts.Token; Active = true; - _ = Task.Run(async () => - { + _ = Task.Run(async () => + { try { using var call = _devicesClient.GetDeviceStateStream(new DeviceStateStreamRequest { DeviceId = Id, PollingSettings = _pollingSettings }); @@ -92,13 +92,13 @@ public async Task StartStateStream() await foreach(var state in call.ResponseStream.ReadAllAsync(token)) { _stateSubject.OnNext(state.State); - } - } - catch(Exception e) - { - _logger.LogError("Device stream for {name} has stopped. {ex}", Name, e.Message); - UpdateStatusIfChanged(ConnectionStatus.Disconnected); - } + } + } + catch(Exception e) + { + _logger.LogError("Device stream for {name} has stopped. {ex}", Name, e.Message); + UpdateStatusIfChanged(ConnectionStatus.Disconnected); + } }, _stateStreamCts.Token); Active = false; @@ -111,13 +111,13 @@ internal async Task FetchOperationalStatus() var callOpts = new CallOptions(deadline: DateTime.UtcNow.AddSeconds(5)); var status = await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = Id }, callOpts); _logger.LogInformation("Fetched operational status for device {name}.", Name); - if(status.OperationalState == OperationalState.Active) - { - UpdateStatusIfChanged(ConnectionStatus.ConnectedToDevice); + if(status.OperationalState == OperationalState.Active) + { + UpdateStatusIfChanged(ConnectionStatus.ConnectedToDevice); } - else + else { - UpdateStatusIfChanged(ConnectionStatus.ConnectedToService); + UpdateStatusIfChanged(ConnectionStatus.ConnectedToService); } OperationalStatus = status; } diff --git a/UI/Backend/Devices/RemoteDeviceAdapterMonitor.cs b/UI/Features/Devices/Shared/RemoteDeviceAdapterMonitor.cs similarity index 96% rename from UI/Backend/Devices/RemoteDeviceAdapterMonitor.cs rename to UI/Features/Devices/Shared/RemoteDeviceAdapterMonitor.cs index 03230cc8..48a30533 100644 --- a/UI/Backend/Devices/RemoteDeviceAdapterMonitor.cs +++ b/UI/Features/Devices/Shared/RemoteDeviceAdapterMonitor.cs @@ -1,143 +1,143 @@ using System.Reactive.Disposables; using System.Reactive.Linq; -namespace UI.Backend.Devices; +namespace UI.Features.Devices.Shared; public class RemoteDeviceAdapterMonitor(IAresDeviceAdapter deviceAdapter, ILogger logger) : IDisposable -{ - private IDisposable _deviceStatusWatcher = Disposable.Empty; - private Task _reconnectLoop = Task.CompletedTask; - private CancellationTokenSource _reconnectCts = new(); - - // Interlocked-backed state - private int _reconnectInProgress = 0; // 0=false, 1=true - private int _reconnectAttempts = 0; - private int _activated = 0; // 0=false, 1=true - private readonly IAresDeviceAdapter _deviceAdapter = deviceAdapter; - private readonly ILogger _logger = logger; - - public void Activate() - { - // ensure one-time activation - if(Interlocked.Exchange(ref _activated, 1) == 1) - { - _logger.LogDebug("Monitor already activated {}.", _deviceAdapter.Name); - return; - } - - _deviceStatusWatcher = _deviceAdapter.ConnectionStatusStream - .DistinctUntilChanged() - .Subscribe(async status => - { - try - { - if(status == ConnectionStatus.Disconnected) - { - StartReconnectionLoop(); - } - else if(status == ConnectionStatus.ConnectedToService) - { - await EndReconnectionLoop().ConfigureAwait(false); - _logger.LogInformation("Device connected; activating adapter."); - await _deviceAdapter.Activate().ConfigureAwait(false); - } - } - catch(Exception ex) - { - _logger.LogError(ex, "Error handling status {Status}", status); - } - }); - } - - private void StartReconnectionLoop() - { - // fast exit if already running - if(Interlocked.Exchange(ref _reconnectInProgress, 1) == 1) - return; - - // reset/replace CTS defensively - try { _reconnectCts.Cancel(); } catch { /* ignore */ } - _reconnectCts.Dispose(); - _reconnectCts = new CancellationTokenSource(); - - Interlocked.Exchange(ref _reconnectAttempts, 0); - _logger.LogWarning("Starting reconnection loop."); - - _reconnectLoop = Task.Run(async () => - { - var token = _reconnectCts.Token; - - while(!token.IsCancellationRequested) - { - try - { - await _deviceAdapter.UpdateConnectionStatus().ConfigureAwait(false); - - var attempts = Interlocked.Increment(ref _reconnectAttempts); - - // linear backoff (cap 30s) - var baseSeconds = Math.Min(attempts * 5, 30); - - // jitter ±20% - var jitter = (int)Math.Round(baseSeconds * 0.2); - var waitSeconds = Math.Max(1, baseSeconds + Random.Shared.Next(-jitter, jitter + 1)); - - _logger.LogInformation("Reconnect attempt {Attempt}. Waiting {Delay}s.", attempts, waitSeconds); - await Task.Delay(TimeSpan.FromSeconds(waitSeconds), token).ConfigureAwait(false); - } - catch(OperationCanceledException) - { - break; // expected on cancellation - } - catch(Exception ex) - { - _logger.LogError(ex, "Reconnection attempt errored."); - try { await Task.Delay(TimeSpan.FromSeconds(2), token).ConfigureAwait(false); } catch { break; } - } - } - }, _reconnectCts.Token); - } - - private async Task EndReconnectionLoop() - { - // only if we were running - if(Interlocked.Exchange(ref _reconnectInProgress, 0) == 0) - return; - - _logger.LogInformation("Ending reconnection loop."); - - try - { - await _reconnectCts.CancelAsync().ConfigureAwait(false); - try - { - await _reconnectLoop.ConfigureAwait(false); - } - catch(OperationCanceledException) - { - // ignore - } - } - finally - { - _reconnectCts.Dispose(); - _reconnectCts = new CancellationTokenSource(); - Interlocked.Exchange(ref _reconnectAttempts, 0); - } - } - - public void Dispose() - { - // best-effort, non-throwing shutdown - try { _deviceStatusWatcher.Dispose(); } catch { /* ignore */ } - - try - { - // cancel loop quickly; avoid long blocking in Dispose() - _ = EndReconnectionLoop(); - } - catch { /* ignore */ } - - GC.SuppressFinalize(this); +{ + private IDisposable _deviceStatusWatcher = Disposable.Empty; + private Task _reconnectLoop = Task.CompletedTask; + private CancellationTokenSource _reconnectCts = new(); + + // Interlocked-backed state + private int _reconnectInProgress = 0; // 0=false, 1=true + private int _reconnectAttempts = 0; + private int _activated = 0; // 0=false, 1=true + private readonly IAresDeviceAdapter _deviceAdapter = deviceAdapter; + private readonly ILogger _logger = logger; + + public void Activate() + { + // ensure one-time activation + if(Interlocked.Exchange(ref _activated, 1) == 1) + { + _logger.LogDebug("Monitor already activated {}.", _deviceAdapter.Name); + return; + } + + _deviceStatusWatcher = _deviceAdapter.ConnectionStatusStream + .DistinctUntilChanged() + .Subscribe(async status => + { + try + { + if(status == ConnectionStatus.Disconnected) + { + StartReconnectionLoop(); + } + else if(status == ConnectionStatus.ConnectedToService) + { + await EndReconnectionLoop().ConfigureAwait(false); + _logger.LogInformation("Device connected; activating adapter."); + await _deviceAdapter.Activate().ConfigureAwait(false); + } + } + catch(Exception ex) + { + _logger.LogError(ex, "Error handling status {Status}", status); + } + }); + } + + private void StartReconnectionLoop() + { + // fast exit if already running + if(Interlocked.Exchange(ref _reconnectInProgress, 1) == 1) + return; + + // reset/replace CTS defensively + try { _reconnectCts.Cancel(); } catch { /* ignore */ } + _reconnectCts.Dispose(); + _reconnectCts = new CancellationTokenSource(); + + Interlocked.Exchange(ref _reconnectAttempts, 0); + _logger.LogWarning("Starting reconnection loop."); + + _reconnectLoop = Task.Run(async () => + { + var token = _reconnectCts.Token; + + while(!token.IsCancellationRequested) + { + try + { + await _deviceAdapter.UpdateConnectionStatus().ConfigureAwait(false); + + var attempts = Interlocked.Increment(ref _reconnectAttempts); + + // linear backoff (cap 30s) + var baseSeconds = Math.Min(attempts * 5, 30); + + // jitter ±20% + var jitter = (int)Math.Round(baseSeconds * 0.2); + var waitSeconds = Math.Max(1, baseSeconds + Random.Shared.Next(-jitter, jitter + 1)); + + _logger.LogInformation("Reconnect attempt {Attempt}. Waiting {Delay}s.", attempts, waitSeconds); + await Task.Delay(TimeSpan.FromSeconds(waitSeconds), token).ConfigureAwait(false); + } + catch(OperationCanceledException) + { + break; // expected on cancellation + } + catch(Exception ex) + { + _logger.LogError(ex, "Reconnection attempt errored."); + try { await Task.Delay(TimeSpan.FromSeconds(2), token).ConfigureAwait(false); } catch { break; } + } + } + }, _reconnectCts.Token); + } + + private async Task EndReconnectionLoop() + { + // only if we were running + if(Interlocked.Exchange(ref _reconnectInProgress, 0) == 0) + return; + + _logger.LogInformation("Ending reconnection loop."); + + try + { + await _reconnectCts.CancelAsync().ConfigureAwait(false); + try + { + await _reconnectLoop.ConfigureAwait(false); + } + catch(OperationCanceledException) + { + // ignore + } + } + finally + { + _reconnectCts.Dispose(); + _reconnectCts = new CancellationTokenSource(); + Interlocked.Exchange(ref _reconnectAttempts, 0); + } + } + + public void Dispose() + { + // best-effort, non-throwing shutdown + try { _deviceStatusWatcher.Dispose(); } catch { /* ignore */ } + + try + { + // cancel loop quickly; avoid long blocking in Dispose() + _ = EndReconnectionLoop(); + } + catch { /* ignore */ } + + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/UI/Features/Devices/Settings/DeviceSettingsContainer.razor b/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor similarity index 100% rename from UI/Features/Devices/Settings/DeviceSettingsContainer.razor rename to UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor diff --git a/UI/Features/Devices/Settings/DeviceSettingsContainer.razor.css b/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor.css similarity index 100% rename from UI/Features/Devices/Settings/DeviceSettingsContainer.razor.css rename to UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor.css diff --git a/UI/Features/Devices/Settings/DeviceSettingsLayout.razor b/UI/Features/Devices/Shared/Settings/DeviceSettingsLayout.razor similarity index 98% rename from UI/Features/Devices/Settings/DeviceSettingsLayout.razor rename to UI/Features/Devices/Shared/Settings/DeviceSettingsLayout.razor index 7f7f0539..ab6d9c5c 100644 --- a/UI/Features/Devices/Settings/DeviceSettingsLayout.razor +++ b/UI/Features/Devices/Shared/Settings/DeviceSettingsLayout.razor @@ -1,4 +1,4 @@ -@using UI.Features.Settings +@using UI.Components.Layouts @inherits LayoutComponentBase @layout SettingsLayout diff --git a/UI/Features/Devices/Settings/EmptyDeviceSettingsPage.razor b/UI/Features/Devices/Shared/Settings/EmptyDeviceSettingsPage.razor similarity index 100% rename from UI/Features/Devices/Settings/EmptyDeviceSettingsPage.razor rename to UI/Features/Devices/Shared/Settings/EmptyDeviceSettingsPage.razor diff --git a/UI/Pages/Shared/Devices/StepperController/BoolVisualizer.razor b/UI/Features/Devices/StepperController/BoolVisualizer.razor similarity index 94% rename from UI/Pages/Shared/Devices/StepperController/BoolVisualizer.razor rename to UI/Features/Devices/StepperController/BoolVisualizer.razor index dea7bf23..5e33cce1 100644 --- a/UI/Pages/Shared/Devices/StepperController/BoolVisualizer.razor +++ b/UI/Features/Devices/StepperController/BoolVisualizer.razor @@ -1,19 +1,19 @@ - -
- @if (Bool) - { - - } - else - { - - } - @Label -
-@code { - [Parameter] - public bool Bool { get; set; } - - [Parameter] - public string Label { get; set; } = string.Empty; -} + +
+ @if (Bool) + { + + } + else + { + + } + @Label +
+@code { + [Parameter] + public bool Bool { get; set; } + + [Parameter] + public string Label { get; set; } = string.Empty; +} diff --git a/UI/Pages/Shared/Devices/StepperController/SimpleDataDisplay.razor b/UI/Features/Devices/StepperController/SimpleDataDisplay.razor similarity index 96% rename from UI/Pages/Shared/Devices/StepperController/SimpleDataDisplay.razor rename to UI/Features/Devices/StepperController/SimpleDataDisplay.razor index fdd506ac..c74388fc 100644 --- a/UI/Pages/Shared/Devices/StepperController/SimpleDataDisplay.razor +++ b/UI/Features/Devices/StepperController/SimpleDataDisplay.razor @@ -1,12 +1,12 @@ - - @Label - - -@code { - [Parameter] - public string Label { get; set; } = string.Empty; - - [Parameter] - public object Data { get; set; } = string.Empty; - -} + + @Label + + +@code { + [Parameter] + public string Label { get; set; } = string.Empty; + + [Parameter] + public object Data { get; set; } = string.Empty; + +} diff --git a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor b/UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor rename to UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor index 2973b74d..190624b3 100644 --- a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor @@ -1,88 +1,88 @@ -@using TicStepperController.Messaging; -@using UI.Backend.ViewModels.Settings.Device.StepperController - -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @if(ViewModel!.DynamicStepCalculation) - { - - - - - - - - - - - - - - - - } - - - - - - - - - - - - - - - - - - - - - -@code { - [Parameter] - public Action OnValidSubmit { get; set; } = delegate - { - }; -} +@using TicStepperController.Messaging; +@using UI.Backend.ViewModels.Settings.Device.StepperController + +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if(ViewModel!.DynamicStepCalculation) + { + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + +@code { + [Parameter] + public Action OnValidSubmit { get; set; } = delegate + { + }; +} diff --git a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor.css b/UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor.css similarity index 92% rename from UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor.css rename to UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor.css index 8f89307d..7c44cfee 100644 --- a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerConfigEditView.razor.css +++ b/UI/Features/Devices/StepperController/StepperControllerConfigEditView.razor.css @@ -1,54 +1,54 @@ -.stepper-config-grid { - display: grid; - grid-template: - "name ." 1fr - "port ." 1fr - "simulated ." 1fr - "properties ." auto - / auto 40px -} - -.stepper-property-grid { - grid-area: properties; - grid-template-columns: 1fr 1fr; -} - -.stepper-max-acceleration { - grid-column: 0; -} - -.stepper-max-deceleration { - grid-column: 1; -} - -.stepper-starting-speed { - grid-column: 0; -} - -.stepper-max-speed { - grid-column: 1; -} - -.stepper-custom-step-size { - grid-column: 0; -} - -.stepper-step-mode { - grid-column: 1; -} - -.stepper-name { - grid-area: name; -} - -.stepper-port { - grid-area: port; -} - -.stepper-id { - grid-area: id; -} - -.stepper-simulated { - grid-area: simulated; +.stepper-config-grid { + display: grid; + grid-template: + "name ." 1fr + "port ." 1fr + "simulated ." 1fr + "properties ." auto + / auto 40px +} + +.stepper-property-grid { + grid-area: properties; + grid-template-columns: 1fr 1fr; +} + +.stepper-max-acceleration { + grid-column: 0; +} + +.stepper-max-deceleration { + grid-column: 1; +} + +.stepper-starting-speed { + grid-column: 0; +} + +.stepper-max-speed { + grid-column: 1; +} + +.stepper-custom-step-size { + grid-column: 0; +} + +.stepper-step-mode { + grid-column: 1; +} + +.stepper-name { + grid-area: name; +} + +.stepper-port { + grid-area: port; +} + +.stepper-id { + grid-area: id; +} + +.stepper-simulated { + grid-area: simulated; } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerConfigEditViewModel.cs b/UI/Features/Devices/StepperController/StepperControllerConfigEditViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerConfigEditViewModel.cs rename to UI/Features/Devices/StepperController/StepperControllerConfigEditViewModel.cs index 44ab97cd..f27fc5fa 100644 --- a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerConfigEditViewModel.cs +++ b/UI/Features/Devices/StepperController/StepperControllerConfigEditViewModel.cs @@ -1,174 +1,174 @@ -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using System.ComponentModel.DataAnnotations; -using Ares.Services.Device; -using TicStepperController.Config; -using TicStepperController.Messaging; - -namespace UI.Backend.ViewModels.Settings.Device.StepperController; - -public partial class StepperControllerConfigEditViewModel : ReactiveObject -{ - private readonly StepperControllerRpc.StepperControllerRpcClient _stepperClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly StepperControllerConfig _config; - private string _name = string.Empty; - - public StepperControllerConfigEditViewModel(StepperControllerRpc.StepperControllerRpcClient stepperClient, AresDevices.AresDevicesClient devicesClient) - { - _stepperClient = stepperClient; - _devicesClient = devicesClient; - _config = new StepperControllerConfig(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - StepAngle = 1.8; - CustomStepSize = 1; - } - - public StepperControllerConfigEditViewModel(StepperControllerRpc.StepperControllerRpcClient stepperClient, AresDevices.AresDevicesClient devicesClient, - StepperControllerConfig config) - { - _stepperClient = stepperClient; - _devicesClient = devicesClient; - _config = config; - _ = UpdateAvailableSerialPorts(); - StepAngle = 1.8; - CustomStepSize = 1; - LoadConfig(config); - } - - public bool NewConfig { get; private set; } - - [Required] - public string Name - { - get => _name; - set - { - if(!NewConfig) - { - return; - } - - this.RaiseAndSetIfChanged(ref _name, value); - } - } - - public void LoadConfig(StepperControllerConfig config) - { - Port = config.PortName; - _name = config.Name; - MaxAcceleration = config.MaxAcceleration; - MaxDeceleration = config.MaxDeceleration; - StartingSpeed = config.StartingSpeed; - CustomStepSize = config.CustomStepSize; - MaxSpeed = config.MaxSpeed; - CurrentLimit = config.CurrentLimit; - StepMode = config.StepMode; - Simulated = config.Simulated; - DynamicStepCalculation = config.DynamicStepCalculation; - InitialSpoolRadius = config.SpoolRadius; - FilterPaperThickness = config.FilterPaperThickness; - LinearStepSize = config.IdealLinearStepSize; - StepAngle = config.StepAngle; - } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - [Reactive] - [Required] - public partial string? Port { get; set; } - - [Reactive] - [Range(100, 2_147_483_647, ErrorMessage = "Acceleration must be a value from 100 to 2,147,483,647 microsteps per 100 s²")] - public partial uint? MaxAcceleration { get; set; } - - [Reactive] - [Range(100, 2_147_483_647, ErrorMessage = "Deceleration must be a value from 100 to 2,147,483,647 microsteps per 100 s²")] - public partial uint? MaxDeceleration { get; set; } - - [Reactive] - [Range(0, 500_000_000, ErrorMessage = "Starting speed must be a value from 0 to 500,000,000 microsteps per 10,000 s")] - public partial uint? StartingSpeed { get; set; } - - [Reactive] - [Range(1, uint.MaxValue, ErrorMessage = "Custom step size must be greater than 0")] - public partial uint? CustomStepSize { get; set; } - - [Reactive] - [Range(100, 500_000_000, ErrorMessage = "Max speed must be a value from 0 to 500,000,000 microsteps per 10,000 s")] - public partial uint? MaxSpeed { get; set; } - - [Reactive] - [Range(0, 64, ErrorMessage = "Current Limit must be between 0 and 64")] - public partial uint? CurrentLimit { get; set; } - - [Reactive] - public partial StepMode StepMode { get; set; } - - [Reactive] - public partial bool Simulated { get; set; } - - [Reactive] - public partial bool DynamicStepCalculation { get; set; } //Determines whether a spool is set to dynamically calculate its steps - - [Reactive] - public partial double? InitialSpoolRadius { get; set; } - - [Reactive] - public partial double? FilterPaperThickness { get; set; } - - [Reactive] - public partial double? LinearStepSize { get; set; } - - [Reactive] - public partial double? StepAngle { get; set; } - - public bool Modified => _config.Name != Name - || _config.PortName != Port - || _config.MaxAcceleration != MaxAcceleration - || _config.MaxDeceleration != MaxDeceleration - || _config.StartingSpeed != StartingSpeed - || _config.CustomStepSize != CustomStepSize - || _config.MaxSpeed != MaxSpeed - || _config.CurrentLimit != CurrentLimit - || _config.StepMode != StepMode - || _config.Simulated != Simulated - || _config.DynamicStepCalculation != DynamicStepCalculation - || _config.SpoolRadius != InitialSpoolRadius - || _config.FilterPaperThickness != FilterPaperThickness - || _config.IdealLinearStepSize != LinearStepSize - || _config.StepAngle != StepAngle; - - public StepperControllerConfig Save() - { - return Modified ? new StepperControllerConfig - { - Name = Name, - Simulated = Simulated, - StepMode = StepMode, - CustomStepSize = CustomStepSize, - MaxAcceleration = MaxAcceleration, - MaxDeceleration = MaxDeceleration, - CurrentLimit = CurrentLimit, - StartingSpeed = StartingSpeed, - MaxSpeed = MaxSpeed, - IdealLinearStepSize = LinearStepSize, - SpoolRadius = InitialSpoolRadius, - FilterPaperThickness = FilterPaperThickness, - StepAngle = StepAngle, - DynamicStepCalculation = DynamicStepCalculation, - PortName = Port - } : _config; - } -} +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using System.ComponentModel.DataAnnotations; +using Ares.Services.Device; +using TicStepperController.Config; +using TicStepperController.Messaging; + +namespace UI.Backend.ViewModels.Settings.Device.StepperController; + +public partial class StepperControllerConfigEditViewModel : ReactiveObject +{ + private readonly StepperControllerRpc.StepperControllerRpcClient _stepperClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly StepperControllerConfig _config; + private string _name = string.Empty; + + public StepperControllerConfigEditViewModel(StepperControllerRpc.StepperControllerRpcClient stepperClient, AresDevices.AresDevicesClient devicesClient) + { + _stepperClient = stepperClient; + _devicesClient = devicesClient; + _config = new StepperControllerConfig(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + StepAngle = 1.8; + CustomStepSize = 1; + } + + public StepperControllerConfigEditViewModel(StepperControllerRpc.StepperControllerRpcClient stepperClient, AresDevices.AresDevicesClient devicesClient, + StepperControllerConfig config) + { + _stepperClient = stepperClient; + _devicesClient = devicesClient; + _config = config; + _ = UpdateAvailableSerialPorts(); + StepAngle = 1.8; + CustomStepSize = 1; + LoadConfig(config); + } + + public bool NewConfig { get; private set; } + + [Required] + public string Name + { + get => _name; + set + { + if(!NewConfig) + { + return; + } + + this.RaiseAndSetIfChanged(ref _name, value); + } + } + + public void LoadConfig(StepperControllerConfig config) + { + Port = config.PortName; + _name = config.Name; + MaxAcceleration = config.MaxAcceleration; + MaxDeceleration = config.MaxDeceleration; + StartingSpeed = config.StartingSpeed; + CustomStepSize = config.CustomStepSize; + MaxSpeed = config.MaxSpeed; + CurrentLimit = config.CurrentLimit; + StepMode = config.StepMode; + Simulated = config.Simulated; + DynamicStepCalculation = config.DynamicStepCalculation; + InitialSpoolRadius = config.SpoolRadius; + FilterPaperThickness = config.FilterPaperThickness; + LinearStepSize = config.IdealLinearStepSize; + StepAngle = config.StepAngle; + } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + [Reactive] + [Required] + public partial string? Port { get; set; } + + [Reactive] + [Range(100, 2_147_483_647, ErrorMessage = "Acceleration must be a value from 100 to 2,147,483,647 microsteps per 100 s²")] + public partial uint? MaxAcceleration { get; set; } + + [Reactive] + [Range(100, 2_147_483_647, ErrorMessage = "Deceleration must be a value from 100 to 2,147,483,647 microsteps per 100 s²")] + public partial uint? MaxDeceleration { get; set; } + + [Reactive] + [Range(0, 500_000_000, ErrorMessage = "Starting speed must be a value from 0 to 500,000,000 microsteps per 10,000 s")] + public partial uint? StartingSpeed { get; set; } + + [Reactive] + [Range(1, uint.MaxValue, ErrorMessage = "Custom step size must be greater than 0")] + public partial uint? CustomStepSize { get; set; } + + [Reactive] + [Range(100, 500_000_000, ErrorMessage = "Max speed must be a value from 0 to 500,000,000 microsteps per 10,000 s")] + public partial uint? MaxSpeed { get; set; } + + [Reactive] + [Range(0, 64, ErrorMessage = "Current Limit must be between 0 and 64")] + public partial uint? CurrentLimit { get; set; } + + [Reactive] + public partial StepMode StepMode { get; set; } + + [Reactive] + public partial bool Simulated { get; set; } + + [Reactive] + public partial bool DynamicStepCalculation { get; set; } //Determines whether a spool is set to dynamically calculate its steps + + [Reactive] + public partial double? InitialSpoolRadius { get; set; } + + [Reactive] + public partial double? FilterPaperThickness { get; set; } + + [Reactive] + public partial double? LinearStepSize { get; set; } + + [Reactive] + public partial double? StepAngle { get; set; } + + public bool Modified => _config.Name != Name + || _config.PortName != Port + || _config.MaxAcceleration != MaxAcceleration + || _config.MaxDeceleration != MaxDeceleration + || _config.StartingSpeed != StartingSpeed + || _config.CustomStepSize != CustomStepSize + || _config.MaxSpeed != MaxSpeed + || _config.CurrentLimit != CurrentLimit + || _config.StepMode != StepMode + || _config.Simulated != Simulated + || _config.DynamicStepCalculation != DynamicStepCalculation + || _config.SpoolRadius != InitialSpoolRadius + || _config.FilterPaperThickness != FilterPaperThickness + || _config.IdealLinearStepSize != LinearStepSize + || _config.StepAngle != StepAngle; + + public StepperControllerConfig Save() + { + return Modified ? new StepperControllerConfig + { + Name = Name, + Simulated = Simulated, + StepMode = StepMode, + CustomStepSize = CustomStepSize, + MaxAcceleration = MaxAcceleration, + MaxDeceleration = MaxDeceleration, + CurrentLimit = CurrentLimit, + StartingSpeed = StartingSpeed, + MaxSpeed = MaxSpeed, + IdealLinearStepSize = LinearStepSize, + SpoolRadius = InitialSpoolRadius, + FilterPaperThickness = FilterPaperThickness, + StepAngle = StepAngle, + DynamicStepCalculation = DynamicStepCalculation, + PortName = Port + } : _config; + } +} diff --git a/UI/Backend/Factories/StepperControllerDeviceControlViewModelFactory.cs b/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs similarity index 94% rename from UI/Backend/Factories/StepperControllerDeviceControlViewModelFactory.cs rename to UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs index 41052ecd..d8dbc546 100644 --- a/UI/Backend/Factories/StepperControllerDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs @@ -2,11 +2,12 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using TicStepperController.Messaging; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.StepperController; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.StepperController; public class StepperControllerDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsListView.razor b/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor similarity index 98% rename from UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsListView.razor rename to UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor index a9100cae..92f32da9 100644 --- a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsListView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor @@ -1,6 +1,7 @@ @page "/settings/device/stepper_controller" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.StepperController +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsListViewModel.cs b/UI/Features/Devices/StepperController/StepperControllerSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsListViewModel.cs rename to UI/Features/Devices/StepperController/StepperControllerSettingsListViewModel.cs index 67b9762f..6d6c9bf6 100644 --- a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsListViewModel.cs +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsListViewModel.cs @@ -1,57 +1,57 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using TicStepperController; -using TicStepperController.Config; -using TicStepperController.Messaging; - -namespace UI.Backend.ViewModels.Settings.Device.StepperController; - -public partial class StepperControllerSettingsListViewModel : ReactiveObject -{ - private readonly StepperControllerRpc.StepperControllerRpcClient _stepperControllerClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public StepperControllerSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, StepperControllerRpc.StepperControllerRpcClient stepperControllerClient, IMessenger messenger) - { - _devicesClient = devicesClient; - _stepperControllerClient = stepperControllerClient; - _messenger = messenger; - UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new StepperControllerSettingsViewModel(config, _stepperControllerClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public StepperControllerConfigEditViewModel GetNewConfigEditViewModel() - => new(_stepperControllerClient, _devicesClient); - - private Task UpdateConfigs() - { - SettingsViewModels = null; - return _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IStepperController).FullName }) - .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); - } - - public async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(StepperControllerConfig config) - { - await _stepperControllerClient.AddStepperControllerAsync(config); - await UpdateConfigs(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using TicStepperController; +using TicStepperController.Config; +using TicStepperController.Messaging; + +namespace UI.Backend.ViewModels.Settings.Device.StepperController; + +public partial class StepperControllerSettingsListViewModel : ReactiveObject +{ + private readonly StepperControllerRpc.StepperControllerRpcClient _stepperControllerClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public StepperControllerSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, StepperControllerRpc.StepperControllerRpcClient stepperControllerClient, IMessenger messenger) + { + _devicesClient = devicesClient; + _stepperControllerClient = stepperControllerClient; + _messenger = messenger; + UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new StepperControllerSettingsViewModel(config, _stepperControllerClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public StepperControllerConfigEditViewModel GetNewConfigEditViewModel() + => new(_stepperControllerClient, _devicesClient); + + private Task UpdateConfigs() + { + SettingsViewModels = null; + return _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IStepperController).FullName }) + .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); + } + + public async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(StepperControllerConfig config) + { + await _stepperControllerClient.AddStepperControllerAsync(config); + await UpdateConfigs(); + } +} diff --git a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor rename to UI/Features/Devices/StepperController/StepperControllerSettingsView.razor index d7a357f2..ca6f3835 100644 --- a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor @@ -1,74 +1,75 @@ -@using UI.Backend.ViewModels.Settings.Device.StepperController -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - -
-
- Port - - @ViewModel!.StepperControllerConfig.PortName -
- - @if (ViewModel!.DeviceActive) - { -
- @if (_changing) - { -
-
- -
-
- } -
-
-
- } - else if(_initActive) - { - - } -
-
- -@code { - private bool _initActive; - private bool _changing; - - private string Name => ViewModel!.StepperControllerConfig.Name + (ViewModel!.StepperControllerConfig.Simulated ? " -- (Simulated)" : ""); - - protected override async Task OnInitializedAsync() - { - _initActive = true; - StateHasChanged(); - await ViewModel!.Init(); - _initActive = false; - StateHasChanged(); - } - - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit Stepper", ds => - @); - - _changing = true; - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - _changing = false; - } - - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.StepperControllerConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } +@using UI.Backend.ViewModels.Settings.Device.StepperController +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + +
+
+ Port - + @ViewModel!.StepperControllerConfig.PortName +
+ + @if (ViewModel!.DeviceActive) + { +
+ @if (_changing) + { +
+
+ +
+
+ } +
+
+
+ } + else if(_initActive) + { + + } +
+
+ +@code { + private bool _initActive; + private bool _changing; + + private string Name => ViewModel!.StepperControllerConfig.Name + (ViewModel!.StepperControllerConfig.Simulated ? " -- (Simulated)" : ""); + + protected override async Task OnInitializedAsync() + { + _initActive = true; + StateHasChanged(); + await ViewModel!.Init(); + _initActive = false; + StateHasChanged(); + } + + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit Stepper", ds => + @); + + _changing = true; + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + _changing = false; + } + + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.StepperControllerConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor.css b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor.css similarity index 95% rename from UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor.css rename to UI/Features/Devices/StepperController/StepperControllerSettingsView.razor.css index 65a3b003..c09905ae 100644 --- a/UI/Pages/Shared/Settings/Device/StepperController/StepperControllerSettingsView.razor.css +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor.css @@ -1,21 +1,21 @@ -.stepper-settings-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - position: relative; -} - -.stepper-settings-col1 { - display: grid; - align-items: center; - grid-template-columns: 1fr 1fr auto auto; - grid-column-gap: 5px; - max-width: 350px; -} - -.disabled-overlay { - width: 100%; - height: 100%; - z-index: 10; - position: absolute; - background: rgba(0, 0, 0, 0.25); +.stepper-settings-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + position: relative; +} + +.stepper-settings-col1 { + display: grid; + align-items: center; + grid-template-columns: 1fr 1fr auto auto; + grid-column-gap: 5px; + max-width: 350px; +} + +.disabled-overlay { + width: 100%; + height: 100%; + z-index: 10; + position: absolute; + background: rgba(0, 0, 0, 0.25); } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsViewModel.cs b/UI/Features/Devices/StepperController/StepperControllerSettingsViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsViewModel.cs rename to UI/Features/Devices/StepperController/StepperControllerSettingsViewModel.cs index 684d28b1..58aab4f7 100644 --- a/UI/Backend/ViewModels/Settings/Device/StepperController/StepperControllerSettingsViewModel.cs +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsViewModel.cs @@ -1,103 +1,103 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using TicStepperController.Config; -using TicStepperController.Messaging; -using UI.Backend.Devices; - -namespace UI.Backend.ViewModels.Settings.Device.StepperController; - -public partial class StepperControllerSettingsViewModel : ReactiveObject -{ - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly StepperControllerRpc.StepperControllerRpcClient _stepperControllerClient; - private readonly IMessenger _messenger; - - public StepperControllerSettingsViewModel(DeviceConfig deviceConfig, - StepperControllerRpc.StepperControllerRpcClient stepperControllerClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - StepperControllerConfig = deviceConfig.ConfigData.Unpack(); - _stepperControllerClient = stepperControllerClient; - _messenger = messenger; - _devicesClient = devicesClient; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new StepperControllerConfigEditViewModel(_stepperControllerClient, _devicesClient, StepperControllerConfig); - } - - public StepperControllerConfig StepperControllerConfig { get; } - - public Func OnRemoveCallback { get; } - - public StepperControllerConfigEditViewModel EditViewModel { get; } - - public async Task GetDeviceOperationalStatus() - { - try - { - var status = await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - DeviceActive = status.OperationalState is OperationalState.Active; - return status; - } - catch(RpcException) - { - return new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered stepper controller with a name {StepperControllerConfig.Name}" }; - } - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - - public async Task Save() - { - var stepperControllerConfig = EditViewModel.Save(); - var updateRequest = new StepperControllerUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = stepperControllerConfig - }; - - await _stepperControllerClient.UpdateStepperControllerAsync(updateRequest); - } - - public async Task Remove() - { - await _stepperControllerClient.RemoveStepperControllerAsync(new TicRequest { TicId = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } - - public async Task Init() - { - var status = await GetDeviceOperationalStatus(); - if(status.OperationalState != OperationalState.Active) - return; - - var state = await _stepperControllerClient.GetStateAsync(new TicRequest { TicId = _deviceConfig.UniqueId }); - - MaxAcceleration = state.MaxAcceleration; - MaxDeceleration = state.MaxDeceleration; - CurrentLimit = state.CurrentLimit; - StartingSpeed = state.StartingSpeed; - CustomStepSize = state.CustomStepSize; - MaxSpeed = state.MaxSpeed; - StepMode = state.StepMode; - } - - [Reactive] - public partial bool DeviceActive { get; private set; } - public uint MaxAcceleration { get; private set; } - public uint MaxDeceleration { get; private set; } - public uint CurrentLimit { get; private set; } - public uint StartingSpeed { get; private set; } - public uint CustomStepSize { get; private set; } - public uint MaxSpeed { get; private set; } - public StepMode StepMode { get; private set; } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using TicStepperController.Config; +using TicStepperController.Messaging; +using UI.Features.Devices.Shared; + +namespace UI.Backend.ViewModels.Settings.Device.StepperController; + +public partial class StepperControllerSettingsViewModel : ReactiveObject +{ + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly StepperControllerRpc.StepperControllerRpcClient _stepperControllerClient; + private readonly IMessenger _messenger; + + public StepperControllerSettingsViewModel(DeviceConfig deviceConfig, + StepperControllerRpc.StepperControllerRpcClient stepperControllerClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + StepperControllerConfig = deviceConfig.ConfigData.Unpack(); + _stepperControllerClient = stepperControllerClient; + _messenger = messenger; + _devicesClient = devicesClient; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new StepperControllerConfigEditViewModel(_stepperControllerClient, _devicesClient, StepperControllerConfig); + } + + public StepperControllerConfig StepperControllerConfig { get; } + + public Func OnRemoveCallback { get; } + + public StepperControllerConfigEditViewModel EditViewModel { get; } + + public async Task GetDeviceOperationalStatus() + { + try + { + var status = await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + DeviceActive = status.OperationalState is OperationalState.Active; + return status; + } + catch(RpcException) + { + return new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered stepper controller with a name {StepperControllerConfig.Name}" }; + } + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + + public async Task Save() + { + var stepperControllerConfig = EditViewModel.Save(); + var updateRequest = new StepperControllerUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = stepperControllerConfig + }; + + await _stepperControllerClient.UpdateStepperControllerAsync(updateRequest); + } + + public async Task Remove() + { + await _stepperControllerClient.RemoveStepperControllerAsync(new TicRequest { TicId = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } + + public async Task Init() + { + var status = await GetDeviceOperationalStatus(); + if(status.OperationalState != OperationalState.Active) + return; + + var state = await _stepperControllerClient.GetStateAsync(new TicRequest { TicId = _deviceConfig.UniqueId }); + + MaxAcceleration = state.MaxAcceleration; + MaxDeceleration = state.MaxDeceleration; + CurrentLimit = state.CurrentLimit; + StartingSpeed = state.StartingSpeed; + CustomStepSize = state.CustomStepSize; + MaxSpeed = state.MaxSpeed; + StepMode = state.StepMode; + } + + [Reactive] + public partial bool DeviceActive { get; private set; } + public uint MaxAcceleration { get; private set; } + public uint MaxDeceleration { get; private set; } + public uint CurrentLimit { get; private set; } + public uint StartingSpeed { get; private set; } + public uint CustomStepSize { get; private set; } + public uint MaxSpeed { get; private set; } + public StepMode StepMode { get; private set; } +} diff --git a/UI/Backend/ViewModels/Devices/StepperController/StepperControllerViewModel.cs b/UI/Features/Devices/StepperController/StepperControllerViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Devices/StepperController/StepperControllerViewModel.cs rename to UI/Features/Devices/StepperController/StepperControllerViewModel.cs index 0143c027..6205c9e1 100644 --- a/UI/Backend/ViewModels/Devices/StepperController/StepperControllerViewModel.cs +++ b/UI/Features/Devices/StepperController/StepperControllerViewModel.cs @@ -1,134 +1,134 @@ -using ReactiveUI.SourceGenerators; -using TicStepperController.Messaging; -using UI.Pages.Shared.Devices.StepperController; - -namespace UI.Backend.ViewModels.StepperController; - -public partial class StepperControllerViewModel : DeviceUnitControlViewModel, IAsyncDisposable -{ - private readonly StepperControllerRpc.StepperControllerRpcClient _client; - private readonly CancellationTokenSource _stateUpdateTokenSource = new(); - private Task _stateListener = Task.CompletedTask; - - public StepperControllerViewModel(string id, string name, StepperControllerRpc.StepperControllerRpcClient client) : base(id, name) - { - _client = client; - StartStateUpdater(); - ViewType = typeof(StepperControllerWidgetView); - DefaultWidth = 18; - } - - #region Properties - - [Reactive] - public partial uint MaxAcceleration { get; private set; } - [Reactive] - public partial uint MaxDeceleration { get; private set; } - [Reactive] - public partial uint MaxSpeed { get; private set; } - [Reactive] - public partial uint StartingSpeed { get; private set; } - [Reactive] - public partial StepMode StepMode { get; private set; } - [Reactive] - public partial int CurrentPosition { get; private set; } - [Reactive] - public partial int TargetPosition { get; private set; } - [Reactive] - public partial MiscFlags? MiscFlags { get; private set; } - [Reactive] - public partial ErrorStatus? ErrorStatus { get; private set; } - [Reactive] - public partial ErrorsOccurred? ErrorsOccurred { get; private set; } - - #endregion - - #region Actions - - public Task ExitSafeStart() - { - return _client.ExitSafeStartAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task EnterSafeStart() - { - return _client.EnterSafeStartAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task HaltAndHold() - { - return _client.HaltAndHoldAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task NextStep() - { - return _client.NextStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task PreviousStep() - { - return _client.PreviousStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task HalfStep() - { - return _client.HalfStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; - } - - public Task SetTargetPosition(int position) - { - return _client.SetTargetPositionAsync(new PositionCommand { TicId = DeviceId, Position = position }).ResponseAsync; - } - - #endregion - - private void StartStateUpdater() - { - _stateListener = Task.Factory.StartNew(async _ => - { - Thread.CurrentThread.Name = "Stepper Controller State Listener View Model Thread"; - var cancelled = false; - lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } - while (!cancelled) - { - await UpdateState(); - await Task.Delay(TimeSpan.FromMilliseconds(500)); - lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } - } - }, - _stateUpdateTokenSource.Token, - TaskCreationOptions.LongRunning); - } - - private void StopStateUpdater() - { - _stateUpdateTokenSource.Cancel(); - } - - private async Task UpdateState() - { - var response = await _client.GetStateAsync(new TicRequest { TicId = DeviceId }); - if (!response.Valid) - return; - - MaxAcceleration = response.MaxAcceleration; - MaxDeceleration = response.MaxDeceleration; - MaxSpeed = response.MaxSpeed; - StartingSpeed = response.StartingSpeed; - StepMode = response.StepMode; - CurrentPosition = response.CurrentPosition; - TargetPosition = response.TargetPosition; - MiscFlags = response.MiscFlags; - ErrorStatus = response.ErrorStatus; - ErrorsOccurred = response.ErrorsOccurred; - } - - public async ValueTask DisposeAsync() - { - StopStateUpdater(); - await _stateListener; - _stateListener.Dispose(); - _stateUpdateTokenSource.Dispose(); - GC.SuppressFinalize(this); - } -} +using ReactiveUI.SourceGenerators; +using TicStepperController.Messaging; +using UI.Features.Devices.StepperController; + +namespace UI.Backend.ViewModels.StepperController; + +public partial class StepperControllerViewModel : DeviceUnitControlViewModel, IAsyncDisposable +{ + private readonly StepperControllerRpc.StepperControllerRpcClient _client; + private readonly CancellationTokenSource _stateUpdateTokenSource = new(); + private Task _stateListener = Task.CompletedTask; + + public StepperControllerViewModel(string id, string name, StepperControllerRpc.StepperControllerRpcClient client) : base(id, name) + { + _client = client; + StartStateUpdater(); + ViewType = typeof(StepperControllerWidgetView); + DefaultWidth = 18; + } + + #region Properties + + [Reactive] + public partial uint MaxAcceleration { get; private set; } + [Reactive] + public partial uint MaxDeceleration { get; private set; } + [Reactive] + public partial uint MaxSpeed { get; private set; } + [Reactive] + public partial uint StartingSpeed { get; private set; } + [Reactive] + public partial StepMode StepMode { get; private set; } + [Reactive] + public partial int CurrentPosition { get; private set; } + [Reactive] + public partial int TargetPosition { get; private set; } + [Reactive] + public partial MiscFlags? MiscFlags { get; private set; } + [Reactive] + public partial ErrorStatus? ErrorStatus { get; private set; } + [Reactive] + public partial ErrorsOccurred? ErrorsOccurred { get; private set; } + + #endregion + + #region Actions + + public Task ExitSafeStart() + { + return _client.ExitSafeStartAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task EnterSafeStart() + { + return _client.EnterSafeStartAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task HaltAndHold() + { + return _client.HaltAndHoldAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task NextStep() + { + return _client.NextStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task PreviousStep() + { + return _client.PreviousStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task HalfStep() + { + return _client.HalfStepAsync(new TicRequest { TicId = DeviceId }).ResponseAsync; + } + + public Task SetTargetPosition(int position) + { + return _client.SetTargetPositionAsync(new PositionCommand { TicId = DeviceId, Position = position }).ResponseAsync; + } + + #endregion + + private void StartStateUpdater() + { + _stateListener = Task.Factory.StartNew(async _ => + { + Thread.CurrentThread.Name = "Stepper Controller State Listener View Model Thread"; + var cancelled = false; + lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } + while (!cancelled) + { + await UpdateState(); + await Task.Delay(TimeSpan.FromMilliseconds(500)); + lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } + } + }, + _stateUpdateTokenSource.Token, + TaskCreationOptions.LongRunning); + } + + private void StopStateUpdater() + { + _stateUpdateTokenSource.Cancel(); + } + + private async Task UpdateState() + { + var response = await _client.GetStateAsync(new TicRequest { TicId = DeviceId }); + if (!response.Valid) + return; + + MaxAcceleration = response.MaxAcceleration; + MaxDeceleration = response.MaxDeceleration; + MaxSpeed = response.MaxSpeed; + StartingSpeed = response.StartingSpeed; + StepMode = response.StepMode; + CurrentPosition = response.CurrentPosition; + TargetPosition = response.TargetPosition; + MiscFlags = response.MiscFlags; + ErrorStatus = response.ErrorStatus; + ErrorsOccurred = response.ErrorsOccurred; + } + + public async ValueTask DisposeAsync() + { + StopStateUpdater(); + await _stateListener; + _stateListener.Dispose(); + _stateUpdateTokenSource.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor b/UI/Features/Devices/StepperController/StepperControllerWidgetView.razor similarity index 97% rename from UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor rename to UI/Features/Devices/StepperController/StepperControllerWidgetView.razor index 8e6e6c78..94141583 100644 --- a/UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerWidgetView.razor @@ -1,145 +1,145 @@ -@using UI.Backend.ViewModels.StepperController -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -@inject DialogService DialogService -@inject TooltipService TooltipService - -
-
-
@ViewModel!.DeviceName
-
- - - -
-
- -
- - - -
-
-
-
- - - -
- - - - - - -
-
-
-@code { - ElementReference _errorOpener; - - Task NextStep() - { - return ViewModel!.NextStep(); - } - - Task PreviousStep() - { - return ViewModel!.PreviousStep(); - } - - Task HalfStep() - { - return ViewModel!.HalfStep(); - } - - void ShowErrorToolTip() - { - TooltipService.Open(_errorOpener, ds => - @
- @if (ViewModel!.ErrorsOccurred is not null) - { -
- - - - - -
- } - - @if (ViewModel!.ErrorStatus is not null) - { - @if (ViewModel!.ErrorsOccurred is not null) - { -
- } -
- - - - - - - - - -
- } - - @if (ViewModel!.MiscFlags is not null) - { - @if (ViewModel!.ErrorsOccurred is not null || ViewModel!.ErrorStatus is not null) - { -
- } -
- - - - - -
- } -
, new TooltipOptions() - { - CloseTooltipOnDocumentClick = true, - Duration = 60000, - Style = "background: var(--rz-info-dark);"} - ); - } - - async Task GoToPosition() - { - var newPos = ViewModel!.CurrentPosition; - var test = await DialogService.OpenAsync("Position Mover", svc => - @
- New Position - -
- svc.Close(true)) Text="Move" /> - svc.Close(false)) Text="Cancel" /> -
-
- ); - - if (test is bool testBool && testBool && newPos != ViewModel.CurrentPosition) - { - await ViewModel!.SetTargetPosition(newPos); - } - } +@using UI.Backend.ViewModels.StepperController +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +@inject DialogService DialogService +@inject TooltipService TooltipService + +
+
+
@ViewModel!.DeviceName
+
+ + + +
+
+ +
+ + + +
+
+
+
+ + + +
+ + + + + + +
+
+
+@code { + ElementReference _errorOpener; + + Task NextStep() + { + return ViewModel!.NextStep(); + } + + Task PreviousStep() + { + return ViewModel!.PreviousStep(); + } + + Task HalfStep() + { + return ViewModel!.HalfStep(); + } + + void ShowErrorToolTip() + { + TooltipService.Open(_errorOpener, ds => + @
+ @if (ViewModel!.ErrorsOccurred is not null) + { +
+ + + + + +
+ } + + @if (ViewModel!.ErrorStatus is not null) + { + @if (ViewModel!.ErrorsOccurred is not null) + { +
+ } +
+ + + + + + + + + +
+ } + + @if (ViewModel!.MiscFlags is not null) + { + @if (ViewModel!.ErrorsOccurred is not null || ViewModel!.ErrorStatus is not null) + { +
+ } +
+ + + + + +
+ } +
, new TooltipOptions() + { + CloseTooltipOnDocumentClick = true, + Duration = 60000, + Style = "background: var(--rz-info-dark);"} + ); + } + + async Task GoToPosition() + { + var newPos = ViewModel!.CurrentPosition; + var test = await DialogService.OpenAsync("Position Mover", svc => + @
+ New Position + +
+ svc.Close(true)) Text="Move" /> + svc.Close(false)) Text="Cancel" /> +
+
+ ); + + if (test is bool testBool && testBool && newPos != ViewModel.CurrentPosition) + { + await ViewModel!.SetTargetPosition(newPos); + } + } } \ No newline at end of file diff --git a/UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor.css b/UI/Features/Devices/StepperController/StepperControllerWidgetView.razor.css similarity index 94% rename from UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor.css rename to UI/Features/Devices/StepperController/StepperControllerWidgetView.razor.css index e4e37b9e..1676f6c0 100644 --- a/UI/Pages/Shared/Devices/StepperController/StepperControllerWidgetView.razor.css +++ b/UI/Features/Devices/StepperController/StepperControllerWidgetView.razor.css @@ -1,8 +1,8 @@ -.temperature-display { - font-family: Helvetica, sans-serif; - font-size: 2.5em; -} - -.name-display { - font-size: 0.75em; +.temperature-display { + font-family: Helvetica, sans-serif; + font-size: 2.5em; +} + +.name-display { + font-size: 0.75em; } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor b/UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor rename to UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor index 1acc2427..94190d29 100644 --- a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor +++ b/UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor @@ -1,32 +1,32 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@using UI.Backend.ViewModels.Settings.Device.SyringePump; - -
- - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - - -
- - Simulated -
- -
- - - -
-@code { - - [Parameter] - public Action OnValidSubmit { get; set; } = delegate - { - }; -} +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Backend.ViewModels.Settings.Device.SyringePump; + +
+ + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + + +
+ + Simulated +
+ +
+ + + +
+@code { + + [Parameter] + public Action OnValidSubmit { get; set; } = delegate + { + }; +} diff --git a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor.css b/UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor.css similarity index 92% rename from UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor.css rename to UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor.css index 0cf08d37..b2c876a3 100644 --- a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpConfigEditView.razor.css +++ b/UI/Features/Devices/SyringePump/SyringePumpConfigEditView.razor.css @@ -1,29 +1,29 @@ -.pump-property-grid { - display: grid; - grid-template: - "name ." 1fr - "port ." 1fr - "simulated ." 1fr - "address ." 1fr - / auto 40px -} - -.pump-name { - grid-area: name; -} - -.pump-port { - grid-area: port; -} - -.pump-id { - grid-area: id; -} - -.pump-simulated { - grid-area: simulated; -} - -.pump-address { - grid-area: address; -} +.pump-property-grid { + display: grid; + grid-template: + "name ." 1fr + "port ." 1fr + "simulated ." 1fr + "address ." 1fr + / auto 40px +} + +.pump-name { + grid-area: name; +} + +.pump-port { + grid-area: port; +} + +.pump-id { + grid-area: id; +} + +.pump-simulated { + grid-area: simulated; +} + +.pump-address { + grid-area: address; +} diff --git a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpConfigEditViewModel.cs b/UI/Features/Devices/SyringePump/SyringePumpConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpConfigEditViewModel.cs rename to UI/Features/Devices/SyringePump/SyringePumpConfigEditViewModel.cs index ea62c57f..83158a1a 100644 --- a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpConfigEditViewModel.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpConfigEditViewModel.cs @@ -1,82 +1,82 @@ -using System.ComponentModel.DataAnnotations; -using Ares.Services.Device; -using Ares.SyringePump.Ne1000.Messaging; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Settings.Device.SyringePump; - -public partial class SyringePumpConfigEditViewModel : ReactiveObject -{ - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; - private readonly SyringePumpConfig _syringePumpConfig; - private string? _name; - - public SyringePumpConfigEditViewModel(SyringePumpRpc.SyringePumpRpcClient syringePumpClient, - AresDevices.AresDevicesClient devicesClient, - SyringePumpConfig syringePumpConfig - ) - { - _syringePumpClient = syringePumpClient; - _devicesClient = devicesClient; - _syringePumpConfig = syringePumpConfig; - _ = UpdateAvailableSerialPorts(); - _name = _syringePumpConfig.Name; - Address = _syringePumpConfig.Address; - Port = _syringePumpConfig.PortName; - Simulated = _syringePumpConfig.Simulated; - } - - public SyringePumpConfigEditViewModel(SyringePumpRpc.SyringePumpRpcClient syringePumpClient, AresDevices.AresDevicesClient devicesClient) - { - _syringePumpClient = syringePumpClient; - _devicesClient = devicesClient; - _syringePumpConfig = new SyringePumpConfig(); - _ = UpdateAvailableSerialPorts(); - NewConfig = true; - } - - [Required] - public string? Name - { - get => _name; - - set - { - if (!NewConfig) - return; - - _name = value; - } - } - - [Required] - public string? Port { get; set; } - - // TODO this is here to prevent changing name as the name of the device is used for lookup and stuff - // but maybe we should use some kind of GUID instead to make this a bit more robust and allow name changes. - public bool NewConfig { get; } - - [Required] - public uint? Address { get; set; } - - public bool Simulated { get; set; } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public bool Modified => _syringePumpConfig.Name != Name || _syringePumpConfig.PortName != Port || _syringePumpConfig.Simulated != Simulated || _syringePumpConfig.Address != Address; - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - public SyringePumpConfig Save() - => Modified ? new SyringePumpConfig { Address = Address ?? 0, Name = Name, PortName = Port, Simulated = Simulated } : _syringePumpConfig; -} +using System.ComponentModel.DataAnnotations; +using Ares.Services.Device; +using Ares.SyringePump.Ne1000.Messaging; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace UI.Backend.ViewModels.Settings.Device.SyringePump; + +public partial class SyringePumpConfigEditViewModel : ReactiveObject +{ + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; + private readonly SyringePumpConfig _syringePumpConfig; + private string? _name; + + public SyringePumpConfigEditViewModel(SyringePumpRpc.SyringePumpRpcClient syringePumpClient, + AresDevices.AresDevicesClient devicesClient, + SyringePumpConfig syringePumpConfig + ) + { + _syringePumpClient = syringePumpClient; + _devicesClient = devicesClient; + _syringePumpConfig = syringePumpConfig; + _ = UpdateAvailableSerialPorts(); + _name = _syringePumpConfig.Name; + Address = _syringePumpConfig.Address; + Port = _syringePumpConfig.PortName; + Simulated = _syringePumpConfig.Simulated; + } + + public SyringePumpConfigEditViewModel(SyringePumpRpc.SyringePumpRpcClient syringePumpClient, AresDevices.AresDevicesClient devicesClient) + { + _syringePumpClient = syringePumpClient; + _devicesClient = devicesClient; + _syringePumpConfig = new SyringePumpConfig(); + _ = UpdateAvailableSerialPorts(); + NewConfig = true; + } + + [Required] + public string? Name + { + get => _name; + + set + { + if (!NewConfig) + return; + + _name = value; + } + } + + [Required] + public string? Port { get; set; } + + // TODO this is here to prevent changing name as the name of the device is used for lookup and stuff + // but maybe we should use some kind of GUID instead to make this a bit more robust and allow name changes. + public bool NewConfig { get; } + + [Required] + public uint? Address { get; set; } + + public bool Simulated { get; set; } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public bool Modified => _syringePumpConfig.Name != Name || _syringePumpConfig.PortName != Port || _syringePumpConfig.Simulated != Simulated || _syringePumpConfig.Address != Address; + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + public SyringePumpConfig Save() + => Modified ? new SyringePumpConfig { Address = Address ?? 0, Name = Name, PortName = Port, Simulated = Simulated } : _syringePumpConfig; +} diff --git a/UI/Backend/Factories/SyringePumpDeviceControlViewModelFactory.cs b/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs similarity index 95% rename from UI/Backend/Factories/SyringePumpDeviceControlViewModelFactory.cs rename to UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs index 1b2d43d3..6736fe9d 100644 --- a/UI/Backend/Factories/SyringePumpDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs @@ -3,11 +3,12 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using System.Reactive.Linq; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.SyringePump; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.SyringePump; public class SyringePumpDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsList.razor b/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor similarity index 98% rename from UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsList.razor rename to UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor index 5b6c8403..417fcb41 100644 --- a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsList.razor +++ b/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/syringe-pump" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.SyringePump +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsListViewModel.cs b/UI/Features/Devices/SyringePump/SyringePumpSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsListViewModel.cs rename to UI/Features/Devices/SyringePump/SyringePumpSettingsListViewModel.cs index 770e7c89..10dccffd 100644 --- a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsListViewModel.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpSettingsListViewModel.cs @@ -1,57 +1,58 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using Ares.SyringePump.Ne1000.Messaging; -using CommunityToolkit.Mvvm.Messaging; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using SyringePumpNE1000; - -namespace UI.Backend.ViewModels.Settings.Device.SyringePump; - -public partial class SyringePumpSettingsListViewModel : ReactiveObject -{ - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; - private readonly IMessenger _messenger; - - public SyringePumpSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, SyringePumpRpc.SyringePumpRpcClient syringePumpClient, IMessenger messenger) - { - _devicesClient = devicesClient; - _syringePumpClient = syringePumpClient; - _messenger = messenger; - _ = UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new SyringePumpSettingsViewModel(config, _syringePumpClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public SyringePumpConfigEditViewModel GetNewConfigEditViewModel() - => new(_syringePumpClient, _devicesClient); - - private async Task UpdateConfigs() - { - SettingsViewModels = null; - var configs = await _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(ISyringePump).FullName }); - - UpdateViewModels(configs.Configs); - } - - public async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(SyringePumpConfig config) - { - await _syringePumpClient.AddSyringePumpAsync(config); - await UpdateConfigs(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using Ares.SyringePump.Ne1000.Messaging; +using CommunityToolkit.Mvvm.Messaging; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using SyringePumpNE1000; +using UI.Features.Devices.SyringePump; + +namespace UI.Backend.ViewModels.Settings.Device.SyringePump; + +public partial class SyringePumpSettingsListViewModel : ReactiveObject +{ + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; + private readonly IMessenger _messenger; + + public SyringePumpSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, SyringePumpRpc.SyringePumpRpcClient syringePumpClient, IMessenger messenger) + { + _devicesClient = devicesClient; + _syringePumpClient = syringePumpClient; + _messenger = messenger; + _ = UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new SyringePumpSettingsViewModel(config, _syringePumpClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public SyringePumpConfigEditViewModel GetNewConfigEditViewModel() + => new(_syringePumpClient, _devicesClient); + + private async Task UpdateConfigs() + { + SettingsViewModels = null; + var configs = await _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(ISyringePump).FullName }); + + UpdateViewModels(configs.Configs); + } + + public async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(SyringePumpConfig config) + { + await _syringePumpClient.AddSyringePumpAsync(config); + await UpdateConfigs(); + } +} diff --git a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsView.razor b/UI/Features/Devices/SyringePump/SyringePumpSettingsView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsView.razor rename to UI/Features/Devices/SyringePump/SyringePumpSettingsView.razor index 2a43e711..baa58b42 100644 --- a/UI/Pages/Shared/Settings/Device/SyringePump/SyringePumpSettingsView.razor +++ b/UI/Features/Devices/SyringePump/SyringePumpSettingsView.razor @@ -1,42 +1,43 @@ -@using UI.Backend.ViewModels.Settings.Device.SyringePump -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - -
- @if (ViewModel!.SyringePumpConfig.Simulated) - { - Simulated -- - } - @ViewModel!.SyringePumpConfig.Name - @ViewModel!.SyringePumpConfig.PortName - @($" Address: {ViewModel!.SyringePumpConfig.Address}") -
-
- -@code { - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit Syringe Pump", ds => - @); - - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - } - - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.SyringePumpConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } +@using UI.Backend.ViewModels.Settings.Device.SyringePump +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + +
+ @if (ViewModel!.SyringePumpConfig.Simulated) + { + Simulated -- + } + @ViewModel!.SyringePumpConfig.Name + @ViewModel!.SyringePumpConfig.PortName + @($" Address: {ViewModel!.SyringePumpConfig.Address}") +
+
+ +@code { + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit Syringe Pump", ds => + @); + + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + } + + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.SyringePumpConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsViewModel.cs b/UI/Features/Devices/SyringePump/SyringePumpSettingsViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsViewModel.cs rename to UI/Features/Devices/SyringePump/SyringePumpSettingsViewModel.cs index 0fb52f57..0e82db33 100644 --- a/UI/Backend/ViewModels/Settings/Device/SyringePump/SyringePumpSettingsViewModel.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpSettingsViewModel.cs @@ -1,72 +1,73 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using Ares.SyringePump.Ne1000.Messaging; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using ReactiveUI; -using UI.Backend.Devices; - -namespace UI.Backend.ViewModels.Settings.Device.SyringePump; - -public class SyringePumpSettingsViewModel : ReactiveObject -{ - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; - private readonly IMessenger _messenger; - - public SyringePumpSettingsViewModel(DeviceConfig deviceConfig, - SyringePumpRpc.SyringePumpRpcClient syringePumpClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - SyringePumpConfig = deviceConfig.ConfigData.Unpack(); - _syringePumpClient = syringePumpClient; - _devicesClient = devicesClient; - _messenger = messenger; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new SyringePumpConfigEditViewModel(_syringePumpClient, _devicesClient, SyringePumpConfig); - } - - public SyringePumpConfig SyringePumpConfig { get; } - - public Func OnRemoveCallback { get; } - - public SyringePumpConfigEditViewModel EditViewModel { get; } - - public Task GetDeviceOperationalStatus() - { - try - { - return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - } - catch(RpcException) - { - return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered syringe pump with a name {SyringePumpConfig.Name}" }); - } - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - - public async Task Save() - { - var syringePumpConfig = EditViewModel.Save(); - var updateRequest = new SyringePumpUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = syringePumpConfig - }; - - await _syringePumpClient.UpdateSyringePumpAsync(updateRequest); - } - - public async Task Remove() - { - await _syringePumpClient.RemoveSyringePumpAsync(new SyringePumpRemoveRequest { DeviceId = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using Ares.SyringePump.Ne1000.Messaging; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using ReactiveUI; +using UI.Backend.ViewModels.Settings.Device.SyringePump; +using UI.Features.Devices.Shared; + +namespace UI.Features.Devices.SyringePump; + +public class SyringePumpSettingsViewModel : ReactiveObject +{ + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; + private readonly IMessenger _messenger; + + public SyringePumpSettingsViewModel(DeviceConfig deviceConfig, + SyringePumpRpc.SyringePumpRpcClient syringePumpClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + SyringePumpConfig = deviceConfig.ConfigData.Unpack(); + _syringePumpClient = syringePumpClient; + _devicesClient = devicesClient; + _messenger = messenger; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new SyringePumpConfigEditViewModel(_syringePumpClient, _devicesClient, SyringePumpConfig); + } + + public SyringePumpConfig SyringePumpConfig { get; } + + public Func OnRemoveCallback { get; } + + public SyringePumpConfigEditViewModel EditViewModel { get; } + + public Task GetDeviceOperationalStatus() + { + try + { + return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + } + catch(RpcException) + { + return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered syringe pump with a name {SyringePumpConfig.Name}" }); + } + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + + public async Task Save() + { + var syringePumpConfig = EditViewModel.Save(); + var updateRequest = new SyringePumpUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = syringePumpConfig + }; + + await _syringePumpClient.UpdateSyringePumpAsync(updateRequest); + } + + public async Task Remove() + { + await _syringePumpClient.RemoveSyringePumpAsync(new SyringePumpRemoveRequest { DeviceId = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } +} diff --git a/UI/Pages/Shared/Devices/SyringePump/SyringePumpUnitControl.razor b/UI/Features/Devices/SyringePump/SyringePumpUnitControl.razor similarity index 97% rename from UI/Pages/Shared/Devices/SyringePump/SyringePumpUnitControl.razor rename to UI/Features/Devices/SyringePump/SyringePumpUnitControl.razor index d146a986..270389df 100644 --- a/UI/Pages/Shared/Devices/SyringePump/SyringePumpUnitControl.razor +++ b/UI/Features/Devices/SyringePump/SyringePumpUnitControl.razor @@ -1,96 +1,96 @@ -@using Ares.SyringePump.Ne1000.Messaging -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
-

@ViewModel!.DeviceName

-
-

@ViewModel.Status

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Address@ViewModel!.CurrentState.Address -
- - -
-
Diameter@ViewModel.CurrentState.DiameterMm -
- - -
-
Phase@ViewModel.CurrentState.Phase?.Number -
- - -
-
Function@ViewModel.CurrentState.Phase?.Function -
- - -
-
Rate (@($"{ViewModel.CurrentState.RateUnits:G}"))@ViewModel.CurrentState.Phase?.Rate -
- - -
-
Direction@ViewModel.ActiveDirection -
- - -
-
-
-
-
-

Infused: @ViewModel.CurrentState.DispensedVolume

-
-
-

Withdrawn: @ViewModel.CurrentState.WithdrawnVolume

-
-
- - -
-
-
- - -
-
- - -@code { - -} +@using Ares.SyringePump.Ne1000.Messaging +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+

@ViewModel!.DeviceName

+
+

@ViewModel.Status

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Address@ViewModel!.CurrentState.Address +
+ + +
+
Diameter@ViewModel.CurrentState.DiameterMm +
+ + +
+
Phase@ViewModel.CurrentState.Phase?.Number +
+ + +
+
Function@ViewModel.CurrentState.Phase?.Function +
+ + +
+
Rate (@($"{ViewModel.CurrentState.RateUnits:G}"))@ViewModel.CurrentState.Phase?.Rate +
+ + +
+
Direction@ViewModel.ActiveDirection +
+ + +
+
+
+
+
+

Infused: @ViewModel.CurrentState.DispensedVolume

+
+
+

Withdrawn: @ViewModel.CurrentState.WithdrawnVolume

+
+
+ + +
+
+
+ + +
+
+ + +@code { + +} diff --git a/UI/Backend/ViewModels/Devices/SyringePump/SyringePumpUnitControlViewModel.cs b/UI/Features/Devices/SyringePump/SyringePumpUnitControlViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Devices/SyringePump/SyringePumpUnitControlViewModel.cs rename to UI/Features/Devices/SyringePump/SyringePumpUnitControlViewModel.cs index 3b6ad455..8ae8c54f 100644 --- a/UI/Backend/ViewModels/Devices/SyringePump/SyringePumpUnitControlViewModel.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpUnitControlViewModel.cs @@ -1,155 +1,155 @@ -using Ares.SyringePump.Ne1000.Messaging; -using DynamicData.Binding; -using ReactiveUI.SourceGenerators; -using UI.Pages.Shared.Devices.SyringePump; - -namespace UI.Backend.ViewModels.SyringePump; - -public partial class SyringePumpUnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable -{ - private SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; - private DeviceRequest _deviceRequest; - private CancellationTokenSource _stateCts = new(); - private Task _stateListener = Task.CompletedTask; - public SyringePumpUnitControlViewModel(string id, string syringePumpName, SyringePumpRpc.SyringePumpRpcClient syringePumpClient) : base(id, syringePumpName) - { - _syringePumpClient = syringePumpClient; - _deviceRequest = new DeviceRequest { DeviceId = DeviceId }; - CurrentState = new StateResponse(); - Status = "Not Connected"; - Initialize(); - this.WhenValueChanged(vm => vm.CurrentState).Subscribe(_ => UpdateStatus()); - ViewType = typeof(SyringePumpUnitControl); - DefaultWidth = 35; - } - - private void UpdateStatus() - { - Status = CurrentState.Status switch - { - StatusPrompt.UndefinedStatusPrompt => "Error", - StatusPrompt.PromptI => $"Infusing {CurrentState.Phase.Rate:F} mL/minute", - StatusPrompt.PromptW => $"Withdrawing {CurrentState.Phase.Rate:F} mL/minute", - StatusPrompt.PromptS => "Stopped", - StatusPrompt.PromptP => "Paused", - StatusPrompt.PromptT => "Timed Pause Phase", - StatusPrompt.PromptU => "User Trigger", - StatusPrompt.PromptX => "Purging", - _ => throw new ArgumentOutOfRangeException() - }; - } - - private void Initialize() - { - CurrentState = _syringePumpClient.GetCurrentState(_deviceRequest); - _stateListener = Task.Run(async () => - { - Thread.CurrentThread.Name = "Syringe Pump UI State Listener Thread"; - while(!_stateCts.IsCancellationRequested) - { - CurrentState = await _syringePumpClient.GetCurrentStateAsync(_deviceRequest); - UpdateValues(); - await Task.Delay(TimeSpan.FromSeconds(2)); - } - }, _stateCts.Token); - } - - public async Task SetDiameter() - { - var request = new SetDiameterMmRequest { DeviceRequest = _deviceRequest, DiameterMm = TargetDiameterMm }; - await _syringePumpClient.SetDiameterAsync(request); - } - - public async Task SetAddress() - { - var request = new SetAddressRequest() { DeviceRequest = _deviceRequest, Address = TargetAddress }; - await _syringePumpClient.SetAddressAsync(request); - } - - public async Task SetPhaseFunctionRate() - { - var request = new SetProgramFunctionRateMmpmRequest - { DeviceRequest = _deviceRequest, RateMmpm = TargetRateMmpm }; - await _syringePumpClient.SetProgramFunctionRateAsync(request); - } - - public async Task SetPhaseFunctionDirection() - { - var request = new SetProgramFunctionPumpingDirectionRequest { DeviceRequest = _deviceRequest, Direction = TargetDirection }; - await _syringePumpClient.SetProgramFunctionPumpingDirectionAsync(request); - } - - public async Task SetPhase() - { - var request = new SetPhaseNumberRequest { DeviceRequest = _deviceRequest, Phase = TargetPhase }; - await _syringePumpClient.SetPhaseAsync(request); - } - - public async Task SetPhaseFunction() - { - var request = new SetPhaseFunctionRequest { DeviceRequest = _deviceRequest, Function = TargetFunction }; - await _syringePumpClient.SetPhaseFunctionAsync(request); - } - - public async Task ClearVolumeDispensed() - { - var request = new ClearVolumeDispensedRequest { DeviceRequest = _deviceRequest, Direction = CurrentState.Phase.Direction }; - await _syringePumpClient.ClearVolumeDispensedAsync(request); - } - - public async Task Purge() - { - await _syringePumpClient.PurgePumpAsync(_deviceRequest); - } - - public async Task Start() - { - await _syringePumpClient.StartPumpingProgramAsync(_deviceRequest); - } - - public async Task Stop() - { - await _syringePumpClient.StopPumpingProgramAsync(_deviceRequest); - } - - public async ValueTask DisposeAsync() - { - _stateCts.Cancel(); - await _stateListener; - _stateCts.Dispose(); - - GC.SuppressFinalize(this); - } - - private void UpdateValues() - { - if(CurrentState.Phase is null) - return; - - TargetAddress = CurrentState.Address; - ActiveDirection = CurrentState.Phase.Direction; - } - - [Reactive] - public partial int TargetAddress { get; set; } - [Reactive] - public partial float TargetDiameterMm { get; set; } - [Reactive] - public partial float TargetVolumeMl { get; set; } - [Reactive] - public partial float TargetRateMmpm { get; set; } - [Reactive] - public partial int TargetPhase { get; set; } - [Reactive] - public partial Commands TargetFunction { get; set; } - [Reactive] - public partial Direction TargetDirection { get; set; } - [Reactive] - public partial Direction ActiveDirection { get; set; } - [Reactive] - public partial StateResponse CurrentState { get; private set; } - [Reactive] - public partial string Status { get; private set; } - [Reactive] - public partial VolumeUnit VolumeUnit { get; set; } -} +using Ares.SyringePump.Ne1000.Messaging; +using DynamicData.Binding; +using ReactiveUI.SourceGenerators; +using UI.Features.Devices.SyringePump; + +namespace UI.Backend.ViewModels.SyringePump; + +public partial class SyringePumpUnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable +{ + private SyringePumpRpc.SyringePumpRpcClient _syringePumpClient; + private DeviceRequest _deviceRequest; + private CancellationTokenSource _stateCts = new(); + private Task _stateListener = Task.CompletedTask; + public SyringePumpUnitControlViewModel(string id, string syringePumpName, SyringePumpRpc.SyringePumpRpcClient syringePumpClient) : base(id, syringePumpName) + { + _syringePumpClient = syringePumpClient; + _deviceRequest = new DeviceRequest { DeviceId = DeviceId }; + CurrentState = new StateResponse(); + Status = "Not Connected"; + Initialize(); + this.WhenValueChanged(vm => vm.CurrentState).Subscribe(_ => UpdateStatus()); + ViewType = typeof(SyringePumpUnitControl); + DefaultWidth = 35; + } + + private void UpdateStatus() + { + Status = CurrentState.Status switch + { + StatusPrompt.UndefinedStatusPrompt => "Error", + StatusPrompt.PromptI => $"Infusing {CurrentState.Phase.Rate:F} mL/minute", + StatusPrompt.PromptW => $"Withdrawing {CurrentState.Phase.Rate:F} mL/minute", + StatusPrompt.PromptS => "Stopped", + StatusPrompt.PromptP => "Paused", + StatusPrompt.PromptT => "Timed Pause Phase", + StatusPrompt.PromptU => "User Trigger", + StatusPrompt.PromptX => "Purging", + _ => throw new ArgumentOutOfRangeException() + }; + } + + private void Initialize() + { + CurrentState = _syringePumpClient.GetCurrentState(_deviceRequest); + _stateListener = Task.Run(async () => + { + Thread.CurrentThread.Name = "Syringe Pump UI State Listener Thread"; + while(!_stateCts.IsCancellationRequested) + { + CurrentState = await _syringePumpClient.GetCurrentStateAsync(_deviceRequest); + UpdateValues(); + await Task.Delay(TimeSpan.FromSeconds(2)); + } + }, _stateCts.Token); + } + + public async Task SetDiameter() + { + var request = new SetDiameterMmRequest { DeviceRequest = _deviceRequest, DiameterMm = TargetDiameterMm }; + await _syringePumpClient.SetDiameterAsync(request); + } + + public async Task SetAddress() + { + var request = new SetAddressRequest() { DeviceRequest = _deviceRequest, Address = TargetAddress }; + await _syringePumpClient.SetAddressAsync(request); + } + + public async Task SetPhaseFunctionRate() + { + var request = new SetProgramFunctionRateMmpmRequest + { DeviceRequest = _deviceRequest, RateMmpm = TargetRateMmpm }; + await _syringePumpClient.SetProgramFunctionRateAsync(request); + } + + public async Task SetPhaseFunctionDirection() + { + var request = new SetProgramFunctionPumpingDirectionRequest { DeviceRequest = _deviceRequest, Direction = TargetDirection }; + await _syringePumpClient.SetProgramFunctionPumpingDirectionAsync(request); + } + + public async Task SetPhase() + { + var request = new SetPhaseNumberRequest { DeviceRequest = _deviceRequest, Phase = TargetPhase }; + await _syringePumpClient.SetPhaseAsync(request); + } + + public async Task SetPhaseFunction() + { + var request = new SetPhaseFunctionRequest { DeviceRequest = _deviceRequest, Function = TargetFunction }; + await _syringePumpClient.SetPhaseFunctionAsync(request); + } + + public async Task ClearVolumeDispensed() + { + var request = new ClearVolumeDispensedRequest { DeviceRequest = _deviceRequest, Direction = CurrentState.Phase.Direction }; + await _syringePumpClient.ClearVolumeDispensedAsync(request); + } + + public async Task Purge() + { + await _syringePumpClient.PurgePumpAsync(_deviceRequest); + } + + public async Task Start() + { + await _syringePumpClient.StartPumpingProgramAsync(_deviceRequest); + } + + public async Task Stop() + { + await _syringePumpClient.StopPumpingProgramAsync(_deviceRequest); + } + + public async ValueTask DisposeAsync() + { + _stateCts.Cancel(); + await _stateListener; + _stateCts.Dispose(); + + GC.SuppressFinalize(this); + } + + private void UpdateValues() + { + if(CurrentState.Phase is null) + return; + + TargetAddress = CurrentState.Address; + ActiveDirection = CurrentState.Phase.Direction; + } + + [Reactive] + public partial int TargetAddress { get; set; } + [Reactive] + public partial float TargetDiameterMm { get; set; } + [Reactive] + public partial float TargetVolumeMl { get; set; } + [Reactive] + public partial float TargetRateMmpm { get; set; } + [Reactive] + public partial int TargetPhase { get; set; } + [Reactive] + public partial Commands TargetFunction { get; set; } + [Reactive] + public partial Direction TargetDirection { get; set; } + [Reactive] + public partial Direction ActiveDirection { get; set; } + [Reactive] + public partial StateResponse CurrentState { get; private set; } + [Reactive] + public partial string Status { get; private set; } + [Reactive] + public partial VolumeUnit VolumeUnit { get; set; } +} diff --git a/UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsList.razor b/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsList.razor rename to UI/Features/Devices/Tc0304/DataloggerSettingsList.razor index eb860cc3..11ded9e9 100644 --- a/UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsList.razor +++ b/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/tc0304" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Tc0304 +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsView.razor b/UI/Features/Devices/Tc0304/DataloggerSettingsView.razor similarity index 88% rename from UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsView.razor rename to UI/Features/Devices/Tc0304/DataloggerSettingsView.razor index 66699f1d..10f0752f 100644 --- a/UI/Pages/Shared/Settings/Device/Tc0304/DataloggerSettingsView.razor +++ b/UI/Features/Devices/Tc0304/DataloggerSettingsView.razor @@ -1,42 +1,43 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - -
- @if (ViewModel!.DataloggerConfig.Simulated) - { - Simulated -- - } - @ViewModel!.DataloggerConfig.Name - @ViewModel!.DataloggerConfig.PortName -
-
- -@code { - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit Datalogger", ds => - @); - - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - } - -} -@code { - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.DataloggerConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + +
+ @if (ViewModel!.DataloggerConfig.Simulated) + { + Simulated -- + } + @ViewModel!.DataloggerConfig.Name + @ViewModel!.DataloggerConfig.PortName +
+
+ +@code { + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit Datalogger", ds => + @); + + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + } + +} +@code { + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.DataloggerConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } } \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Device/Tc0304/Tc0304ConfigEditView.razor b/UI/Features/Devices/Tc0304/Tc0304ConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/Tc0304/Tc0304ConfigEditView.razor rename to UI/Features/Devices/Tc0304/Tc0304ConfigEditView.razor index 402b2990..5fb8b83b 100644 --- a/UI/Pages/Shared/Settings/Device/Tc0304/Tc0304ConfigEditView.razor +++ b/UI/Features/Devices/Tc0304/Tc0304ConfigEditView.razor @@ -1,38 +1,38 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
- - - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - -
- - Simulated -
-
- - - -
-@code { - - [Parameter] - public Action OnValidSubmit { get; set; } = delegate { - }; - - private void PortChanged(ChangeEventArgs obj) - { - if (obj.Value is not string stringValue) - throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); - - ViewModel!.Port = stringValue; - } +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+ + + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + +
+ + Simulated +
+
+ + + +
+@code { + + [Parameter] + public Action OnValidSubmit { get; set; } = delegate { + }; + + private void PortChanged(ChangeEventArgs obj) + { + if (obj.Value is not string stringValue) + throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); + + ViewModel!.Port = stringValue; + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304ConfigEditViewModel.cs b/UI/Features/Devices/Tc0304/Tc0304ConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304ConfigEditViewModel.cs rename to UI/Features/Devices/Tc0304/Tc0304ConfigEditViewModel.cs index a49308ef..590f2e5c 100644 --- a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304ConfigEditViewModel.cs +++ b/UI/Features/Devices/Tc0304/Tc0304ConfigEditViewModel.cs @@ -1,76 +1,76 @@ -using System.ComponentModel.DataAnnotations; -using Ares.Services.Device; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using Tc0304.Config; -using Tc0304.Services; - -namespace UI.Backend.ViewModels.Settings.Device.Tc0304; - -public partial class Tc0304ConfigEditViewModel : ReactiveObject -{ - private readonly TC0304Rpc.TC0304RpcClient _client; - private readonly Tc0304Config _config; - private readonly AresDevices.AresDevicesClient _devicesClient; - private string? _name; - - public Tc0304ConfigEditViewModel(TC0304Rpc.TC0304RpcClient client, AresDevices.AresDevicesClient devicesClient) - { - _client = client; - _devicesClient = devicesClient; - _config = new Tc0304Config(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - } - - public Tc0304ConfigEditViewModel(TC0304Rpc.TC0304RpcClient client, AresDevices.AresDevicesClient devicesClient, Tc0304Config config) - { - _client = client; - _devicesClient = devicesClient; - _config = config; - _ = UpdateAvailableSerialPorts(); - _name = config.Name; - Port = config.PortName; - Simulated = config.Simulated; - } - - [Required] - public string? Name - { - get => _name; - - set - { - if (!NewConfig) - return; - - _name = value; - } - } - - [Required] - public string? Port { get; set; } - - // TODO this is here to prevent changing name as the name of the device is used for lookup and stuff - // but maybe we should use some kind of GUID instead to make this a bit more robust and allow name changes. - public bool NewConfig { get; } - - public bool Simulated { get; set; } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - public Tc0304Config Save() - => Modified ? new Tc0304Config { Name = Name, PortName = Port, Simulated = Simulated } : _config; -} +using System.ComponentModel.DataAnnotations; +using Ares.Services.Device; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using Tc0304.Config; +using Tc0304.Services; + +namespace UI.Backend.ViewModels.Settings.Device.Tc0304; + +public partial class Tc0304ConfigEditViewModel : ReactiveObject +{ + private readonly TC0304Rpc.TC0304RpcClient _client; + private readonly Tc0304Config _config; + private readonly AresDevices.AresDevicesClient _devicesClient; + private string? _name; + + public Tc0304ConfigEditViewModel(TC0304Rpc.TC0304RpcClient client, AresDevices.AresDevicesClient devicesClient) + { + _client = client; + _devicesClient = devicesClient; + _config = new Tc0304Config(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + } + + public Tc0304ConfigEditViewModel(TC0304Rpc.TC0304RpcClient client, AresDevices.AresDevicesClient devicesClient, Tc0304Config config) + { + _client = client; + _devicesClient = devicesClient; + _config = config; + _ = UpdateAvailableSerialPorts(); + _name = config.Name; + Port = config.PortName; + Simulated = config.Simulated; + } + + [Required] + public string? Name + { + get => _name; + + set + { + if (!NewConfig) + return; + + _name = value; + } + } + + [Required] + public string? Port { get; set; } + + // TODO this is here to prevent changing name as the name of the device is used for lookup and stuff + // but maybe we should use some kind of GUID instead to make this a bit more robust and allow name changes. + public bool NewConfig { get; } + + public bool Simulated { get; set; } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + public Tc0304Config Save() + => Modified ? new Tc0304Config { Name = Name, PortName = Port, Simulated = Simulated } : _config; +} diff --git a/UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor b/UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor similarity index 97% rename from UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor rename to UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor index ebe54d11..9f46e132 100644 --- a/UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor +++ b/UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor @@ -1,21 +1,21 @@ -@using UI.Backend.ViewModels.Tc0304 -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
-
-
@ViewModel!.Probe1Name
-
@(ViewModel!.T1Temp?.ToString() ?? "--")
-
@ViewModel!.Probe3Name
-
@(ViewModel!.T3Temp?.ToString() ?? "--")
-
-
-
@ViewModel!.Probe2Name
-
@(ViewModel!.T2Temp?.ToString() ?? "--")
-
@ViewModel!.Probe4Name
-
@(ViewModel!.T4Temp?.ToString() ?? "--")
-
-
- -@code { - +@using UI.Backend.ViewModels.Tc0304 +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+
+
@ViewModel!.Probe1Name
+
@(ViewModel!.T1Temp?.ToString() ?? "--")
+
@ViewModel!.Probe3Name
+
@(ViewModel!.T3Temp?.ToString() ?? "--")
+
+
+
@ViewModel!.Probe2Name
+
@(ViewModel!.T2Temp?.ToString() ?? "--")
+
@ViewModel!.Probe4Name
+
@(ViewModel!.T4Temp?.ToString() ?? "--")
+
+
+ +@code { + } \ No newline at end of file diff --git a/UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor.css b/UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor.css similarity index 94% rename from UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor.css rename to UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor.css index e4e37b9e..1676f6c0 100644 --- a/UI/Pages/Shared/Devices/DataLogger/Tc0304ControlWidgetView.razor.css +++ b/UI/Features/Devices/Tc0304/Tc0304ControlWidgetView.razor.css @@ -1,8 +1,8 @@ -.temperature-display { - font-family: Helvetica, sans-serif; - font-size: 2.5em; -} - -.name-display { - font-size: 0.75em; +.temperature-display { + font-family: Helvetica, sans-serif; + font-size: 2.5em; +} + +.name-display { + font-size: 0.75em; } \ No newline at end of file diff --git a/UI/Backend/Factories/Tc0304DeviceControlViewModelFactory.cs b/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs similarity index 94% rename from UI/Backend/Factories/Tc0304DeviceControlViewModelFactory.cs rename to UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs index b2f0f27f..a636c935 100644 --- a/UI/Backend/Factories/Tc0304DeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs @@ -2,11 +2,12 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using Tc0304.Services; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Tc0304; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.Tc0304; public class Tc0304DeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsListViewModel.cs b/UI/Features/Devices/Tc0304/Tc0304SettingsListViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsListViewModel.cs rename to UI/Features/Devices/Tc0304/Tc0304SettingsListViewModel.cs index 5c4667df..5c185294 100644 --- a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsListViewModel.cs +++ b/UI/Features/Devices/Tc0304/Tc0304SettingsListViewModel.cs @@ -1,57 +1,58 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using TC0304; -using Tc0304.Config; -using Tc0304.Services; -using CommunityToolkit.Mvvm.Messaging; - -namespace UI.Backend.ViewModels.Settings.Device.Tc0304; - -public partial class Tc0304SettingsListViewModel : ReactiveObject -{ - private readonly TC0304Rpc.TC0304RpcClient _dataloggerClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public Tc0304SettingsListViewModel(AresDevices.AresDevicesClient devicesClient, TC0304Rpc.TC0304RpcClient dataloggerClient, IMessenger messenger) - { - _devicesClient = devicesClient; - _dataloggerClient = dataloggerClient; - _messenger = messenger; - UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new Tc0304SettingsViewModel(config, _dataloggerClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public Tc0304ConfigEditViewModel GetNewConfigEditViewModel() - => new(_dataloggerClient, _devicesClient); - - private Task UpdateConfigs() - { - SettingsViewModels = null; - return _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IDataloggerThermometer).FullName }) - .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); - } - - public async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(Tc0304Config config) - { - await _dataloggerClient.AddTc0304Async(config); - await UpdateConfigs(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using TC0304; +using Tc0304.Config; +using Tc0304.Services; +using CommunityToolkit.Mvvm.Messaging; +using UI.Features.Devices.Tc0304; + +namespace UI.Backend.ViewModels.Settings.Device.Tc0304; + +public partial class Tc0304SettingsListViewModel : ReactiveObject +{ + private readonly TC0304Rpc.TC0304RpcClient _dataloggerClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public Tc0304SettingsListViewModel(AresDevices.AresDevicesClient devicesClient, TC0304Rpc.TC0304RpcClient dataloggerClient, IMessenger messenger) + { + _devicesClient = devicesClient; + _dataloggerClient = dataloggerClient; + _messenger = messenger; + UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new Tc0304SettingsViewModel(config, _dataloggerClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public Tc0304ConfigEditViewModel GetNewConfigEditViewModel() + => new(_dataloggerClient, _devicesClient); + + private Task UpdateConfigs() + { + SettingsViewModels = null; + return _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IDataloggerThermometer).FullName }) + .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); + } + + public async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(Tc0304Config config) + { + await _dataloggerClient.AddTc0304Async(config); + await UpdateConfigs(); + } +} diff --git a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsViewModel.cs b/UI/Features/Devices/Tc0304/Tc0304SettingsViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsViewModel.cs rename to UI/Features/Devices/Tc0304/Tc0304SettingsViewModel.cs index bee6b06f..ef946c0f 100644 --- a/UI/Backend/ViewModels/Settings/Device/Tc0304/Tc0304SettingsViewModel.cs +++ b/UI/Features/Devices/Tc0304/Tc0304SettingsViewModel.cs @@ -1,78 +1,79 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using ReactiveUI; -using Tc0304.Config; -using Tc0304.Services; -using UI.Backend.Devices; - -namespace UI.Backend.ViewModels.Settings.Device.Tc0304; - -public class Tc0304SettingsViewModel : ReactiveObject -{ - private readonly TC0304Rpc.TC0304RpcClient _dataloggerClient; - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public Tc0304SettingsViewModel(DeviceConfig deviceConfig, - TC0304Rpc.TC0304RpcClient dataloggerClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - DataloggerConfig = deviceConfig.ConfigData.Unpack(); - _dataloggerClient = dataloggerClient; - _devicesClient = devicesClient; - _messenger = messenger; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new Tc0304ConfigEditViewModel(_dataloggerClient, _devicesClient, DataloggerConfig); - } - - public Tc0304Config DataloggerConfig { get; } - - public Func OnRemoveCallback { get; } - - public Tc0304ConfigEditViewModel EditViewModel { get; } - - public Task GetDeviceOperationalStatus() - { - try - { - return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - } - catch(RpcException) - { - return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered TC0304 datalogger with a name {DataloggerConfig.Name}" }); - } - } - - - - public async Task Save() - { - var dataloggerConfig = EditViewModel.Save(); - var updateRequest = new UpdateTc0304Request - { - Id = _deviceConfig.UniqueId, - Config = dataloggerConfig - }; - - await _dataloggerClient.UpdateTc0304Async(updateRequest); - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest - { - DeviceId = _deviceConfig.UniqueId - }).ResponseAsync; - - public async Task Remove() - { - await _dataloggerClient.RemoveTc0304Async(new Tc0304Request { Tc0304Id = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using ReactiveUI; +using Tc0304.Config; +using Tc0304.Services; +using UI.Backend.ViewModels.Settings.Device.Tc0304; +using UI.Features.Devices.Shared; + +namespace UI.Features.Devices.Tc0304; + +public class Tc0304SettingsViewModel : ReactiveObject +{ + private readonly TC0304Rpc.TC0304RpcClient _dataloggerClient; + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public Tc0304SettingsViewModel(DeviceConfig deviceConfig, + TC0304Rpc.TC0304RpcClient dataloggerClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + DataloggerConfig = deviceConfig.ConfigData.Unpack(); + _dataloggerClient = dataloggerClient; + _devicesClient = devicesClient; + _messenger = messenger; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new Tc0304ConfigEditViewModel(_dataloggerClient, _devicesClient, DataloggerConfig); + } + + public Tc0304Config DataloggerConfig { get; } + + public Func OnRemoveCallback { get; } + + public Tc0304ConfigEditViewModel EditViewModel { get; } + + public Task GetDeviceOperationalStatus() + { + try + { + return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + } + catch(RpcException) + { + return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered TC0304 datalogger with a name {DataloggerConfig.Name}" }); + } + } + + + + public async Task Save() + { + var dataloggerConfig = EditViewModel.Save(); + var updateRequest = new UpdateTc0304Request + { + Id = _deviceConfig.UniqueId, + Config = dataloggerConfig + }; + + await _dataloggerClient.UpdateTc0304Async(updateRequest); + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest + { + DeviceId = _deviceConfig.UniqueId + }).ResponseAsync; + + public async Task Remove() + { + await _dataloggerClient.RemoveTc0304Async(new Tc0304Request { Tc0304Id = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } +} diff --git a/UI/Backend/ViewModels/Devices/Tc0304/Tc0304UnitControlViewModel.cs b/UI/Features/Devices/Tc0304/Tc0304UnitControlViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Devices/Tc0304/Tc0304UnitControlViewModel.cs rename to UI/Features/Devices/Tc0304/Tc0304UnitControlViewModel.cs index c8183d88..da16ec03 100644 --- a/UI/Backend/ViewModels/Devices/Tc0304/Tc0304UnitControlViewModel.cs +++ b/UI/Features/Devices/Tc0304/Tc0304UnitControlViewModel.cs @@ -1,90 +1,90 @@ -using ReactiveUI.SourceGenerators; -using Tc0304.Services; -using UI.Pages.Shared.Devices.DataLogger; -using UnitsNet; - -namespace UI.Backend.ViewModels.Tc0304; - -public partial class Tc0304UnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable -{ - private readonly TC0304Rpc.TC0304RpcClient _client; - private readonly CancellationTokenSource _stateUpdateTokenSource = new(); - private Task _stateListener = Task.CompletedTask; - - public Tc0304UnitControlViewModel(string id, string name, TC0304Rpc.TC0304RpcClient client) : base(id, name) - { - _client = client; - StartStateUpdater(); - - ViewType = typeof(Tc0304ControlWidgetView); - DefaultWidth = 22; - } - - [Reactive] - public partial bool BatteryLow { get; private set; } - - [Reactive] - public partial bool HoldActive { get; private set; } - - [Reactive] - public partial Temperature? T1Temp { get; private set; } - - [Reactive] - public partial Temperature? T2Temp { get; private set; } - - [Reactive] - public partial Temperature? T3Temp { get; private set; } - - [Reactive] - public partial Temperature? T4Temp { get; private set; } - - public string Probe1Name { get; set; } = "Probe 1"; - public string Probe2Name { get; set; } = "Probe 2"; - public string Probe3Name { get; set; } = "Probe 3"; - public string Probe4Name { get; set; } = "Probe 4"; - - - - private void StartStateUpdater() - { - var token = _stateUpdateTokenSource.Token; - - _stateListener = Task.Run(async () => - { - while (!token.IsCancellationRequested) - { - await UpdateState(); - await Task.Delay(TimeSpan.FromMilliseconds(500), token); - } - }, token); - } - - private void StopStateUpdater() - { - _stateUpdateTokenSource.Cancel(); - } - - private async Task UpdateState() - { - var response = await _client.GetTemperaturesAsync(new DeviceRequest { DeviceId = DeviceId }); - - T1Temp = response.HasProbe1C ? Temperature.FromDegreesCelsius(response.Probe1C) : null; - T2Temp = response.HasProbe2C ? Temperature.FromDegreesCelsius(response.Probe2C) : null; - T3Temp = response.HasProbe3C ? Temperature.FromDegreesCelsius(response.Probe3C) : null; - T4Temp = response.HasProbe4C ? Temperature.FromDegreesCelsius(response.Probe4C) : null; - } - - public void Hold() - { - _client.Hold(new DeviceRequest { DeviceId = DeviceId }); - } - - public async ValueTask DisposeAsync() - { - StopStateUpdater(); - await _stateListener; - _stateListener.Dispose(); - _stateUpdateTokenSource.Dispose(); - GC.SuppressFinalize(this); - } -} +using ReactiveUI.SourceGenerators; +using Tc0304.Services; +using UI.Features.Devices.Tc0304; +using UnitsNet; + +namespace UI.Backend.ViewModels.Tc0304; + +public partial class Tc0304UnitControlViewModel : DeviceUnitControlViewModel, IAsyncDisposable +{ + private readonly TC0304Rpc.TC0304RpcClient _client; + private readonly CancellationTokenSource _stateUpdateTokenSource = new(); + private Task _stateListener = Task.CompletedTask; + + public Tc0304UnitControlViewModel(string id, string name, TC0304Rpc.TC0304RpcClient client) : base(id, name) + { + _client = client; + StartStateUpdater(); + + ViewType = typeof(Tc0304ControlWidgetView); + DefaultWidth = 22; + } + + [Reactive] + public partial bool BatteryLow { get; private set; } + + [Reactive] + public partial bool HoldActive { get; private set; } + + [Reactive] + public partial Temperature? T1Temp { get; private set; } + + [Reactive] + public partial Temperature? T2Temp { get; private set; } + + [Reactive] + public partial Temperature? T3Temp { get; private set; } + + [Reactive] + public partial Temperature? T4Temp { get; private set; } + + public string Probe1Name { get; set; } = "Probe 1"; + public string Probe2Name { get; set; } = "Probe 2"; + public string Probe3Name { get; set; } = "Probe 3"; + public string Probe4Name { get; set; } = "Probe 4"; + + + + private void StartStateUpdater() + { + var token = _stateUpdateTokenSource.Token; + + _stateListener = Task.Run(async () => + { + while (!token.IsCancellationRequested) + { + await UpdateState(); + await Task.Delay(TimeSpan.FromMilliseconds(500), token); + } + }, token); + } + + private void StopStateUpdater() + { + _stateUpdateTokenSource.Cancel(); + } + + private async Task UpdateState() + { + var response = await _client.GetTemperaturesAsync(new DeviceRequest { DeviceId = DeviceId }); + + T1Temp = response.HasProbe1C ? Temperature.FromDegreesCelsius(response.Probe1C) : null; + T2Temp = response.HasProbe2C ? Temperature.FromDegreesCelsius(response.Probe2C) : null; + T3Temp = response.HasProbe3C ? Temperature.FromDegreesCelsius(response.Probe3C) : null; + T4Temp = response.HasProbe4C ? Temperature.FromDegreesCelsius(response.Probe4C) : null; + } + + public void Hold() + { + _client.Hold(new DeviceRequest { DeviceId = DeviceId }); + } + + public async ValueTask DisposeAsync() + { + StopStateUpdater(); + await _stateListener; + _stateListener.Dispose(); + _stateUpdateTokenSource.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceConfigEditView.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceConfigEditView.razor rename to UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditView.razor index aefcfd10..995547ee 100644 --- a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceConfigEditView.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditView.razor @@ -1,34 +1,34 @@ -@using UI.Backend.ViewModels.Settings.Device.TubeFurnace - -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
- - - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - -
- - Simulated -
- -
- - - -
- -@code { - [Parameter] - public Action OnValidSubmit { get; set; } = delegate - { - }; -} +@using UI.Backend.ViewModels.Settings.Device.TubeFurnace + +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+ + + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + +
+ + Simulated +
+ +
+ + + +
+ +@code { + [Parameter] + public Action OnValidSubmit { get; set; } = delegate + { + }; +} diff --git a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceConfigEditViewModel.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceConfigEditViewModel.cs rename to UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditViewModel.cs index cebe83c0..a1851be1 100644 --- a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceConfigEditViewModel.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceConfigEditViewModel.cs @@ -1,100 +1,100 @@ -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using System.ComponentModel.DataAnnotations; -using Ares.Services.Device; -using TubeFurnace.Config; -using TubeFurnace.Messaging; - -namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace -{ - public partial class TubeFurnaceConfigEditViewModel : ReactiveObject - { - private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private TubeFurnaceConfig _config; - private string _name = string.Empty; - - public TubeFurnaceConfigEditViewModel(TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, AresDevices.AresDevicesClient devicesClient) - { - _tubeFurnaceClient = tubeFurnaceClient; - _devicesClient = devicesClient; - _config = new TubeFurnaceConfig(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - } - - public TubeFurnaceConfigEditViewModel(TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, AresDevices.AresDevicesClient devicesClient, - TubeFurnaceConfig config) - { - _tubeFurnaceClient = tubeFurnaceClient; - _devicesClient = devicesClient; - _config = config; - _ = UpdateAvailableSerialPorts(); - LoadConfig(config); - } - - public bool NewConfig { get; private set; } - - [Required] - public string Name - { - get => _name; - set - { - if (!NewConfig) - { - return; - } - - this.RaiseAndSetIfChanged(ref _name, value); - } - } - - public void LoadConfig(TubeFurnaceConfig config) - { - Port = config.PortName; - _name = config.Name; - Simulated = config.Simulated; - Address = config.Address; - } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - [Reactive] - [Required] - public partial string? Port { get; set; } - - [Required] - public int? Address { get; set; } - - - [Reactive] - public partial bool Simulated { get; set; } - - public bool Modified => _config.Name != Name - || _config.PortName != Port - || _config.Simulated != Simulated - || _config.Address != Address; - - public TubeFurnaceConfig Save() - { - return Modified ? new TubeFurnaceConfig - { - Name = Name, - Simulated = Simulated, - PortName = Port, - Address = Address ?? 0, - } : _config; - } - } -} +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using System.ComponentModel.DataAnnotations; +using Ares.Services.Device; +using TubeFurnace.Config; +using TubeFurnace.Messaging; + +namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace +{ + public partial class TubeFurnaceConfigEditViewModel : ReactiveObject + { + private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private TubeFurnaceConfig _config; + private string _name = string.Empty; + + public TubeFurnaceConfigEditViewModel(TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, AresDevices.AresDevicesClient devicesClient) + { + _tubeFurnaceClient = tubeFurnaceClient; + _devicesClient = devicesClient; + _config = new TubeFurnaceConfig(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + } + + public TubeFurnaceConfigEditViewModel(TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, AresDevices.AresDevicesClient devicesClient, + TubeFurnaceConfig config) + { + _tubeFurnaceClient = tubeFurnaceClient; + _devicesClient = devicesClient; + _config = config; + _ = UpdateAvailableSerialPorts(); + LoadConfig(config); + } + + public bool NewConfig { get; private set; } + + [Required] + public string Name + { + get => _name; + set + { + if (!NewConfig) + { + return; + } + + this.RaiseAndSetIfChanged(ref _name, value); + } + } + + public void LoadConfig(TubeFurnaceConfig config) + { + Port = config.PortName; + _name = config.Name; + Simulated = config.Simulated; + Address = config.Address; + } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + [Reactive] + [Required] + public partial string? Port { get; set; } + + [Required] + public int? Address { get; set; } + + + [Reactive] + public partial bool Simulated { get; set; } + + public bool Modified => _config.Name != Name + || _config.PortName != Port + || _config.Simulated != Simulated + || _config.Address != Address; + + public TubeFurnaceConfig Save() + { + return Modified ? new TubeFurnaceConfig + { + Name = Name, + Simulated = Simulated, + PortName = Port, + Address = Address ?? 0, + } : _config; + } + } +} diff --git a/UI/Pages/Shared/Devices/TubeFurnace/TubeFurnaceControl.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceControl.razor similarity index 96% rename from UI/Pages/Shared/Devices/TubeFurnace/TubeFurnaceControl.razor rename to UI/Features/Devices/TubeFurnace/TubeFurnaceControl.razor index c6b05193..8fd5c2ec 100644 --- a/UI/Pages/Shared/Devices/TubeFurnace/TubeFurnaceControl.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceControl.razor @@ -1,81 +1,81 @@ -@using UI.Backend.ViewModels.TubeFurnace; -@using UnitsNet; -@using UnitsNet.Units; -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -@inject DialogService DialogService -@inject TooltipService TooltipService - -
-
-
@ViewModel!.DeviceName
-
- - @if(ViewModel!.CurrentTemperatureValue != null) - { - - } - else - { - - } -
- -
- -
- -@code { - - private string CurrentTemperatureDisplay - { - get - { - var result = $"PV {Temperature.From(ViewModel!.CurrentTemperatureValue!.Value, ViewModel!.TemperatureUnit):#0.####}"; - if (ViewModel!.TargetTemperatureValue is not null) - { - result += $" - SV {Temperature.From(ViewModel!.TargetTemperatureValue.Value, ViewModel!.TemperatureUnit):#0.####}"; - } - - return result; - } - } - - private Task SubmitTemperature() - { - if (TargetTemperatureValue is null) - return Task.CompletedTask; - - return ViewModel!.SetTargetTemperature(TargetTemperatureValue.Value); - } - - private int? TargetTemperatureValue { get; set; } - - - private string TargetTemperatureDisplay - { - get => ViewModel!.TargetTemperatureValue!.HasValue ? $"{Temperature.From(ViewModel!.TargetTemperatureValue.Value, ViewModel!.TemperatureUnit):#0.####}" : $"Target Temperature {UnitsNet.UnitAbbreviationsCache.Default.GetDefaultAbbreviation(ViewModel!.TemperatureUnit)}"; - } - - private string[] TemperatureUnits - { - get => Temperature.Units.Select(unit => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(unit)).ToArray(); - } - - private string TemperatureUnit - { - get => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(ViewModel!.TemperatureUnit); - set - { - if(UnitsNetSetup.Default.UnitParser.TryParse(value, out var tempUnit)) - { - ViewModel!.TemperatureUnit = tempUnit; - } - } - } -} +@using UI.Backend.ViewModels.TubeFurnace; +@using UnitsNet; +@using UnitsNet.Units; +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +@inject DialogService DialogService +@inject TooltipService TooltipService + +
+
+
@ViewModel!.DeviceName
+
+ + @if(ViewModel!.CurrentTemperatureValue != null) + { + + } + else + { + + } +
+ +
+ +
+ +@code { + + private string CurrentTemperatureDisplay + { + get + { + var result = $"PV {Temperature.From(ViewModel!.CurrentTemperatureValue!.Value, ViewModel!.TemperatureUnit):#0.####}"; + if (ViewModel!.TargetTemperatureValue is not null) + { + result += $" - SV {Temperature.From(ViewModel!.TargetTemperatureValue.Value, ViewModel!.TemperatureUnit):#0.####}"; + } + + return result; + } + } + + private Task SubmitTemperature() + { + if (TargetTemperatureValue is null) + return Task.CompletedTask; + + return ViewModel!.SetTargetTemperature(TargetTemperatureValue.Value); + } + + private int? TargetTemperatureValue { get; set; } + + + private string TargetTemperatureDisplay + { + get => ViewModel!.TargetTemperatureValue!.HasValue ? $"{Temperature.From(ViewModel!.TargetTemperatureValue.Value, ViewModel!.TemperatureUnit):#0.####}" : $"Target Temperature {UnitsNet.UnitAbbreviationsCache.Default.GetDefaultAbbreviation(ViewModel!.TemperatureUnit)}"; + } + + private string[] TemperatureUnits + { + get => Temperature.Units.Select(unit => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(unit)).ToArray(); + } + + private string TemperatureUnit + { + get => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(ViewModel!.TemperatureUnit); + set + { + if(UnitsNetSetup.Default.UnitParser.TryParse(value, out var tempUnit)) + { + ViewModel!.TemperatureUnit = tempUnit; + } + } + } +} diff --git a/UI/Backend/Factories/TubeFurnaceDeviceControlViewModelFactory.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs similarity index 95% rename from UI/Backend/Factories/TubeFurnaceDeviceControlViewModelFactory.cs rename to UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs index 253ce152..b1c0bff8 100644 --- a/UI/Backend/Factories/TubeFurnaceDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs @@ -2,11 +2,12 @@ using DynamicData; using Google.Protobuf.WellKnownTypes; using TubeFurnace.Messaging; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.TubeFurnace; -namespace UI.Backend.Factories +namespace UI.Features.Devices.TubeFurnace { public class TubeFurnaceDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsListView.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsListView.razor rename to UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor index a1d3b90d..6f1fb890 100644 --- a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsListView.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor @@ -1,66 +1,67 @@ -@page "/settings/device/tube_furnace" -@layout DeviceSettingsLayout -@using UI.Backend.ViewModels.Settings.Device.TubeFurnace -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - -@if (ViewModel!.SettingsViewModels is null) -{ - -} -else -{ -
- @foreach (var settingsViewModel in ViewModel!.SettingsViewModels) - { -
- -
- } - -
-} - -@code { - private async Task AddConfigClick() - { - var viewModel = ViewModel!.GetNewConfigEditViewModel(); - await DialogService.OpenAsync("New Tube Furnace", ds => - @); - } - - private async Task HandleSave(TubeFurnaceConfigEditViewModel viewModel) - { - SavingInProgress = true; - StateHasChanged(); - var config = viewModel.Save(); - try - { - await ViewModel!.AddNewConfig(viewModel.Save()); - DialogService.Close(); - NotificationService.Success($"Successfully added MFC {viewModel.Name}"); - } - catch(Grpc.Core.RpcException e) - { - NotificationService.Error(e.InnerException?.Message ?? e.Message); - } - finally - { - SavingInProgress = false; - StateHasChanged(); - } - } - - private bool SavingInProgress { get; set; } +@page "/settings/device/tube_furnace" +@layout DeviceSettingsLayout +@using UI.Backend.ViewModels.Settings.Device.TubeFurnace +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + +@if (ViewModel!.SettingsViewModels is null) +{ + +} +else +{ +
+ @foreach (var settingsViewModel in ViewModel!.SettingsViewModels) + { +
+ +
+ } + +
+} + +@code { + private async Task AddConfigClick() + { + var viewModel = ViewModel!.GetNewConfigEditViewModel(); + await DialogService.OpenAsync("New Tube Furnace", ds => + @); + } + + private async Task HandleSave(TubeFurnaceConfigEditViewModel viewModel) + { + SavingInProgress = true; + StateHasChanged(); + var config = viewModel.Save(); + try + { + await ViewModel!.AddNewConfig(viewModel.Save()); + DialogService.Close(); + NotificationService.Success($"Successfully added MFC {viewModel.Name}"); + } + catch(Grpc.Core.RpcException e) + { + NotificationService.Error(e.InnerException?.Message ?? e.Message); + } + finally + { + SavingInProgress = false; + StateHasChanged(); + } + } + + private bool SavingInProgress { get; set; } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsListViewModel.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsListViewModel.cs rename to UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListViewModel.cs index 6ac976f7..99ea8ddb 100644 --- a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsListViewModel.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListViewModel.cs @@ -1,58 +1,58 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using ReactiveUI.SourceGenerators; -using ReactiveUI; -using LindbergFurnace; -using TubeFurnace.Config; -using TubeFurnace.Messaging; -using CommunityToolkit.Mvvm.Messaging; - -namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace -{ - public partial class TubeFurnaceSettingsListViewModel : ReactiveObject - { - private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public TubeFurnaceSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, IMessenger messenger) - { - _devicesClient = devicesClient; - _tubeFurnaceClient = tubeFurnaceClient; - _messenger = messenger; - UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new TubeFurnaceSettingsViewModel(config, _tubeFurnaceClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public TubeFurnaceConfigEditViewModel GetNewConfigEditViewModel() - => new(_tubeFurnaceClient, _devicesClient); - - private Task UpdateConfigs() - { - SettingsViewModels = null; - return _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(ITubeFurnace).FullName }) - .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); - } - - public async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(TubeFurnaceConfig config) - { - await _tubeFurnaceClient.AddTubeFurnaceAsync(config); - await UpdateConfigs(); - } - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using ReactiveUI.SourceGenerators; +using ReactiveUI; +using LindbergFurnace; +using TubeFurnace.Config; +using TubeFurnace.Messaging; +using CommunityToolkit.Mvvm.Messaging; + +namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace +{ + public partial class TubeFurnaceSettingsListViewModel : ReactiveObject + { + private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public TubeFurnaceSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, IMessenger messenger) + { + _devicesClient = devicesClient; + _tubeFurnaceClient = tubeFurnaceClient; + _messenger = messenger; + UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new TubeFurnaceSettingsViewModel(config, _tubeFurnaceClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public TubeFurnaceConfigEditViewModel GetNewConfigEditViewModel() + => new(_tubeFurnaceClient, _devicesClient); + + private Task UpdateConfigs() + { + SettingsViewModels = null; + return _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(ITubeFurnace).FullName }) + .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); + } + + public async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(TubeFurnaceConfig config) + { + await _tubeFurnaceClient.AddTubeFurnaceAsync(config); + await UpdateConfigs(); + } + } +} diff --git a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsView.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsView.razor rename to UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor index 30ba72df..6ce77fc3 100644 --- a/UI/Pages/Shared/Settings/Device/TubeFurnace/TubeFurnaceSettingsView.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor @@ -1,74 +1,75 @@ -@using UI.Backend.ViewModels.Settings.Device.TubeFurnace -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - -
-
- Port - - @ViewModel!.TubeFurnaceConfig.PortName -
- - @if (ViewModel!.DeviceActive) - { -
- @if (_changing) - { -
-
- -
-
- } -
-
-
- } - else if(_initActive) - { - - } -
-
- -@code { - private bool _initActive; - private bool _changing; - - private string Name => ViewModel!.TubeFurnaceConfig.Name + (ViewModel!.TubeFurnaceConfig.Simulated ? " -- (Simulated)" : ""); - - protected override async Task OnInitializedAsync() - { - _initActive = true; - StateHasChanged(); - await ViewModel!.Init(); - _initActive = false; - StateHasChanged(); - } - - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit Stepper", ds => - @); - - _changing = true; - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - _changing = false; - } - - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.TubeFurnaceConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } +@using UI.Backend.ViewModels.Settings.Device.TubeFurnace +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + +
+
+ Port - + @ViewModel!.TubeFurnaceConfig.PortName +
+ + @if (ViewModel!.DeviceActive) + { +
+ @if (_changing) + { +
+
+ +
+
+ } +
+
+
+ } + else if(_initActive) + { + + } +
+
+ +@code { + private bool _initActive; + private bool _changing; + + private string Name => ViewModel!.TubeFurnaceConfig.Name + (ViewModel!.TubeFurnaceConfig.Simulated ? " -- (Simulated)" : ""); + + protected override async Task OnInitializedAsync() + { + _initActive = true; + StateHasChanged(); + await ViewModel!.Init(); + _initActive = false; + StateHasChanged(); + } + + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit Stepper", ds => + @); + + _changing = true; + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + _changing = false; + } + + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.TubeFurnaceConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsViewModel.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsViewModel.cs rename to UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsViewModel.cs index f9936159..c34266ae 100644 --- a/UI/Backend/ViewModels/Settings/Device/TubeFurnace/TubeFurnaceSettingsViewModel.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsViewModel.cs @@ -1,90 +1,90 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using TubeFurnace.Config; -using TubeFurnace.Messaging; -using UI.Backend.Devices; - -namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace -{ - public partial class TubeFurnaceSettingsViewModel : ReactiveObject - { - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; - private readonly IMessenger _messenger; - - public TubeFurnaceSettingsViewModel(DeviceConfig deviceConfig, - TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _deviceConfig = deviceConfig; - TubeFurnaceConfig = deviceConfig.ConfigData.Unpack(); - _tubeFurnaceClient = tubeFurnaceClient; - _devicesClient = devicesClient; - _messenger = messenger; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new TubeFurnaceConfigEditViewModel(_tubeFurnaceClient, _devicesClient, TubeFurnaceConfig); - } - - public TubeFurnaceConfig TubeFurnaceConfig { get; } - - public Func OnRemoveCallback { get; } - - public TubeFurnaceConfigEditViewModel EditViewModel { get; } - - public async Task GetDeviceOperationalStatus() - { - try - { - var status = await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - DeviceActive = status.OperationalState is OperationalState.Active; - return status; - } - catch (RpcException) - { - return new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered stepper controller with a name {TubeFurnaceConfig.Name}" }; - } - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - - public async Task Save() - { - var tubeFurnaceConfig = EditViewModel.Save(); - var updateRequest = new TubeFurnaceUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = tubeFurnaceConfig - }; - - await _tubeFurnaceClient.UpdateTubeFurnaceAsync(updateRequest); - } - - public async Task Remove() - { - await _tubeFurnaceClient.RemoveTubeFurnaceAsync(new TubeFurnaceRequest { TubeFurnaceId = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } - - public async Task Init() - { - var status = await GetDeviceOperationalStatus(); - if (status.OperationalState != OperationalState.Active) - return; - - var state = await _tubeFurnaceClient.GetStateAsync(new TubeFurnaceRequest { TubeFurnaceId = _deviceConfig.UniqueId }); - - } - - [Reactive] - public partial bool DeviceActive { get; private set; } - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using TubeFurnace.Config; +using TubeFurnace.Messaging; +using UI.Features.Devices.Shared; + +namespace UI.Backend.ViewModels.Settings.Device.TubeFurnace +{ + public partial class TubeFurnaceSettingsViewModel : ReactiveObject + { + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; + private readonly IMessenger _messenger; + + public TubeFurnaceSettingsViewModel(DeviceConfig deviceConfig, + TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _deviceConfig = deviceConfig; + TubeFurnaceConfig = deviceConfig.ConfigData.Unpack(); + _tubeFurnaceClient = tubeFurnaceClient; + _devicesClient = devicesClient; + _messenger = messenger; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new TubeFurnaceConfigEditViewModel(_tubeFurnaceClient, _devicesClient, TubeFurnaceConfig); + } + + public TubeFurnaceConfig TubeFurnaceConfig { get; } + + public Func OnRemoveCallback { get; } + + public TubeFurnaceConfigEditViewModel EditViewModel { get; } + + public async Task GetDeviceOperationalStatus() + { + try + { + var status = await _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + DeviceActive = status.OperationalState is OperationalState.Active; + return status; + } + catch (RpcException) + { + return new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered stepper controller with a name {TubeFurnaceConfig.Name}" }; + } + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + + public async Task Save() + { + var tubeFurnaceConfig = EditViewModel.Save(); + var updateRequest = new TubeFurnaceUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = tubeFurnaceConfig + }; + + await _tubeFurnaceClient.UpdateTubeFurnaceAsync(updateRequest); + } + + public async Task Remove() + { + await _tubeFurnaceClient.RemoveTubeFurnaceAsync(new TubeFurnaceRequest { TubeFurnaceId = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } + + public async Task Init() + { + var status = await GetDeviceOperationalStatus(); + if (status.OperationalState != OperationalState.Active) + return; + + var state = await _tubeFurnaceClient.GetStateAsync(new TubeFurnaceRequest { TubeFurnaceId = _deviceConfig.UniqueId }); + + } + + [Reactive] + public partial bool DeviceActive { get; private set; } + } +} diff --git a/UI/Backend/ViewModels/Devices/TubeFurnace/TubeFurnaceViewModel.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Devices/TubeFurnace/TubeFurnaceViewModel.cs rename to UI/Features/Devices/TubeFurnace/TubeFurnaceViewModel.cs index 208eced9..b857b4f0 100644 --- a/UI/Backend/ViewModels/Devices/TubeFurnace/TubeFurnaceViewModel.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceViewModel.cs @@ -1,101 +1,101 @@ -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using TubeFurnace.Messaging; -using UI.Pages.Shared.Devices.TubeFurnace; -using UnitsNet; -using UnitsNet.Units; - -namespace UI.Backend.ViewModels.TubeFurnace; - -public partial class TubeFurnaceViewModel : DeviceUnitControlViewModel, IAsyncDisposable -{ - private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; - private Task _stateListener = Task.CompletedTask; - private CancellationTokenSource _stateUpdateTokenSource; - private TemperatureUnit _temperatureUnit = TemperatureUnit.DegreeCelsius; - - public TubeFurnaceViewModel(string id, string deviceName, TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient) : base(id, deviceName) - { - _tubeFurnaceClient = tubeFurnaceClient; - _stateUpdateTokenSource = new(); - StartStateUpdater(); - - ViewType = typeof(TubeFurnaceControl); - DefaultWidth = 18; - } - - private void StartStateUpdater() - { - _stateListener = Task.Factory.StartNew(async _ => - { - Thread.CurrentThread.Name = "Tube Furnace State Listener View Model Thread"; - var cancelled = false; - lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } - while (!cancelled) - { - await UpdateState(); - await Task.Delay(TimeSpan.FromMilliseconds(500)); - lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } - } - }, - _stateUpdateTokenSource.Token, - TaskCreationOptions.LongRunning); - } - - private async Task UpdateState() - { - var deviceRequest = new TubeFurnaceRequest { TubeFurnaceId = DeviceId }; - await _tubeFurnaceClient.GetSetpointAsync(deviceRequest); - await _tubeFurnaceClient.GetCurrentTemperatureAsync(deviceRequest); - - var state = await _tubeFurnaceClient.GetStateAsync(new TubeFurnaceRequest { TubeFurnaceId = DeviceId }); - - // TODO - CurrentTemperatureValue = Temperature.FromDegreesCelsius(state.CurrentTemperature).As(TemperatureUnit); - TargetTemperatureValue = Temperature.FromDegreesCelsius(state.Setpoint).As(TemperatureUnit); - } - - public async ValueTask DisposeAsync() - { - _stateUpdateTokenSource.Cancel(); - await _stateListener; - _stateListener.Dispose(); - _stateUpdateTokenSource.Dispose(); - GC.SuppressFinalize(this); - } - - public async Task SetTargetTemperature(double value) - { - await _tubeFurnaceClient - .SetSetpointAsync( - new SetSetpointRequest - { - DeviceRequest = new TubeFurnaceRequest - { - TubeFurnaceId = DeviceId - }, - DegreesCelsius = Temperature.From(value, TemperatureUnit).DegreesCelsius - }); - } - - public TemperatureUnit TemperatureUnit - { - get => _temperatureUnit; - set - { - TargetTemperatureValue = TargetTemperatureValue.HasValue ? Temperature.From(TargetTemperatureValue.Value, TemperatureUnit).As(value) : null; - CurrentTemperatureValue = CurrentTemperatureValue.HasValue ? Temperature.From(CurrentTemperatureValue.Value, TemperatureUnit).As(value) : null; - this.RaiseAndSetIfChanged(ref _temperatureUnit, value); - } - } - [Reactive] - public partial double? CurrentTemperatureValue { get; private set; } - [Reactive] - public partial double? TargetTemperatureValue { get; set; } - [Reactive] - public partial DurationUnit RampRateDurationUnit { get; set; } - [Reactive] - public partial double? TargetRampRateTemperatureValue { get; set; } - [Reactive] - public partial double? CurrentRampRateTemperatureValue { get; set; } -} +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using TubeFurnace.Messaging; +using UI.Features.Devices.TubeFurnace; +using UnitsNet; +using UnitsNet.Units; + +namespace UI.Backend.ViewModels.TubeFurnace; + +public partial class TubeFurnaceViewModel : DeviceUnitControlViewModel, IAsyncDisposable +{ + private readonly TubeFurnaceRpc.TubeFurnaceRpcClient _tubeFurnaceClient; + private Task _stateListener = Task.CompletedTask; + private CancellationTokenSource _stateUpdateTokenSource; + private TemperatureUnit _temperatureUnit = TemperatureUnit.DegreeCelsius; + + public TubeFurnaceViewModel(string id, string deviceName, TubeFurnaceRpc.TubeFurnaceRpcClient tubeFurnaceClient) : base(id, deviceName) + { + _tubeFurnaceClient = tubeFurnaceClient; + _stateUpdateTokenSource = new(); + StartStateUpdater(); + + ViewType = typeof(TubeFurnaceControl); + DefaultWidth = 18; + } + + private void StartStateUpdater() + { + _stateListener = Task.Factory.StartNew(async _ => + { + Thread.CurrentThread.Name = "Tube Furnace State Listener View Model Thread"; + var cancelled = false; + lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } + while (!cancelled) + { + await UpdateState(); + await Task.Delay(TimeSpan.FromMilliseconds(500)); + lock (_stateUpdateTokenSource) { cancelled = _stateUpdateTokenSource.IsCancellationRequested; } + } + }, + _stateUpdateTokenSource.Token, + TaskCreationOptions.LongRunning); + } + + private async Task UpdateState() + { + var deviceRequest = new TubeFurnaceRequest { TubeFurnaceId = DeviceId }; + await _tubeFurnaceClient.GetSetpointAsync(deviceRequest); + await _tubeFurnaceClient.GetCurrentTemperatureAsync(deviceRequest); + + var state = await _tubeFurnaceClient.GetStateAsync(new TubeFurnaceRequest { TubeFurnaceId = DeviceId }); + + // TODO + CurrentTemperatureValue = Temperature.FromDegreesCelsius(state.CurrentTemperature).As(TemperatureUnit); + TargetTemperatureValue = Temperature.FromDegreesCelsius(state.Setpoint).As(TemperatureUnit); + } + + public async ValueTask DisposeAsync() + { + _stateUpdateTokenSource.Cancel(); + await _stateListener; + _stateListener.Dispose(); + _stateUpdateTokenSource.Dispose(); + GC.SuppressFinalize(this); + } + + public async Task SetTargetTemperature(double value) + { + await _tubeFurnaceClient + .SetSetpointAsync( + new SetSetpointRequest + { + DeviceRequest = new TubeFurnaceRequest + { + TubeFurnaceId = DeviceId + }, + DegreesCelsius = Temperature.From(value, TemperatureUnit).DegreesCelsius + }); + } + + public TemperatureUnit TemperatureUnit + { + get => _temperatureUnit; + set + { + TargetTemperatureValue = TargetTemperatureValue.HasValue ? Temperature.From(TargetTemperatureValue.Value, TemperatureUnit).As(value) : null; + CurrentTemperatureValue = CurrentTemperatureValue.HasValue ? Temperature.From(CurrentTemperatureValue.Value, TemperatureUnit).As(value) : null; + this.RaiseAndSetIfChanged(ref _temperatureUnit, value); + } + } + [Reactive] + public partial double? CurrentTemperatureValue { get; private set; } + [Reactive] + public partial double? TargetTemperatureValue { get; set; } + [Reactive] + public partial DurationUnit RampRateDurationUnit { get; set; } + [Reactive] + public partial double? TargetRampRateTemperatureValue { get; set; } + [Reactive] + public partial double? CurrentRampRateTemperatureValue { get; set; } +} diff --git a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerConfigEditView.razor b/UI/Features/Devices/ValveController/ValveControllerConfigEditView.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/ValveController/ValveControllerConfigEditView.razor rename to UI/Features/Devices/ValveController/ValveControllerConfigEditView.razor index de5b2ae4..f7327c48 100644 --- a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerConfigEditView.razor +++ b/UI/Features/Devices/ValveController/ValveControllerConfigEditView.razor @@ -1,39 +1,39 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
- - - - @if (ViewModel!.AvailablePorts is not null) - { - @foreach (var port in ViewModel!.AvailablePorts) - { - - } - } - -
- - Simulated -
-
- - - -
- -@code { - [Parameter] - public Action OnValidSubmit { get; set; } = delegate - { - }; - - private void PortChanged(ChangeEventArgs obj) - { - if (obj.Value is not string stringValue) - throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); - - ViewModel!.Port = stringValue; - } -} +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+ + + + @if (ViewModel!.AvailablePorts is not null) + { + @foreach (var port in ViewModel!.AvailablePorts) + { + + } + } + +
+ + Simulated +
+
+ + + +
+ +@code { + [Parameter] + public Action OnValidSubmit { get; set; } = delegate + { + }; + + private void PortChanged(ChangeEventArgs obj) + { + if (obj.Value is not string stringValue) + throw new InvalidOperationException($"{obj.Value} is not a string and cannot be used as a port name."); + + ViewModel!.Port = stringValue; + } +} diff --git a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerConfigEditViewModel.cs b/UI/Features/Devices/ValveController/ValveControllerConfigEditViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerConfigEditViewModel.cs rename to UI/Features/Devices/ValveController/ValveControllerConfigEditViewModel.cs index 43c701e1..1efde4c1 100644 --- a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerConfigEditViewModel.cs +++ b/UI/Features/Devices/ValveController/ValveControllerConfigEditViewModel.cs @@ -1,76 +1,76 @@ -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using System.ComponentModel.DataAnnotations; -using Ares.Services.Device; -using ValveController.Config; -using ValveController.Services; - -namespace UI.Backend.ViewModels.Settings.Device.ValveController; - -public partial class ValveControllerConfigEditViewModel : ReactiveObject -{ - private readonly ValveControllerRpc.ValveControllerRpcClient _client; - private readonly ValveControllerConfig _config; - private readonly AresDevices.AresDevicesClient _devicesClient; - private string? _deviceName; - - public ValveControllerConfigEditViewModel(ValveControllerRpc.ValveControllerRpcClient client, AresDevices.AresDevicesClient devicesClient) - { - _client = client; - _devicesClient = devicesClient; - _config = new ValveControllerConfig(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - } - - public ValveControllerConfigEditViewModel(ValveControllerRpc.ValveControllerRpcClient client, AresDevices.AresDevicesClient devicesClient, ValveControllerConfig config) - { - _client = client; - _devicesClient = devicesClient; - _config = new ValveControllerConfig(); - NewConfig = true; - _ = UpdateAvailableSerialPorts(); - _deviceName = config.Name; - Port = config.PortName; - Simulated = config.Simulated; - } - - [Required] - public string? Name - { - get => _deviceName; - - set - { - if (!NewConfig) - return; - - _deviceName = value; - } - } - - [Required] - public string? Port { get; set; } - - public bool NewConfig { get; set; } - - public bool Simulated { get; set; } - - [Reactive] - public partial IEnumerable? AvailablePorts { get; private set; } - - public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; - - public async Task UpdateAvailableSerialPorts() - { - AvailablePorts = null; - Port = null; - var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); - AvailablePorts = ports.SerialPorts; - } - - public ValveControllerConfig Save() - => Modified ? new ValveControllerConfig { Name = Name, PortName = Port, Simulated = Simulated } : _config; - -} +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using System.ComponentModel.DataAnnotations; +using Ares.Services.Device; +using ValveController.Config; +using ValveController.Services; + +namespace UI.Backend.ViewModels.Settings.Device.ValveController; + +public partial class ValveControllerConfigEditViewModel : ReactiveObject +{ + private readonly ValveControllerRpc.ValveControllerRpcClient _client; + private readonly ValveControllerConfig _config; + private readonly AresDevices.AresDevicesClient _devicesClient; + private string? _deviceName; + + public ValveControllerConfigEditViewModel(ValveControllerRpc.ValveControllerRpcClient client, AresDevices.AresDevicesClient devicesClient) + { + _client = client; + _devicesClient = devicesClient; + _config = new ValveControllerConfig(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + } + + public ValveControllerConfigEditViewModel(ValveControllerRpc.ValveControllerRpcClient client, AresDevices.AresDevicesClient devicesClient, ValveControllerConfig config) + { + _client = client; + _devicesClient = devicesClient; + _config = new ValveControllerConfig(); + NewConfig = true; + _ = UpdateAvailableSerialPorts(); + _deviceName = config.Name; + Port = config.PortName; + Simulated = config.Simulated; + } + + [Required] + public string? Name + { + get => _deviceName; + + set + { + if (!NewConfig) + return; + + _deviceName = value; + } + } + + [Required] + public string? Port { get; set; } + + public bool NewConfig { get; set; } + + public bool Simulated { get; set; } + + [Reactive] + public partial IEnumerable? AvailablePorts { get; private set; } + + public bool Modified => _config.Name != Name || _config.PortName != Port || _config.Simulated != Simulated; + + public async Task UpdateAvailableSerialPorts() + { + AvailablePorts = null; + Port = null; + var ports = await _devicesClient.GetServerSerialPortsAsync(new Empty()); + AvailablePorts = ports.SerialPorts; + } + + public ValveControllerConfig Save() + => Modified ? new ValveControllerConfig { Name = Name, PortName = Port, Simulated = Simulated } : _config; + +} diff --git a/UI/Backend/Factories/ValveControllerDeviceControlViewModelFactory.cs b/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs similarity index 93% rename from UI/Backend/Factories/ValveControllerDeviceControlViewModelFactory.cs rename to UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs index 117e3c87..7b5e465a 100644 --- a/UI/Backend/Factories/ValveControllerDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs @@ -1,12 +1,12 @@ using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; +using UI.Backend.Factories; using UI.Backend.Repos; using UI.Backend.ViewModels; -using UI.Backend.ViewModels.Devices.ValveController; using ValveController.Services; -namespace UI.Backend.Factories; +namespace UI.Features.Devices.ValveController; public class ValveControllerDeviceControlViewModelFactory : DeviceConnectorViewModelFactory { diff --git a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsList.razor b/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor similarity index 96% rename from UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsList.razor rename to UI/Features/Devices/ValveController/ValveControllerSettingsList.razor index 855e12ff..237eb2f2 100644 --- a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsList.razor +++ b/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor @@ -1,66 +1,67 @@ -@page "/settings/device/ValveController" -@layout DeviceSettingsLayout -@using UI.Backend.ViewModels.Settings.Device.ValveController -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - -@if (ViewModel!.SettingsViewModels is null) -{ - -} -else -{ -
- @foreach (var vm in ViewModel!.SettingsViewModels) - { -
- -
- } - -
-} - - -@code { - private async Task AddConfigClick() - { - var viewModel = ViewModel!.GetNewConfigEditViewModel(); - await DialogService.OpenAsync("New Valve Controller", ds => - @); - } - - private async Task HandleSave(ValveControllerConfigEditViewModel viewModel) - { - SavingInProgress = true; - StateHasChanged(); - try - { - await ViewModel!.AddNewConfig(viewModel.Save()); - DialogService.Close(); - NotificationService.Success($"Successfully added Valve Controller {viewModel.Name}"); - } - catch(Grpc.Core.RpcException e) - { - NotificationService.Error(e.InnerException?.Message ?? e.Message); - } - finally - { - SavingInProgress = false; - StateHasChanged(); - } - } - - private bool SavingInProgress { get; set; } +@page "/settings/device/ValveController" +@layout DeviceSettingsLayout +@using UI.Backend.ViewModels.Settings.Device.ValveController +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + +@if (ViewModel!.SettingsViewModels is null) +{ + +} +else +{ +
+ @foreach (var vm in ViewModel!.SettingsViewModels) + { +
+ +
+ } + +
+} + + +@code { + private async Task AddConfigClick() + { + var viewModel = ViewModel!.GetNewConfigEditViewModel(); + await DialogService.OpenAsync("New Valve Controller", ds => + @); + } + + private async Task HandleSave(ValveControllerConfigEditViewModel viewModel) + { + SavingInProgress = true; + StateHasChanged(); + try + { + await ViewModel!.AddNewConfig(viewModel.Save()); + DialogService.Close(); + NotificationService.Success($"Successfully added Valve Controller {viewModel.Name}"); + } + catch(Grpc.Core.RpcException e) + { + NotificationService.Error(e.InnerException?.Message ?? e.Message); + } + finally + { + SavingInProgress = false; + StateHasChanged(); + } + } + + private bool SavingInProgress { get; set; } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsListViewModel.cs b/UI/Features/Devices/ValveController/ValveControllerSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsListViewModel.cs rename to UI/Features/Devices/ValveController/ValveControllerSettingsListViewModel.cs index 6a08bd1d..755e8b30 100644 --- a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsListViewModel.cs +++ b/UI/Features/Devices/ValveController/ValveControllerSettingsListViewModel.cs @@ -1,59 +1,60 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using ValveController; -using ValveController.Config; -using ValveController.Services; - -namespace UI.Backend.ViewModels.Settings.Device.ValveController; - -public partial class ValveControllerSettingsListViewModel : ReactiveObject -{ - private readonly ValveControllerRpc.ValveControllerRpcClient _valveControllerClient; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public ValveControllerSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, - ValveControllerRpc.ValveControllerRpcClient valveControllerRpcClient, - IMessenger messenger) - { - _devicesClient = devicesClient; - _valveControllerClient = valveControllerRpcClient; - _messenger = messenger; - UpdateConfigs(); - } - - [Reactive] - public partial IEnumerable? SettingsViewModels { get; private set; } - - private void UpdateViewModels(IEnumerable deviceConfigs) - { - var viewModels = deviceConfigs.Select(config => new ValveControllerSettingsViewModel(config, _valveControllerClient, _devicesClient, _messenger, OnConfigRemoved)); - SettingsViewModels = viewModels; - } - - public ValveControllerConfigEditViewModel GetNewConfigEditViewModel() => new(_valveControllerClient, _devicesClient); - - private Task UpdateConfigs() - { - SettingsViewModels = null; - return _devicesClient - .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IValveController).FullName }) - .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); - } - - private async Task OnConfigRemoved() - { - SettingsViewModels = null; - await UpdateConfigs(); - } - - public async Task AddNewConfig(ValveControllerConfig config) - { - await _valveControllerClient.AddValveControllerAsync(config); - await UpdateConfigs(); - } - -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.Devices.ValveController; +using ValveController; +using ValveController.Config; +using ValveController.Services; + +namespace UI.Backend.ViewModels.Settings.Device.ValveController; + +public partial class ValveControllerSettingsListViewModel : ReactiveObject +{ + private readonly ValveControllerRpc.ValveControllerRpcClient _valveControllerClient; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public ValveControllerSettingsListViewModel(AresDevices.AresDevicesClient devicesClient, + ValveControllerRpc.ValveControllerRpcClient valveControllerRpcClient, + IMessenger messenger) + { + _devicesClient = devicesClient; + _valveControllerClient = valveControllerRpcClient; + _messenger = messenger; + UpdateConfigs(); + } + + [Reactive] + public partial IEnumerable? SettingsViewModels { get; private set; } + + private void UpdateViewModels(IEnumerable deviceConfigs) + { + var viewModels = deviceConfigs.Select(config => new ValveControllerSettingsViewModel(config, _valveControllerClient, _devicesClient, _messenger, OnConfigRemoved)); + SettingsViewModels = viewModels; + } + + public ValveControllerConfigEditViewModel GetNewConfigEditViewModel() => new(_valveControllerClient, _devicesClient); + + private Task UpdateConfigs() + { + SettingsViewModels = null; + return _devicesClient + .GetAllDeviceConfigsAsync(new DeviceConfigRequest { DeviceType = typeof(IValveController).FullName }) + .ResponseAsync.ContinueWith(task => UpdateViewModels(task.Result.Configs)); + } + + private async Task OnConfigRemoved() + { + SettingsViewModels = null; + await UpdateConfigs(); + } + + public async Task AddNewConfig(ValveControllerConfig config) + { + await _valveControllerClient.AddValveControllerAsync(config); + await UpdateConfigs(); + } + +} diff --git a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsView.razor b/UI/Features/Devices/ValveController/ValveControllerSettingsView.razor similarity index 87% rename from UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsView.razor rename to UI/Features/Devices/ValveController/ValveControllerSettingsView.razor index e1c0d707..53f65cdb 100644 --- a/UI/Pages/Shared/Settings/Device/ValveController/ValveControllerSettingsView.razor +++ b/UI/Features/Devices/ValveController/ValveControllerSettingsView.razor @@ -1,40 +1,41 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject DialogService DialogService -@inject IUiNotificationService NotificationService - - - -
- @if (ViewModel!.ValveControllerConfig.Simulated) - { - Simulated Valve Controller -- - } - - @ViewModel!.ValveControllerConfig.Name - @ViewModel!.ValveControllerConfig.PortName -
-
- -@code { - private async Task EditCallback() - { - var viewModel = ViewModel!.EditViewModel; - var result = await DialogService.OpenAsync("Edit Valve Controller", ds => - @); - - if (result is true && viewModel.Modified) - { - await ViewModel!.Save(); - NotificationService.Success("Config has been saved"); - } - } - - private async Task RemoveCallback() - { - var result = await DialogService.Confirm($"Delete {ViewModel!.ValveControllerConfig.Name}?"); - if (result is true) - { - await ViewModel!.Remove(); - } - } -} +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject DialogService DialogService +@inject IUiNotificationService NotificationService + + + +
+ @if (ViewModel!.ValveControllerConfig.Simulated) + { + Simulated Valve Controller -- + } + + @ViewModel!.ValveControllerConfig.Name + @ViewModel!.ValveControllerConfig.PortName +
+
+ +@code { + private async Task EditCallback() + { + var viewModel = ViewModel!.EditViewModel; + var result = await DialogService.OpenAsync("Edit Valve Controller", ds => + @); + + if (result is true && viewModel.Modified) + { + await ViewModel!.Save(); + NotificationService.Success("Config has been saved"); + } + } + + private async Task RemoveCallback() + { + var result = await DialogService.Confirm($"Delete {ViewModel!.ValveControllerConfig.Name}?"); + if (result is true) + { + await ViewModel!.Remove(); + } + } +} diff --git a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsViewModel.cs b/UI/Features/Devices/ValveController/ValveControllerSettingsViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsViewModel.cs rename to UI/Features/Devices/ValveController/ValveControllerSettingsViewModel.cs index 6e562278..222e6557 100644 --- a/UI/Backend/ViewModels/Settings/Device/ValveController/ValveControllerSettingsViewModel.cs +++ b/UI/Features/Devices/ValveController/ValveControllerSettingsViewModel.cs @@ -1,72 +1,73 @@ -using Ares.Datamodel.Device; -using Ares.Services.Device; -using CommunityToolkit.Mvvm.Messaging; -using Grpc.Core; -using ReactiveUI; -using UI.Backend.Devices; -using ValveController.Config; -using ValveController.Services; - -namespace UI.Backend.ViewModels.Settings.Device.ValveController; - -public class ValveControllerSettingsViewModel : ReactiveObject -{ - private readonly ValveControllerRpc.ValveControllerRpcClient _valveControllerClient; - private readonly DeviceConfig _deviceConfig; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly IMessenger _messenger; - - public ValveControllerSettingsViewModel(DeviceConfig deviceConfig, - ValveControllerRpc.ValveControllerRpcClient valveControllerClient, - AresDevices.AresDevicesClient devicesClient, - IMessenger messenger, - Func onRemoveCallback) - { - _valveControllerClient = valveControllerClient; - _deviceConfig = deviceConfig; - ValveControllerConfig = deviceConfig.ConfigData.Unpack(); - _devicesClient = devicesClient; - _messenger = messenger; - OnRemoveCallback = onRemoveCallback; - EditViewModel = new ValveControllerConfigEditViewModel(_valveControllerClient, _devicesClient, ValveControllerConfig); - } - - public ValveControllerConfig ValveControllerConfig { get; } - - public Func OnRemoveCallback { get; } - - public ValveControllerConfigEditViewModel EditViewModel { get; } - - public Task GetDeviceOperationalStatus() - { - try - { - return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; - } - - catch(RpcException) - { - return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Valve Controller with a name {ValveControllerConfig.Name}" }); - } - } - - public async Task Save() - { - var valveControllerConfig = EditViewModel.Save(); - var updateRequest = new UpdateValveControllerRequest { Id = _deviceConfig.UniqueId, Config = valveControllerConfig }; - await _valveControllerClient.UpdateValveControllersAsync(updateRequest); - } - - public Task Activate() - => _devicesClient.ActivateAsync(new DeviceActivateRequest - { - DeviceId = _deviceConfig.UniqueId - }).ResponseAsync; - - public async Task Remove() - { - await _valveControllerClient.RemoveValveControllerAsync(new ValveControllerRequest { DeviceId = _deviceConfig.UniqueId }); - _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); - await OnRemoveCallback(); - } -} +using Ares.Datamodel.Device; +using Ares.Services.Device; +using CommunityToolkit.Mvvm.Messaging; +using Grpc.Core; +using ReactiveUI; +using UI.Backend.ViewModels.Settings.Device.ValveController; +using UI.Features.Devices.Shared; +using ValveController.Config; +using ValveController.Services; + +namespace UI.Features.Devices.ValveController; + +public class ValveControllerSettingsViewModel : ReactiveObject +{ + private readonly ValveControllerRpc.ValveControllerRpcClient _valveControllerClient; + private readonly DeviceConfig _deviceConfig; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly IMessenger _messenger; + + public ValveControllerSettingsViewModel(DeviceConfig deviceConfig, + ValveControllerRpc.ValveControllerRpcClient valveControllerClient, + AresDevices.AresDevicesClient devicesClient, + IMessenger messenger, + Func onRemoveCallback) + { + _valveControllerClient = valveControllerClient; + _deviceConfig = deviceConfig; + ValveControllerConfig = deviceConfig.ConfigData.Unpack(); + _devicesClient = devicesClient; + _messenger = messenger; + OnRemoveCallback = onRemoveCallback; + EditViewModel = new ValveControllerConfigEditViewModel(_valveControllerClient, _devicesClient, ValveControllerConfig); + } + + public ValveControllerConfig ValveControllerConfig { get; } + + public Func OnRemoveCallback { get; } + + public ValveControllerConfigEditViewModel EditViewModel { get; } + + public Task GetDeviceOperationalStatus() + { + try + { + return _devicesClient.GetDeviceStatusAsync(new DeviceStatusRequest { DeviceId = _deviceConfig.UniqueId }).ResponseAsync; + } + + catch(RpcException) + { + return Task.FromResult(new DeviceOperationalStatus { OperationalState = OperationalState.Error, Message = $"Unable to find a registered Valve Controller with a name {ValveControllerConfig.Name}" }); + } + } + + public async Task Save() + { + var valveControllerConfig = EditViewModel.Save(); + var updateRequest = new UpdateValveControllerRequest { Id = _deviceConfig.UniqueId, Config = valveControllerConfig }; + await _valveControllerClient.UpdateValveControllersAsync(updateRequest); + } + + public Task Activate() + => _devicesClient.ActivateAsync(new DeviceActivateRequest + { + DeviceId = _deviceConfig.UniqueId + }).ResponseAsync; + + public async Task Remove() + { + await _valveControllerClient.RemoveValveControllerAsync(new ValveControllerRequest { DeviceId = _deviceConfig.UniqueId }); + _messenger.Send(new DeviceDeletedMessage(_deviceConfig.UniqueId)); + await OnRemoveCallback(); + } +} diff --git a/UI/Backend/ViewModels/Devices/ValveController/ValveControllerUnitControlViewModel.cs b/UI/Features/Devices/ValveController/ValveControllerUnitControlViewModel.cs similarity index 86% rename from UI/Backend/ViewModels/Devices/ValveController/ValveControllerUnitControlViewModel.cs rename to UI/Features/Devices/ValveController/ValveControllerUnitControlViewModel.cs index baa978d7..7133d285 100644 --- a/UI/Backend/ViewModels/Devices/ValveController/ValveControllerUnitControlViewModel.cs +++ b/UI/Features/Devices/ValveController/ValveControllerUnitControlViewModel.cs @@ -1,35 +1,35 @@ -using UI.Pages.Shared.Devices.ValveController; -using ValveController.Services; - -namespace UI.Backend.ViewModels.Devices.ValveController; - -public class ValveControllerUnitControlViewModel : DeviceUnitControlViewModel -{ - private readonly ValveControllerRpc.ValveControllerRpcClient _client; - - public ValveControllerUnitControlViewModel(string deviceId, string deviceName, ValveControllerRpc.ValveControllerRpcClient client) : base(deviceId, deviceName) - { - _client = client; - ViewType = typeof(ValveControllerWidgetView); - } - - public void EngageRelayOne() - { - _client.EngageRelayOne(new DeviceRequest { DeviceId = DeviceId }); - } - - public void DisengageRelayOne() - { - _client.DisengageRelayOne(new DeviceRequest { DeviceId = DeviceId }); - } - - public void EngageRelayTwo() - { - _client.EngageRelayTwo(new DeviceRequest { DeviceId = DeviceId }); - } - - public void DisengageRelayTwo() - { - _client.DisengageRelayTwo(new DeviceRequest { DeviceId = DeviceId }); - } -} +using UI.Backend.ViewModels; +using ValveController.Services; + +namespace UI.Features.Devices.ValveController; + +public class ValveControllerUnitControlViewModel : DeviceUnitControlViewModel +{ + private readonly ValveControllerRpc.ValveControllerRpcClient _client; + + public ValveControllerUnitControlViewModel(string deviceId, string deviceName, ValveControllerRpc.ValveControllerRpcClient client) : base(deviceId, deviceName) + { + _client = client; + ViewType = typeof(ValveControllerWidgetView); + } + + public void EngageRelayOne() + { + _client.EngageRelayOne(new DeviceRequest { DeviceId = DeviceId }); + } + + public void DisengageRelayOne() + { + _client.DisengageRelayOne(new DeviceRequest { DeviceId = DeviceId }); + } + + public void EngageRelayTwo() + { + _client.EngageRelayTwo(new DeviceRequest { DeviceId = DeviceId }); + } + + public void DisengageRelayTwo() + { + _client.DisengageRelayTwo(new DeviceRequest { DeviceId = DeviceId }); + } +} diff --git a/UI/Pages/Shared/Devices/ValveController/ValveControllerWidgetView.razor b/UI/Features/Devices/ValveController/ValveControllerWidgetView.razor similarity index 92% rename from UI/Pages/Shared/Devices/ValveController/ValveControllerWidgetView.razor rename to UI/Features/Devices/ValveController/ValveControllerWidgetView.razor index f4cd1272..d72b8477 100644 --- a/UI/Pages/Shared/Devices/ValveController/ValveControllerWidgetView.razor +++ b/UI/Features/Devices/ValveController/ValveControllerWidgetView.razor @@ -1,45 +1,44 @@ -@using UI.Backend.ViewModels.Devices.ValveController -@inherits ReactiveUI.Blazor.ReactiveComponentBase -@inject TooltipService tooltipService - - - - Valve Controller - - - - - - - - - - - - - - - - - - - - - - - ViewModel!.DisengageRelayOne()) Icon="lock_open" MouseEnter="@(args => ShowDisengageValveOneTip(args) )" Size="ButtonSize.Large"/> - - - - - - - - - -@code { - void ShowEngageValveOneTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Engage Valve One", options); - void ShowEngageValveTwoTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Engage Valve Two", options); - void ShowDisengageValveOneTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Disengage Valve One", options); - void ShowDisengageValveTwoTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Disengage Valve Two", options); -} +@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inject TooltipService tooltipService + + + + Valve Controller + + + + + + + + + + + + + + + + + + + + + + + ViewModel!.DisengageRelayOne()) Icon="lock_open" MouseEnter="@(args => ShowDisengageValveOneTip(args) )" Size="ButtonSize.Large"/> + + + + + + + + + +@code { + void ShowEngageValveOneTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Engage Valve One", options); + void ShowEngageValveTwoTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Engage Valve Two", options); + void ShowDisengageValveOneTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Disengage Valve One", options); + void ShowDisengageValveTwoTip(ElementReference elementReference, TooltipOptions? options = null) => tooltipService.Open(elementReference, "Disengage Valve Two", options); +} diff --git a/UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserConfigEditView.razor b/UI/Features/Devices/VerdiV6Laser/VerdiLaserConfigEditView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserConfigEditView.razor rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserConfigEditView.razor diff --git a/UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserConfigEditViewModel.cs b/UI/Features/Devices/VerdiV6Laser/VerdiLaserConfigEditViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserConfigEditViewModel.cs rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserConfigEditViewModel.cs diff --git a/UI/Pages/Shared/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor b/UI/Features/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor similarity index 97% rename from UI/Pages/Shared/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor index ccb5463d..1fd5dc14 100644 --- a/UI/Pages/Shared/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserControlWidgetView.razor @@ -1,4 +1,5 @@ @using UI.Backend.ViewModels.Devices.VerdiLaser +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject TooltipService tooltipService @inject IUiNotificationService notificationService diff --git a/UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsList.razor b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsList.razor rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor index 44cb7366..cedfe44e 100644 --- a/UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsList.razor +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/verdilaser" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.VerdiLaser +@using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsListViewModel.cs b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsListViewModel.cs rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsListViewModel.cs index d45a06bb..87a72220 100644 --- a/UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsListViewModel.cs +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsListViewModel.cs @@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.Messaging; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.Devices.VerdiV6Laser; using VerdiV6.Config; using VerdiV6.Services; using VerdiV6Laser; diff --git a/UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsView.razor b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsView.razor similarity index 90% rename from UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsView.razor rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsView.razor index f4014fbe..38f4b0ac 100644 --- a/UI/Pages/Shared/Settings/Device/VerdiV6Laser/VerdiLaserSettingsView.razor +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsView.razor @@ -1,4 +1,5 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Infrastructure.Interfaces +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IUiNotificationService NotificationService diff --git a/UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsViewModel.cs b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsViewModel.cs similarity index 94% rename from UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsViewModel.cs rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsViewModel.cs index d7e015fe..e1088e9b 100644 --- a/UI/Backend/ViewModels/Settings/Device/VerdiLaser/VerdiLaserSettingsViewModel.cs +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsViewModel.cs @@ -3,11 +3,12 @@ using CommunityToolkit.Mvvm.Messaging; using Grpc.Core; using ReactiveUI; -using UI.Backend.Devices; +using UI.Backend.ViewModels.Settings.Device.VerdiLaser; +using UI.Features.Devices.Shared; using VerdiV6.Config; using VerdiV6.Services; -namespace UI.Backend.ViewModels.Settings.Device.VerdiLaser +namespace UI.Features.Devices.VerdiV6Laser { public class VerdiLaserSettingsViewModel : ReactiveObject { @@ -51,10 +52,10 @@ public Task GetDeviceOperationalStatus() public async Task Save() { var laserConfig = EditViewModel.Save(); - var updateRequest = new LaserUpdateRequest - { - Id = _deviceConfig.UniqueId, - Config = laserConfig + var updateRequest = new LaserUpdateRequest + { + Id = _deviceConfig.UniqueId, + Config = laserConfig }; await _laserClient.UpdateLaserAsync(updateRequest); } diff --git a/UI/Backend/ViewModels/Devices/VerdiLaser/VerdiLaserUnitControlViewModel.cs b/UI/Features/Devices/VerdiV6Laser/VerdiLaserUnitControlViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Devices/VerdiLaser/VerdiLaserUnitControlViewModel.cs rename to UI/Features/Devices/VerdiV6Laser/VerdiLaserUnitControlViewModel.cs index 612c87bf..c3193076 100644 --- a/UI/Backend/ViewModels/Devices/VerdiLaser/VerdiLaserUnitControlViewModel.cs +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserUnitControlViewModel.cs @@ -1,6 +1,6 @@ -using DynamicData.Binding; +using DynamicData.Binding; using ReactiveUI.SourceGenerators; -using UI.Pages.Shared.Devices.VerdiV6Laser; +using UI.Features.Devices.VerdiV6Laser; using VerdiV6.Services; namespace UI.Backend.ViewModels.Devices.VerdiLaser; diff --git a/UI/Infrastructure/Interfaces/IUiNotificationService.cs b/UI/Infrastructure/Interfaces/IUiNotificationService.cs index 2555104b..5895569b 100644 --- a/UI/Infrastructure/Interfaces/IUiNotificationService.cs +++ b/UI/Infrastructure/Interfaces/IUiNotificationService.cs @@ -1,4 +1,6 @@ -namespace UI.Services.Notification; +using UI.Services.Notification; + +namespace UI.Infrastructure.Interfaces; public interface IUiNotificationService { diff --git a/UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs b/UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs index 4370b2df..e08a53f0 100644 --- a/UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs +++ b/UI/Infrastructure/Monaco/Interops/CompletionItemExtensions.cs @@ -3,7 +3,7 @@ using AresCompletionItem = Ares.Datamodel.Scripting.CompletionItem; using AresCompletionItemKind = Ares.Datamodel.Scripting.CompletionItemKind; -namespace UI.JsInterops; +namespace UI.Infrastructure.Monaco.Interops; public static class CompletionItemExtensions { diff --git a/UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs index 6756bb72..d3bc33fd 100644 --- a/UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs +++ b/UI/Infrastructure/Monaco/Interops/MonacoCompletionProvider.cs @@ -2,7 +2,7 @@ using Microsoft.JSInterop; using MonacoCompletionItem = BlazorMonaco.Languages.CompletionItem; -namespace UI.JsInterops; +namespace UI.Infrastructure.Monaco.Interops; public class MonacoCompletionProvider(AresScriptingService.AresScriptingServiceClient aresScriptingServiceClient) { diff --git a/UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs index ad6472a3..36da8868 100644 --- a/UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs +++ b/UI/Infrastructure/Monaco/Interops/MonacoDiagnosticsProvider.cs @@ -1,7 +1,7 @@ using Ares.Services; using Microsoft.JSInterop; -namespace UI.JsInterops; +namespace UI.Infrastructure.Monaco.Interops; public sealed class MonacoDiagnosticsProvider(AresScriptingService.AresScriptingServiceClient aresScriptingServiceClient) { diff --git a/UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs index cf91eba8..2ac2d6b0 100644 --- a/UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs +++ b/UI/Infrastructure/Monaco/Interops/MonacoHoverProvider.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; using Antlr4.Runtime; using Ares.Datamodel; using Ares.Datamodel.Extensions; @@ -11,11 +7,10 @@ using AresScript; using AresScript.Generated; using AresScript.Interpreters; -using AresScript.ScriptAnalysis; using Google.Protobuf.WellKnownTypes; using Microsoft.JSInterop; -namespace UI.JsInterops; +namespace UI.Infrastructure.Monaco.Interops; public sealed class MonacoHoverProvider(AresScriptingService.AresScriptingServiceClient aresScriptingServiceClient) { diff --git a/UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs b/UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs index 293375e9..5506f344 100644 --- a/UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs +++ b/UI/Infrastructure/Monaco/Interops/MonacoSemanticTokensProvider.cs @@ -4,7 +4,7 @@ using AresScript.Generated; using Antlr4.Runtime.Misc; -namespace UI.JsInterops; +namespace UI.Infrastructure.Monaco.Interops; public sealed class MonacoSemanticTokensProvider { diff --git a/UI/NotFound.razor b/UI/NotFound.razor index 5532768b..ac101f5c 100644 --- a/UI/NotFound.razor +++ b/UI/NotFound.razor @@ -1,5 +1,7 @@ @page "/404" +@using UI.Components.Layouts @inject NavigationManager Nav +@layout MainLayout Not Found diff --git a/UI/Pages/Automation/CampaignDesigner.razor b/UI/Pages/Automation/CampaignDesigner.razor index 66e749b7..60c91347 100644 --- a/UI/Pages/Automation/CampaignDesigner.razor +++ b/UI/Pages/Automation/CampaignDesigner.razor @@ -1,6 +1,7 @@ @page "/automation/campaigndesigner" @page "/automation/campaigndesigner/{CampaignId:guid}" @using UI.Backend.ViewModels.Automation.CampaignEdit; +@using UI.Infrastructure.Interfaces @using UI.Pages.Shared.CampaignEdit @inject IUiNotificationService NotificationService @inject DialogService DialogService diff --git a/UI/Pages/Automation/Execution.razor b/UI/Pages/Automation/Execution.razor index 83128bc9..353f61ba 100644 --- a/UI/Pages/Automation/Execution.razor +++ b/UI/Pages/Automation/Execution.razor @@ -1,11 +1,12 @@ @page "/automation/execution" @using System.Collections.ObjectModel +@using System.Threading.Tasks @using Ares.Datamodel @using Ares.Services @using Google.Protobuf.WellKnownTypes @using Radzen.Blazor -@using System.Threading.Tasks @using UI.Backend.Extensions +@using UI.Infrastructure.Interfaces @using UI.Pages.Shared.CampaignEdit @using UI.Pages.Shared.Execution.Planning @inject ContextMenuService ContextMenuService diff --git a/UI/Pages/Index.razor b/UI/Pages/Index.razor index 2beeb74d..13fb27b0 100644 --- a/UI/Pages/Index.razor +++ b/UI/Pages/Index.razor @@ -1,21 +1,6 @@ -@page "/" -@using UI.Backend.Devices +@page "/" @using UI.Backend.Repos @using UI.Backend.ViewModels -@using UI.Pages.Shared.Devices.CM3Camera -@using UI.Pages.Shared.Devices.ChemyxPump -@using UI.Pages.Shared.Devices.DataLogger -@using UI.Pages.Shared.Devices.LaserChiller -@using UI.Pages.Shared.Devices.Mfc -@using UI.Pages.Shared.Devices.Remote -@using UI.Pages.Shared.Devices.RestDevice -@using UI.Pages.Shared.Devices.SerialRestDevice -@using UI.Pages.Shared.Devices.SyringePump -@using UI.Pages.Shared.Devices.TubeFurnace -@using UI.Pages.Shared.Devices.StepperController -@using UI.Pages.Shared.Devices.ValveController -@using UI.Pages.Shared.Devices.Servo -@using UI.Pages.Shared.Devices.VerdiV6Laser @using UI.Pages.Shared.Misc @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor b/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor index ce1d5593..7bb4546f 100644 --- a/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor +++ b/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor @@ -1,5 +1,6 @@ @using Ares.Datamodel.Templates @using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Infrastructure.Interfaces @using UI.Pages.Resources @inject IUiNotificationService notificationService; diff --git a/UI/Pages/Shared/ScriptEditor.razor b/UI/Pages/Shared/ScriptEditor.razor index b0b3fc78..a54687f0 100644 --- a/UI/Pages/Shared/ScriptEditor.razor +++ b/UI/Pages/Shared/ScriptEditor.razor @@ -1,4 +1,4 @@ -@using UI.JsInterops +@using UI.Infrastructure.Monaco.Interops @inject IJSRuntime JSRuntime @implements IAsyncDisposable diff --git a/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor b/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor index 0ce41632..514e1183 100644 --- a/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor +++ b/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor @@ -1,4 +1,5 @@ -@inherits LayoutComponentBase +@using UI.Components.Layouts +@inherits LayoutComponentBase @layout SettingsLayout
diff --git a/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor b/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor index 0f20df2b..50e85020 100644 --- a/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor +++ b/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/logging" @using ReactiveUI.Blazor @using UI.Backend.ViewModels.Settings.Logging +@using UI.Components.Layouts @layout SettingsLayout @inherits ReactiveInjectableComponentBase diff --git a/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor b/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor index 0ce41632..514e1183 100644 --- a/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor +++ b/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor @@ -1,4 +1,5 @@ -@inherits LayoutComponentBase +@using UI.Components.Layouts +@inherits LayoutComponentBase @layout SettingsLayout
diff --git a/UI/Program.cs b/UI/Program.cs index cb3aaaed..1616a745 100644 --- a/UI/Program.cs +++ b/UI/Program.cs @@ -4,7 +4,7 @@ using Serilog; using UI; using UI.Backend.Helpers; -using UI.Data; +using UI.Domain; using UI.Services.Grpc; using UI.Services.Notification; using UI.Settings; diff --git a/UI/Routes.razor b/UI/Routes.razor index a63d56df..b49345f5 100644 --- a/UI/Routes.razor +++ b/UI/Routes.razor @@ -1,4 +1,5 @@ - +@using UI.Components.Layouts + diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 69a07bf2..f9615ffa 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -22,8 +22,6 @@ using TicStepperController.Messaging; using TubeFurnace.Messaging; using UI.Authentication; -using UI.Backend.Devices; -using UI.Backend.Factories; using UI.Backend.Helpers; using UI.Backend.Notifications; using UI.Backend.Repos; @@ -32,8 +30,6 @@ using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; using UI.Backend.ViewModels.Automation.Planning; -using UI.Backend.ViewModels.DeviceStateLogging; -using UI.Backend.ViewModels.Factories; using UI.Backend.ViewModels.Misc; using UI.Backend.ViewModels.Settings.Analysis; using UI.Backend.ViewModels.Settings.Device.ChemyxPump; @@ -52,7 +48,21 @@ using UI.Backend.ViewModels.Settings.Device.VerdiLaser; using UI.Backend.ViewModels.Settings.Logging; using UI.Backend.ViewModels.Settings.Planning; -using UI.JsInterops; +using UI.Features.Devices.ChemyxPump; +using UI.Features.Devices.CM3Camera; +using UI.Features.Devices.Mfc; +using UI.Features.Devices.Remote; +using UI.Features.Devices.Servo; +using UI.Features.Devices.Shared; +using UI.Features.Devices.StepperController; +using UI.Features.Devices.SyringePump; +using UI.Features.Devices.Tc0304; +using UI.Features.Devices.TubeFurnace; +using UI.Features.Devices.ValveController; +using UI.Features.DeviceStateExport; +using UI.Features.DeviceStateLogging; +using UI.Infrastructure.Interfaces; +using UI.Infrastructure.Monaco.Interops; using UI.Services.CampaignEdit; using UI.Services.Dialog; using UI.Services.Grpc; diff --git a/UI/ServiceStarter.cs b/UI/ServiceStarter.cs index 19788dc7..3f47c271 100644 --- a/UI/ServiceStarter.cs +++ b/UI/ServiceStarter.cs @@ -1,6 +1,15 @@ -using UI.Backend.Devices; -using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Backend.Repos; +using UI.Features.Devices.ChemyxPump; +using UI.Features.Devices.CM3Camera; +using UI.Features.Devices.Mfc; +using UI.Features.Devices.Remote; +using UI.Features.Devices.Servo; +using UI.Features.Devices.Shared; +using UI.Features.Devices.StepperController; +using UI.Features.Devices.SyringePump; +using UI.Features.Devices.Tc0304; +using UI.Features.Devices.TubeFurnace; +using UI.Features.Devices.ValveController; using UI.Services.Notification; namespace UI; diff --git a/UI/Services/Notification/NotificationReceivingService.cs b/UI/Services/Notification/NotificationReceivingService.cs index 957e420c..f004072e 100644 --- a/UI/Services/Notification/NotificationReceivingService.cs +++ b/UI/Services/Notification/NotificationReceivingService.cs @@ -3,6 +3,7 @@ using Grpc.Core; using NuGet.Packaging; using UI.Backend.Notifications; +using UI.Infrastructure.Interfaces; namespace UI.Services.Notification; diff --git a/UI/Services/Notification/RadzenUiNotificationService.cs b/UI/Services/Notification/RadzenUiNotificationService.cs index 2d25ad06..30e222d8 100644 --- a/UI/Services/Notification/RadzenUiNotificationService.cs +++ b/UI/Services/Notification/RadzenUiNotificationService.cs @@ -1,4 +1,5 @@ using Radzen; +using UI.Infrastructure.Interfaces; namespace UI.Services.Notification; diff --git a/UI/Services/Notification/UiNotificationServiceExtensions.cs b/UI/Services/Notification/UiNotificationServiceExtensions.cs index 2d681250..340d01df 100644 --- a/UI/Services/Notification/UiNotificationServiceExtensions.cs +++ b/UI/Services/Notification/UiNotificationServiceExtensions.cs @@ -1,3 +1,5 @@ +using UI.Infrastructure.Interfaces; + namespace UI.Services.Notification; public static class UiNotificationServiceExtensions diff --git a/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs b/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs index 8fa2d27e..92a346cc 100644 --- a/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs +++ b/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs @@ -1,4 +1,5 @@ using Ares.Services; +using UI.Infrastructure.Interfaces; using UI.Services.Notification; using UI.Services.ServerHealth; diff --git a/UI/UI.csproj b/UI/UI.csproj index 3fd8a6c5..4b91b4b4 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -37,16 +37,13 @@ - - - - - - - - - - + + + + + + + @@ -73,10 +70,6 @@ - - - - diff --git a/UI/_Imports.razor b/UI/_Imports.razor index 5df472fc..1b2ebf59 100644 --- a/UI/_Imports.razor +++ b/UI/_Imports.razor @@ -7,6 +7,8 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using UI +@using UI.Features.Devices.Shared +@using UI.Features.Devices.Shared.Settings @using UI.Pages.Shared @using UI.Services.Notification @using Radzen From b29bb3d05be6cd01e2945b94a0de7f30279c1af7 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 12:24:42 -0500 Subject: [PATCH 03/17] Moved campaign design stuff and auth --- UI/AppSettings.cs | 10 +- UI/{Pages => Features}/Auth/Login.razor | 0 UI/{Pages => Features}/Auth/Logout.razor | 0 UI/{Pages => Features/Auth}/Profile.razor | 49 +- .../Auth}/ProfileViewModel.cs | 12 +- UI/{Pages => Features}/Auth/Register.razor | 0 .../CampaignEdit}/CampaignDesigner.razor | 369 +++++++------ .../AnalyzerInputDesignerVmFactory.cs | 3 +- .../Factories/CloseoutDesignerFactory.cs | 3 +- .../Factories/CommandDesignerFactory.cs | 55 +- .../CommandParameterDesignerFactory.cs | 43 +- .../Factories/ExperimentDesignerFactory.cs | 69 +-- .../Factories/MetadataPickerFactory.cs | 51 +- .../Factories/ParameterEditorFactory.cs | 37 +- .../PlannableParameterDesignerFactory.cs | 33 +- .../Factories/PlanningDesignerFactory.cs | 51 +- .../Factories/StartupDesignerFactory.cs | 3 +- .../Factories/StepDesignerFactory.cs | 39 +- .../ViewModels}/AnalyzerDesignerViewModel.cs | 0 .../ViewModels}/CampaignCloseoutViewModel.cs | 2 +- .../ViewModels}/CampaignDesignerViewModel.cs | 331 ++++++------ .../ViewModels}/CampaignListViewModel.cs | 72 +-- .../ViewModels}/CampaignStartupViewModel.cs | 2 +- .../ViewModels}/CloseoutDesignerViewModel.cs | 3 +- .../ViewModels}/CommandDesignerViewModel.cs | 3 +- .../CommandParameterDesignerViewModel.cs | 2 +- .../ExperimentDesignerViewModel.cs | 277 +++++----- .../ViewModels}/MetadataPickerViewModel.cs | 0 .../ViewModels}/ParameterEditorViewModel.cs | 496 +++++++++--------- .../PlannableParameterDesignerViewModel.cs | 115 ++-- .../PlannerAllocationEditorViewModel.cs | 214 ++++---- .../ViewModels}/PlanningViewModel.cs | 95 ++-- .../ViewModels}/StartupDesignerViewModel.cs | 3 +- .../ViewModels}/StepDesignerViewModel.cs | 247 ++++----- .../CampaignEdit/Views}/CampaignList.razor | 215 ++++---- .../Views}/CloseoutScriptDesigner.razor | 1 + .../CampaignEdit/Views}/CommandDesigner.razor | 0 .../Views}/CommandDesignerQuickView.razor | 210 ++++---- .../Views}/CommandParameterDesigner.razor | 165 +++--- .../CampaignEdit/Views}/CommandViewer.razor | 194 +++---- .../Views}/ExperimentDesigner.razor | 149 +++--- .../Views}/ExperimentViewer.razor | 0 .../CampaignEdit/Views}/MetadataPicker.razor | 0 .../Views}/PlannableParameterDesigner.razor | 43 +- .../Views}/PlannableParameterEditor.razor | 224 ++++---- .../Views}/PlannerAllocationEditor.razor | 112 ++-- .../Views}/StartupScriptDesigner.razor | 1 + .../CampaignEdit/Views}/StepDesigner.razor | 1 + .../Views}/StepPropertyEditor.razor | 78 +-- .../CampaignEdit/Views}/StepViewer.razor | 6 +- .../ExecutionHistory}/ExecutionHistory.razor | 122 ++--- .../ExecutionHistoryViewModel.cs | 66 +-- UI/Pages/Automation/Execution.razor | 1 - UI/Pages/Automation/Planning.razor | 4 +- UI/ServiceCollectionExtensions.cs | 4 +- UI/UI.csproj | 12 +- 56 files changed, 2166 insertions(+), 2131 deletions(-) rename UI/{Pages => Features}/Auth/Login.razor (100%) rename UI/{Pages => Features}/Auth/Logout.razor (100%) rename UI/{Pages => Features/Auth}/Profile.razor (85%) rename UI/{Backend/ViewModels => Features/Auth}/ProfileViewModel.cs (65%) rename UI/{Pages => Features}/Auth/Register.razor (100%) rename UI/{Pages/Automation => Features/CampaignEdit}/CampaignDesigner.razor (96%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs (90%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/CloseoutDesignerFactory.cs (92%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/CommandDesignerFactory.cs (91%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/CommandParameterDesignerFactory.cs (91%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/ExperimentDesignerFactory.cs (92%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/MetadataPickerFactory.cs (85%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/ParameterEditorFactory.cs (86%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs (85%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/PlanningDesignerFactory.cs (89%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/StartupDesignerFactory.cs (92%) rename UI/{Backend/ViewModels/Automation => Features}/CampaignEdit/Factories/StepDesignerFactory.cs (85%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/AnalyzerDesignerViewModel.cs (100%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CampaignCloseoutViewModel.cs (63%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CampaignDesignerViewModel.cs (96%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CampaignListViewModel.cs (92%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CampaignStartupViewModel.cs (63%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CloseoutDesignerViewModel.cs (98%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CommandDesignerViewModel.cs (98%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/CommandParameterDesignerViewModel.cs (98%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/ExperimentDesignerViewModel.cs (95%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/MetadataPickerViewModel.cs (100%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/ParameterEditorViewModel.cs (96%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/PlannableParameterDesignerViewModel.cs (90%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/PlannerAllocationEditorViewModel.cs (97%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/PlanningViewModel.cs (95%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/StartupDesignerViewModel.cs (98%) rename UI/{Backend/ViewModels/Automation/CampaignEdit => Features/CampaignEdit/ViewModels}/StepDesignerViewModel.cs (92%) rename UI/{Pages/Automation => Features/CampaignEdit/Views}/CampaignList.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/CloseoutScriptDesigner.razor (98%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/CommandDesigner.razor (100%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/CommandDesignerQuickView.razor (96%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/CommandParameterDesigner.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/CommandViewer.razor (96%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/ExperimentDesigner.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/ExperimentViewer.razor (100%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/MetadataPicker.razor (100%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/PlannableParameterDesigner.razor (94%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/PlannableParameterEditor.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/PlannerAllocationEditor.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/StartupScriptDesigner.razor (98%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/StepDesigner.razor (99%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/StepPropertyEditor.razor (97%) rename UI/{Pages/Shared/CampaignEdit => Features/CampaignEdit/Views}/StepViewer.razor (99%) rename UI/{Pages/Automation => Features/ExecutionHistory}/ExecutionHistory.razor (97%) rename UI/{Backend/ViewModels/Automation => Features/ExecutionHistory}/ExecutionHistoryViewModel.cs (96%) diff --git a/UI/AppSettings.cs b/UI/AppSettings.cs index 7e671c20..cf3d424d 100644 --- a/UI/AppSettings.cs +++ b/UI/AppSettings.cs @@ -1,8 +1,10 @@ +namespace UI; + public struct AppSettings { - public AppSettings() - { - } + public AppSettings() + { + } - public string DatabaseProvider { get; set; } = string.Empty; + public string DatabaseProvider { get; set; } = string.Empty; } \ No newline at end of file diff --git a/UI/Pages/Auth/Login.razor b/UI/Features/Auth/Login.razor similarity index 100% rename from UI/Pages/Auth/Login.razor rename to UI/Features/Auth/Login.razor diff --git a/UI/Pages/Auth/Logout.razor b/UI/Features/Auth/Logout.razor similarity index 100% rename from UI/Pages/Auth/Logout.razor rename to UI/Features/Auth/Logout.razor diff --git a/UI/Pages/Profile.razor b/UI/Features/Auth/Profile.razor similarity index 85% rename from UI/Pages/Profile.razor rename to UI/Features/Auth/Profile.razor index ea2f1263..ecc149ef 100644 --- a/UI/Pages/Profile.razor +++ b/UI/Features/Auth/Profile.razor @@ -1,25 +1,26 @@ -@page "/profile" -@inject ContextMenuService ContextMenuService -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -Project - - - - -@code { - void ShowContextMenuWithItems(MouseEventArgs args) - { - ContextMenuService.Open(args, - new List { - new(){ Text = "Context menu item 1", Value = 1 }, - new(){ Text = "Context menu item 2", Value = 2 }, - new(){ Text = "Context menu item 3", Value = 3 }, - }, OnMenuItemClick); - } - - void OnMenuItemClick(MenuItemEventArgs args) - { - ContextMenuService.Close(); - } +@page "/profile" +@using UI.Features.Auth +@inject ContextMenuService ContextMenuService +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +Project + + + + +@code { + void ShowContextMenuWithItems(MouseEventArgs args) + { + ContextMenuService.Open(args, + new List { + new(){ Text = "Context menu item 1", Value = 1 }, + new(){ Text = "Context menu item 2", Value = 2 }, + new(){ Text = "Context menu item 3", Value = 3 }, + }, OnMenuItemClick); + } + + void OnMenuItemClick(MenuItemEventArgs args) + { + ContextMenuService.Close(); + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/ProfileViewModel.cs b/UI/Features/Auth/ProfileViewModel.cs similarity index 65% rename from UI/Backend/ViewModels/ProfileViewModel.cs rename to UI/Features/Auth/ProfileViewModel.cs index 27a7cc1f..cfd322c2 100644 --- a/UI/Backend/ViewModels/ProfileViewModel.cs +++ b/UI/Features/Auth/ProfileViewModel.cs @@ -1,7 +1,7 @@ -using ReactiveUI; - -namespace UI.Backend.ViewModels; - -public class ProfileViewModel : ReactiveObject -{ +using ReactiveUI; + +namespace UI.Features.Auth; + +public class ProfileViewModel : ReactiveObject +{ } \ No newline at end of file diff --git a/UI/Pages/Auth/Register.razor b/UI/Features/Auth/Register.razor similarity index 100% rename from UI/Pages/Auth/Register.razor rename to UI/Features/Auth/Register.razor diff --git a/UI/Pages/Automation/CampaignDesigner.razor b/UI/Features/CampaignEdit/CampaignDesigner.razor similarity index 96% rename from UI/Pages/Automation/CampaignDesigner.razor rename to UI/Features/CampaignEdit/CampaignDesigner.razor index 60c91347..50c326c3 100644 --- a/UI/Pages/Automation/CampaignDesigner.razor +++ b/UI/Features/CampaignEdit/CampaignDesigner.razor @@ -1,185 +1,184 @@ -@page "/automation/campaigndesigner" -@page "/automation/campaigndesigner/{CampaignId:guid}" -@using UI.Backend.ViewModels.Automation.CampaignEdit; -@using UI.Infrastructure.Interfaces -@using UI.Pages.Shared.CampaignEdit -@inject IUiNotificationService NotificationService -@inject DialogService DialogService -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -Campaign Designer - -
-
-

@ViewModel!.CampaignName

- -
-
- - - - - - - - - - - - -
-
- @if (SelectedSubOption1) - { -
- -
- } - @if (SelectedSubOption2) - { - - } - @if (SelectedSubOption3) - { - - } - @if (SelectedSubOption4) - { - - } - @if (SelectedSubOption5) - { - - } - @if (SelectedSubOption6) - { - - } -
-
- - -
-
- - -@code { - - [Parameter] - public Guid? CampaignId { get; set; } - - protected override async Task OnInitializedAsync() - { - if (CampaignId.HasValue) - await ViewModel!.SelectCampaignById(CampaignId.Value); - } - - public bool SelectedSubOption1 { get; set; } = true; - public bool SelectedSubOption2 { get; set; } - public bool SelectedSubOption3 { get; set; } - public bool SelectedSubOption4 { get; set; } - public bool SelectedSubOption5 { get; set; } - public bool SelectedSubOption6 { get; set; } - - - - public void SelectParam() - { - SelectedSubOption1 = true; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } - - public void SelectExp() - { - SelectedSubOption2 = true; - SelectedSubOption1 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } - - public void SelectPlan() - { - SelectedSubOption2 = false; - SelectedSubOption1 = false; - SelectedSubOption3 = true; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } - - public void SelectStartup() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = true; - SelectedSubOption5 = false; - SelectedSubOption6 = false; - } - - public void SelectCloseout() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = true; - SelectedSubOption6 = false; - } - - public void SelectAnalyzer() - { - SelectedSubOption1 = false; - SelectedSubOption2 = false; - SelectedSubOption3 = false; - SelectedSubOption4 = false; - SelectedSubOption5 = false; - SelectedSubOption6 = true; - } - - private async Task SaveClick() - { - try - { - await ViewModel!.Update(); - NotificationService.Success("Campaign has been saved"); - } - catch (Exception e) - { - NotificationService.Error($"Failed to save campaign {e.Message}"); - } - } - - private async void TitleClicked(MouseEventArgs obj) - { - string newName = ViewModel!.CampaignName; - var result = await DialogService.OpenAsync("Campaign Name Edit", ds => - @
-
-
- Campaign Name -
- -
-
- - -
-
- ); - - if (result ?? false) - { - ViewModel!.CampaignName = newName; - } - } - -} +@page "/automation/campaigndesigner" +@page "/automation/campaigndesigner/{CampaignId:guid}" +@using UI.Backend.ViewModels.Automation.CampaignEdit; +@using UI.Infrastructure.Interfaces +@inject IUiNotificationService NotificationService +@inject DialogService DialogService +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +Campaign Designer + +
+
+

@ViewModel!.CampaignName

+ +
+
+ + + + + + + + + + + + +
+
+ @if (SelectedSubOption1) + { +
+ +
+ } + @if (SelectedSubOption2) + { + + } + @if (SelectedSubOption3) + { + + } + @if (SelectedSubOption4) + { + + } + @if (SelectedSubOption5) + { + + } + @if (SelectedSubOption6) + { + + } +
+
+ + +
+
+ + +@code { + + [Parameter] + public Guid? CampaignId { get; set; } + + protected override async Task OnInitializedAsync() + { + if (CampaignId.HasValue) + await ViewModel!.SelectCampaignById(CampaignId.Value); + } + + public bool SelectedSubOption1 { get; set; } = true; + public bool SelectedSubOption2 { get; set; } + public bool SelectedSubOption3 { get; set; } + public bool SelectedSubOption4 { get; set; } + public bool SelectedSubOption5 { get; set; } + public bool SelectedSubOption6 { get; set; } + + + + public void SelectParam() + { + SelectedSubOption1 = true; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } + + public void SelectExp() + { + SelectedSubOption2 = true; + SelectedSubOption1 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } + + public void SelectPlan() + { + SelectedSubOption2 = false; + SelectedSubOption1 = false; + SelectedSubOption3 = true; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } + + public void SelectStartup() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = true; + SelectedSubOption5 = false; + SelectedSubOption6 = false; + } + + public void SelectCloseout() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = true; + SelectedSubOption6 = false; + } + + public void SelectAnalyzer() + { + SelectedSubOption1 = false; + SelectedSubOption2 = false; + SelectedSubOption3 = false; + SelectedSubOption4 = false; + SelectedSubOption5 = false; + SelectedSubOption6 = true; + } + + private async Task SaveClick() + { + try + { + await ViewModel!.Update(); + NotificationService.Success("Campaign has been saved"); + } + catch (Exception e) + { + NotificationService.Error($"Failed to save campaign {e.Message}"); + } + } + + private async void TitleClicked(MouseEventArgs obj) + { + string newName = ViewModel!.CampaignName; + var result = await DialogService.OpenAsync("Campaign Name Edit", ds => + @
+
+
+ Campaign Name +
+ +
+
+ + +
+
+ ); + + if (result ?? false) + { + ViewModel!.CampaignName = newName; + } + } + +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs b/UI/Features/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs similarity index 90% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs rename to UI/Features/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs index ea5b104d..d166a5ef 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs +++ b/UI/Features/CampaignEdit/Factories/AnalyzerInputDesignerVmFactory.cs @@ -1,7 +1,8 @@ using Ares.Datamodel.Templates; using Ares.Services; +using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +namespace UI.Features.CampaignEdit.Factories; public class AnalyzerInputDesignerVmFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/CloseoutDesignerFactory.cs similarity index 92% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/CloseoutDesignerFactory.cs index d766e547..086b74d9 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CloseoutDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/CloseoutDesignerFactory.cs @@ -1,8 +1,9 @@ using Ares.Datamodel.Templates; using Ares.Services; using Radzen; +using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +namespace UI.Features.CampaignEdit.Factories; public class CloseoutDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/CommandDesignerFactory.cs similarity index 91% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/CommandDesignerFactory.cs index eef4a2c8..73519dc1 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/CommandDesignerFactory.cs @@ -1,27 +1,28 @@ -using Ares.Datamodel.Templates; -using Ares.Services.Device; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class CommandDesignerFactory -{ - private readonly CommandParameterDesignerFactory _commandParameterDesignerFactory; - private readonly AresDevices.AresDevicesClient _devicesClient; - private readonly MetadataPickerFactory _metadataPickerFactory; - - public CommandDesignerFactory( - MetadataPickerFactory metadataPickerFactory, - CommandParameterDesignerFactory commandParameterDesignerFactory, - AresDevices.AresDevicesClient devicesClient) - { - _metadataPickerFactory = metadataPickerFactory; - _commandParameterDesignerFactory = commandParameterDesignerFactory; - _devicesClient = devicesClient; - } - - public CommandDesignerViewModel Create() - => new CommandDesignerViewModel(_commandParameterDesignerFactory, _metadataPickerFactory, _devicesClient); - - public CommandDesignerViewModel Create(CommandTemplate existingTemplate) - => new CommandDesignerViewModel(existingTemplate, _commandParameterDesignerFactory, _metadataPickerFactory, _devicesClient); -} +using Ares.Datamodel.Templates; +using Ares.Services.Device; +using UI.Backend.ViewModels.Automation.CampaignEdit; + +namespace UI.Features.CampaignEdit.Factories; + +public class CommandDesignerFactory +{ + private readonly CommandParameterDesignerFactory _commandParameterDesignerFactory; + private readonly AresDevices.AresDevicesClient _devicesClient; + private readonly MetadataPickerFactory _metadataPickerFactory; + + public CommandDesignerFactory( + MetadataPickerFactory metadataPickerFactory, + CommandParameterDesignerFactory commandParameterDesignerFactory, + AresDevices.AresDevicesClient devicesClient) + { + _metadataPickerFactory = metadataPickerFactory; + _commandParameterDesignerFactory = commandParameterDesignerFactory; + _devicesClient = devicesClient; + } + + public CommandDesignerViewModel Create() + => new CommandDesignerViewModel(_commandParameterDesignerFactory, _metadataPickerFactory, _devicesClient); + + public CommandDesignerViewModel Create(CommandTemplate existingTemplate) + => new CommandDesignerViewModel(existingTemplate, _commandParameterDesignerFactory, _metadataPickerFactory, _devicesClient); +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs similarity index 91% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs index 247094b0..69bb9cbd 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/CommandParameterDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs @@ -1,21 +1,22 @@ -using Ares.Datamodel.Templates; -using UI.Backend.Helpers; -using UI.Services.CampaignEdit; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class CommandParameterDesignerFactory -{ - private readonly CampaignEditContext _campaignEditContext; - private readonly UnitCategoryHelper _unitCategoryHelper; - - public CommandParameterDesignerFactory(CampaignEditContext campaignEditContext, UnitCategoryHelper unitCategoryHelper) - { - _campaignEditContext = campaignEditContext; - _unitCategoryHelper = unitCategoryHelper; - } - - public CommandParameterDesignerViewModel Create(ParameterMetadata existingParameterMetadata) => new(existingParameterMetadata, _unitCategoryHelper, _campaignEditContext.CurrentlyEditingCampaign?.PlannableParameters); - - public CommandParameterDesignerViewModel Create(Parameter existingParameter) => new(existingParameter, _unitCategoryHelper, _campaignEditContext.CurrentlyEditingCampaign?.PlannableParameters); -} +using Ares.Datamodel.Templates; +using UI.Backend.Helpers; +using UI.Features.CampaignEdit.ViewModels; +using UI.Services.CampaignEdit; + +namespace UI.Features.CampaignEdit.Factories; + +public class CommandParameterDesignerFactory +{ + private readonly CampaignEditContext _campaignEditContext; + private readonly UnitCategoryHelper _unitCategoryHelper; + + public CommandParameterDesignerFactory(CampaignEditContext campaignEditContext, UnitCategoryHelper unitCategoryHelper) + { + _campaignEditContext = campaignEditContext; + _unitCategoryHelper = unitCategoryHelper; + } + + public CommandParameterDesignerViewModel Create(ParameterMetadata existingParameterMetadata) => new(existingParameterMetadata, _unitCategoryHelper, _campaignEditContext.CurrentlyEditingCampaign?.PlannableParameters); + + public CommandParameterDesignerViewModel Create(Parameter existingParameter) => new(existingParameter, _unitCategoryHelper, _campaignEditContext.CurrentlyEditingCampaign?.PlannableParameters); +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/ExperimentDesignerFactory.cs similarity index 92% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/ExperimentDesignerFactory.cs index 7dd56fe1..df567015 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ExperimentDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/ExperimentDesignerFactory.cs @@ -1,34 +1,35 @@ -using Ares.Datamodel.Templates; -using Ares.Services; -using Radzen; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class ExperimentDesignerFactory -{ - private readonly AresAutomation.AresAutomationClient _automationClient; - private readonly StepDesignerFactory _stepDesignerFactory; - private readonly AresValidation.AresValidationClient _validationClient; - private readonly NotificationService _notificationService; - - public ExperimentDesignerFactory(StepDesignerFactory stepDesignerFactory, - AresAutomation.AresAutomationClient automationClient, - AresValidation.AresValidationClient validationClient, - NotificationService notificationService) - { - _stepDesignerFactory = stepDesignerFactory; - _automationClient = automationClient; - _validationClient = validationClient; - _notificationService = notificationService; - } - - public ExperimentDesignerViewModel Create() => new(_stepDesignerFactory, _automationClient, _validationClient, _notificationService); - - public ExperimentDesignerViewModel Create(ExperimentTemplate? existingTemplate) - { - if(existingTemplate is null) - return Create(); - - return new ExperimentDesignerViewModel(existingTemplate, _stepDesignerFactory, _automationClient, _validationClient, _notificationService); - } -} +using Ares.Datamodel.Templates; +using Ares.Services; +using Radzen; +using UI.Backend.ViewModels.Automation.CampaignEdit; + +namespace UI.Features.CampaignEdit.Factories; + +public class ExperimentDesignerFactory +{ + private readonly AresAutomation.AresAutomationClient _automationClient; + private readonly StepDesignerFactory _stepDesignerFactory; + private readonly AresValidation.AresValidationClient _validationClient; + private readonly NotificationService _notificationService; + + public ExperimentDesignerFactory(StepDesignerFactory stepDesignerFactory, + AresAutomation.AresAutomationClient automationClient, + AresValidation.AresValidationClient validationClient, + NotificationService notificationService) + { + _stepDesignerFactory = stepDesignerFactory; + _automationClient = automationClient; + _validationClient = validationClient; + _notificationService = notificationService; + } + + public ExperimentDesignerViewModel Create() => new(_stepDesignerFactory, _automationClient, _validationClient, _notificationService); + + public ExperimentDesignerViewModel Create(ExperimentTemplate? existingTemplate) + { + if(existingTemplate is null) + return Create(); + + return new ExperimentDesignerViewModel(existingTemplate, _stepDesignerFactory, _automationClient, _validationClient, _notificationService); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs b/UI/Features/CampaignEdit/Factories/MetadataPickerFactory.cs similarity index 85% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs rename to UI/Features/CampaignEdit/Factories/MetadataPickerFactory.cs index 38ddb3eb..48234e95 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/MetadataPickerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/MetadataPickerFactory.cs @@ -1,25 +1,26 @@ -using Ares.Datamodel.Templates; -using Ares.Services.Device; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class MetadataPickerFactory -{ - private readonly AresDevices.AresDevicesClient _devicesClient; - - public MetadataPickerFactory(AresDevices.AresDevicesClient devicesClient) - { - _devicesClient = devicesClient; - } - - public MetadataPickerViewModel Create() - => new MetadataPickerViewModel(_devicesClient); - - public MetadataPickerViewModel Create(CommandMetadata? existingMetadata) - { - if (existingMetadata is null) - return Create(); - - return new MetadataPickerViewModel(existingMetadata, _devicesClient); - } -} +using Ares.Datamodel.Templates; +using Ares.Services.Device; +using UI.Backend.ViewModels.Automation.CampaignEdit; + +namespace UI.Features.CampaignEdit.Factories; + +public class MetadataPickerFactory +{ + private readonly AresDevices.AresDevicesClient _devicesClient; + + public MetadataPickerFactory(AresDevices.AresDevicesClient devicesClient) + { + _devicesClient = devicesClient; + } + + public MetadataPickerViewModel Create() + => new MetadataPickerViewModel(_devicesClient); + + public MetadataPickerViewModel Create(CommandMetadata? existingMetadata) + { + if (existingMetadata is null) + return Create(); + + return new MetadataPickerViewModel(existingMetadata, _devicesClient); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs b/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs similarity index 86% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs rename to UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs index 0f2d0217..c7788229 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/ParameterEditorFactory.cs +++ b/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs @@ -1,18 +1,19 @@ -using Ares.Datamodel.Templates; -using UI.Backend.Helpers; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class ParameterEditorFactory -{ - private readonly UnitCategoryHelper _unitCategoryHelper; - - public ParameterEditorFactory(UnitCategoryHelper unitCategoryHelper) - { - _unitCategoryHelper = unitCategoryHelper; - } - - public ParameterEditorViewModel Create(IEnumerable availableOutputs) => new(_unitCategoryHelper, availableOutputs); - - public ParameterEditorViewModel Create(ParameterMetadata existingParameterMetadata, IEnumerable availableOutputs) => new(existingParameterMetadata, availableOutputs, _unitCategoryHelper); -} +using Ares.Datamodel.Templates; +using UI.Backend.Helpers; +using UI.Backend.ViewModels.Automation.CampaignEdit; + +namespace UI.Features.CampaignEdit.Factories; + +public class ParameterEditorFactory +{ + private readonly UnitCategoryHelper _unitCategoryHelper; + + public ParameterEditorFactory(UnitCategoryHelper unitCategoryHelper) + { + _unitCategoryHelper = unitCategoryHelper; + } + + public ParameterEditorViewModel Create(IEnumerable availableOutputs) => new(_unitCategoryHelper, availableOutputs); + + public ParameterEditorViewModel Create(ParameterMetadata existingParameterMetadata, IEnumerable availableOutputs) => new(existingParameterMetadata, availableOutputs, _unitCategoryHelper); +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs similarity index 85% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs index c02026c2..a94907ad 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/PlannableParameterDesignerFactory.cs @@ -1,16 +1,17 @@ -using Ares.Datamodel.Templates; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class PlannableParameterDesignerFactory -{ - private readonly ParameterEditorFactory _parameterEditorFactory; - - public PlannableParameterDesignerFactory(ParameterEditorFactory parameterEditorFactory) - { - _parameterEditorFactory = parameterEditorFactory; - } - - public PlannableParameterDesignerViewModel Create(IEnumerable existingParameterMeta, ExperimentTemplate? experimentTemplate) - => new(existingParameterMeta, experimentTemplate, _parameterEditorFactory); -} +using Ares.Datamodel.Templates; +using UI.Features.CampaignEdit.ViewModels; + +namespace UI.Features.CampaignEdit.Factories; + +public class PlannableParameterDesignerFactory +{ + private readonly ParameterEditorFactory _parameterEditorFactory; + + public PlannableParameterDesignerFactory(ParameterEditorFactory parameterEditorFactory) + { + _parameterEditorFactory = parameterEditorFactory; + } + + public PlannableParameterDesignerViewModel Create(IEnumerable existingParameterMeta, ExperimentTemplate? experimentTemplate) + => new(existingParameterMeta, experimentTemplate, _parameterEditorFactory); +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs similarity index 89% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs index 2c0e3dd5..068a88be 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/PlanningDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs @@ -1,25 +1,26 @@ -using Ares.Datamodel.Templates; -using Ares.Services; -using Google.Protobuf.WellKnownTypes; -using UI.Services.Notification; - - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class PlanningDesignerFactory -{ - private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; - private readonly INotificationReceivingService _notificationService; - - public PlanningDesignerFactory(AresPlannerManagementService.AresPlannerManagementServiceClient client, INotificationReceivingService notificationService) - { - _client = client; - _notificationService = notificationService; - } - - public async Task Create(CampaignTemplate template) - { - var planners = await _client.GetAllPlannersAsync(new Empty()); - return new PlanningViewModel(template, planners.Planners, _client, _notificationService); - } -} +using Ares.Datamodel.Templates; +using Ares.Services; +using Google.Protobuf.WellKnownTypes; +using UI.Features.CampaignEdit.ViewModels; +using UI.Services.Notification; + + +namespace UI.Features.CampaignEdit.Factories; + +public class PlanningDesignerFactory +{ + private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; + private readonly INotificationReceivingService _notificationService; + + public PlanningDesignerFactory(AresPlannerManagementService.AresPlannerManagementServiceClient client, INotificationReceivingService notificationService) + { + _client = client; + _notificationService = notificationService; + } + + public async Task Create(CampaignTemplate template) + { + var planners = await _client.GetAllPlannersAsync(new Empty()); + return new PlanningViewModel(template, planners.Planners, _client, _notificationService); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/StartupDesignerFactory.cs similarity index 92% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/StartupDesignerFactory.cs index 003d480b..5e021642 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StartupDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/StartupDesignerFactory.cs @@ -1,8 +1,9 @@ using Ares.Datamodel.Templates; using Ares.Services; using Radzen; +using UI.Backend.ViewModels.Automation.CampaignEdit; -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +namespace UI.Features.CampaignEdit.Factories; public class StartupDesignerFactory { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/StepDesignerFactory.cs similarity index 85% rename from UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs rename to UI/Features/CampaignEdit/Factories/StepDesignerFactory.cs index 4a6d7132..3ff0e793 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/Factories/StepDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/StepDesignerFactory.cs @@ -1,19 +1,20 @@ -using Ares.Datamodel.Templates; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -public class StepDesignerFactory -{ - private readonly CommandDesignerFactory _commandDesignerFactory; - - public StepDesignerFactory(CommandDesignerFactory commandDesignerFactory) - { - _commandDesignerFactory = commandDesignerFactory; - } - - public StepDesignerViewModel Create() - => new StepDesignerViewModel(_commandDesignerFactory); - - public StepDesignerViewModel Create(StepTemplate existingTemplate) - => new StepDesignerViewModel(existingTemplate, _commandDesignerFactory); -} +using Ares.Datamodel.Templates; +using UI.Features.CampaignEdit.ViewModels; + +namespace UI.Features.CampaignEdit.Factories; + +public class StepDesignerFactory +{ + private readonly CommandDesignerFactory _commandDesignerFactory; + + public StepDesignerFactory(CommandDesignerFactory commandDesignerFactory) + { + _commandDesignerFactory = commandDesignerFactory; + } + + public StepDesignerViewModel Create() + => new StepDesignerViewModel(_commandDesignerFactory); + + public StepDesignerViewModel Create(StepTemplate existingTemplate) + => new StepDesignerViewModel(existingTemplate, _commandDesignerFactory); +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/AnalyzerDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Automation/CampaignEdit/AnalyzerDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/AnalyzerDesignerViewModel.cs diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignCloseoutViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignCloseoutViewModel.cs similarity index 63% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CampaignCloseoutViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CampaignCloseoutViewModel.cs index b4fd1729..a7347117 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignCloseoutViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignCloseoutViewModel.cs @@ -1,6 +1,6 @@ using ReactiveUI; -namespace UI.Backend.ViewModels.Automation.CampaignEdit +namespace UI.Features.CampaignEdit.ViewModels { public class CampaignCloseoutViewModel : ReactiveObject { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs index 470d0673..28654e0a 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs @@ -1,165 +1,166 @@ -using Ares.Datamodel.Templates; -using Ares.Services; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; -using UI.Services.CampaignEdit; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public partial class CampaignDesignerViewModel : ReactiveObject -{ - private readonly AresAutomation.AresAutomationClient _automationClient; - private readonly CampaignEditContext _editContext; - private readonly ExperimentDesignerFactory _experimentDesignerFactory; - private readonly StartupDesignerFactory _startupDesignerFactory; - private readonly CloseoutDesignerFactory _closeoutDesignerFactory; - private readonly PlannableParameterDesignerFactory _plannableParameterDesignerFactory; - private readonly PlanningDesignerFactory _planningDesignerFactory; - private CampaignTemplate _campaignTemplate = null!; - readonly AnalyzerInputDesignerVmFactory _analyzerInputDesignerFactory; - - public CampaignDesignerViewModel( - AresAutomation.AresAutomationClient automationClient, - ExperimentDesignerFactory experimentDesignerFactory, - StartupDesignerFactory startupDesignerFactory, - CloseoutDesignerFactory closeoutDesignerFactory, - PlanningDesignerFactory planningDesignerFactory, - AnalyzerInputDesignerVmFactory analyzingDesignerFactory, - PlannableParameterDesignerFactory plannableParameterDesignerFactory, - CampaignEditContext editContext, - IConfiguration configuration) - { - _analyzerInputDesignerFactory = analyzingDesignerFactory; - _automationClient = automationClient; - _experimentDesignerFactory = experimentDesignerFactory; - _startupDesignerFactory = startupDesignerFactory; - _closeoutDesignerFactory = closeoutDesignerFactory; - _planningDesignerFactory = planningDesignerFactory; - _plannableParameterDesignerFactory = plannableParameterDesignerFactory; - _editContext = editContext; - IsCreatingCampaign = false; - IsNotCreatingCampaign = true; - Placeholder = "New Campaign 1"; - - CampaignTemplate = new CampaignTemplate - { - Name = Placeholder, - UniqueId = Guid.NewGuid().ToString(), - ExperimentTemplate = new ExperimentTemplate() { UniqueId = Guid.NewGuid().ToString(), Name = "New Experiment" } - }; - } - - [Reactive] public partial bool IsCreatingCampaign { get; set; } - - [Reactive] public partial bool IsNotCreatingCampaign { get; set; } - - [Reactive] public partial string Placeholder { get; set; } - - public PlannableParameterDesignerViewModel? PlannableParameterDesigner { get; private set; } - - public ExperimentDesignerViewModel? ExperimentDesigner { get; private set; } - - public StartupDesignerViewModel? StartupDesigner { get; private set; } - - public CloseoutDesignerViewModel? CloseoutDesigner { get; private set; } - - public PlanningViewModel? PlanningDesigner { get; private set; } - - public AnalyzerDesignerViewModel? AnalyzerDesignerViewModel { get; private set; } - - public string CampaignName { get; set; } = "Unnamed Campaign"; - - public CampaignTemplate CampaignTemplate - { - private get => _campaignTemplate; - - set - { - _campaignTemplate = value; - _editContext.CurrentlyEditingCampaign = value; - _ = Init(value); - } - } - - [Reactive] public partial bool CreationIsErrorFree { get; set; } - - [Reactive] public partial string? CreationErrorText { get; set; } - - private async Task Init(CampaignTemplate campaignTemplate) - { - CampaignName = campaignTemplate.Name; - PlannableParameterDesigner = _plannableParameterDesignerFactory.Create(campaignTemplate.PlannableParameters, campaignTemplate.ExperimentTemplate); - ExperimentDesigner = _experimentDesignerFactory.Create(campaignTemplate.ExperimentTemplate); - StartupDesigner = _startupDesignerFactory.Create(campaignTemplate.StartupTemplate); - CloseoutDesigner = _closeoutDesignerFactory.Create(campaignTemplate.CloseoutTemplate); - PlanningDesigner = await _planningDesignerFactory.Create(campaignTemplate); - var commandDesigners = ExperimentDesigner?.StepDesigners?.SelectMany(sd => sd.CommandDesigners) ?? []; - var startupDesigners = StartupDesigner?.StartupStepDesigners?.SelectMany(ssd => ssd.CommandDesigners) ?? []; - - if(CampaignTemplate.ExperimentTemplate is not null) - { - AnalyzerDesignerViewModel = _analyzerInputDesignerFactory.Create(campaignTemplate.ExperimentTemplate, commandDesigners, startupDesigners); - } - } - - public CampaignTemplate Save() - { - CampaignTemplate.Name = CampaignName; - CampaignTemplate.PlannableParameters.Clear(); - CampaignTemplate.PlannableParameters.AddRange(PlannableParameterDesigner?.Save() ?? Array.Empty()); - CampaignTemplate.ExperimentTemplate = null; - - if(StartupDesigner is not null) - CampaignTemplate.StartupTemplate = StartupDesigner.Save(); - - if(ExperimentDesigner is not null) - CampaignTemplate.ExperimentTemplate = ExperimentDesigner.Save(); - - if(CloseoutDesigner is not null) - CampaignTemplate.CloseoutTemplate = CloseoutDesigner.Save(); - - //Try Save Instead? - PlannableParameterDesigner?.Save(); - PlanningDesigner?.Save(); - AnalyzerDesignerViewModel?.Save(); - return CampaignTemplate; - } - - public async Task SelectCampaignById(Guid campaignId) - { - var request = new CampaignRequest - { - UniqueId = campaignId.ToString(), - }; - - CampaignTemplate = await _automationClient.GetSingleCampaignAsync(request); - } - - public async Task Update() - { - if(string.IsNullOrEmpty(CampaignName)) - CampaignName = Placeholder; - - var isUpdating = await _automationClient.CampaignExistsAsync(new CampaignRequest { UniqueId = CampaignTemplate.UniqueId }); - - var nameChanged = CampaignTemplate.Name != CampaignName; - if(nameChanged) - { - var campaignExists = await _automationClient.CampaignExistsAsync(new CampaignRequest { CampaignName = CampaignName }); - if(campaignExists.Value) - { - CreationErrorText = "Campaign Name " + CampaignName + " Already Exists!"; - CreationIsErrorFree = false; - return; - } - } - - var template = Save(); - if(isUpdating.Value) - await _automationClient.UpdateCampaignAsync(new AddOrUpdateCampaignRequest() { Template = template }); - - else - await _automationClient.AddCampaignAsync(new AddOrUpdateCampaignRequest() { Template = template }); - } -} +using Ares.Datamodel.Templates; +using Ares.Services; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; +using UI.Services.CampaignEdit; + +namespace UI.Backend.ViewModels.Automation.CampaignEdit; + +public partial class CampaignDesignerViewModel : ReactiveObject +{ + private readonly AresAutomation.AresAutomationClient _automationClient; + private readonly CampaignEditContext _editContext; + private readonly ExperimentDesignerFactory _experimentDesignerFactory; + private readonly StartupDesignerFactory _startupDesignerFactory; + private readonly CloseoutDesignerFactory _closeoutDesignerFactory; + private readonly PlannableParameterDesignerFactory _plannableParameterDesignerFactory; + private readonly PlanningDesignerFactory _planningDesignerFactory; + private CampaignTemplate _campaignTemplate = null!; + readonly AnalyzerInputDesignerVmFactory _analyzerInputDesignerFactory; + + public CampaignDesignerViewModel( + AresAutomation.AresAutomationClient automationClient, + ExperimentDesignerFactory experimentDesignerFactory, + StartupDesignerFactory startupDesignerFactory, + CloseoutDesignerFactory closeoutDesignerFactory, + PlanningDesignerFactory planningDesignerFactory, + AnalyzerInputDesignerVmFactory analyzingDesignerFactory, + PlannableParameterDesignerFactory plannableParameterDesignerFactory, + CampaignEditContext editContext, + IConfiguration configuration) + { + _analyzerInputDesignerFactory = analyzingDesignerFactory; + _automationClient = automationClient; + _experimentDesignerFactory = experimentDesignerFactory; + _startupDesignerFactory = startupDesignerFactory; + _closeoutDesignerFactory = closeoutDesignerFactory; + _planningDesignerFactory = planningDesignerFactory; + _plannableParameterDesignerFactory = plannableParameterDesignerFactory; + _editContext = editContext; + IsCreatingCampaign = false; + IsNotCreatingCampaign = true; + Placeholder = "New Campaign 1"; + + CampaignTemplate = new CampaignTemplate + { + Name = Placeholder, + UniqueId = Guid.NewGuid().ToString(), + ExperimentTemplate = new ExperimentTemplate() { UniqueId = Guid.NewGuid().ToString(), Name = "New Experiment" } + }; + } + + [Reactive] public partial bool IsCreatingCampaign { get; set; } + + [Reactive] public partial bool IsNotCreatingCampaign { get; set; } + + [Reactive] public partial string Placeholder { get; set; } + + public PlannableParameterDesignerViewModel? PlannableParameterDesigner { get; private set; } + + public ExperimentDesignerViewModel? ExperimentDesigner { get; private set; } + + public StartupDesignerViewModel? StartupDesigner { get; private set; } + + public CloseoutDesignerViewModel? CloseoutDesigner { get; private set; } + + public PlanningViewModel? PlanningDesigner { get; private set; } + + public AnalyzerDesignerViewModel? AnalyzerDesignerViewModel { get; private set; } + + public string CampaignName { get; set; } = "Unnamed Campaign"; + + public CampaignTemplate CampaignTemplate + { + private get => _campaignTemplate; + + set + { + _campaignTemplate = value; + _editContext.CurrentlyEditingCampaign = value; + _ = Init(value); + } + } + + [Reactive] public partial bool CreationIsErrorFree { get; set; } + + [Reactive] public partial string? CreationErrorText { get; set; } + + private async Task Init(CampaignTemplate campaignTemplate) + { + CampaignName = campaignTemplate.Name; + PlannableParameterDesigner = _plannableParameterDesignerFactory.Create(campaignTemplate.PlannableParameters, campaignTemplate.ExperimentTemplate); + ExperimentDesigner = _experimentDesignerFactory.Create(campaignTemplate.ExperimentTemplate); + StartupDesigner = _startupDesignerFactory.Create(campaignTemplate.StartupTemplate); + CloseoutDesigner = _closeoutDesignerFactory.Create(campaignTemplate.CloseoutTemplate); + PlanningDesigner = await _planningDesignerFactory.Create(campaignTemplate); + var commandDesigners = ExperimentDesigner?.StepDesigners?.SelectMany(sd => sd.CommandDesigners) ?? []; + var startupDesigners = StartupDesigner?.StartupStepDesigners?.SelectMany(ssd => ssd.CommandDesigners) ?? []; + + if(CampaignTemplate.ExperimentTemplate is not null) + { + AnalyzerDesignerViewModel = _analyzerInputDesignerFactory.Create(campaignTemplate.ExperimentTemplate, commandDesigners, startupDesigners); + } + } + + public CampaignTemplate Save() + { + CampaignTemplate.Name = CampaignName; + CampaignTemplate.PlannableParameters.Clear(); + CampaignTemplate.PlannableParameters.AddRange(PlannableParameterDesigner?.Save() ?? Array.Empty()); + CampaignTemplate.ExperimentTemplate = null; + + if(StartupDesigner is not null) + CampaignTemplate.StartupTemplate = StartupDesigner.Save(); + + if(ExperimentDesigner is not null) + CampaignTemplate.ExperimentTemplate = ExperimentDesigner.Save(); + + if(CloseoutDesigner is not null) + CampaignTemplate.CloseoutTemplate = CloseoutDesigner.Save(); + + //Try Save Instead? + PlannableParameterDesigner?.Save(); + PlanningDesigner?.Save(); + AnalyzerDesignerViewModel?.Save(); + return CampaignTemplate; + } + + public async Task SelectCampaignById(Guid campaignId) + { + var request = new CampaignRequest + { + UniqueId = campaignId.ToString(), + }; + + CampaignTemplate = await _automationClient.GetSingleCampaignAsync(request); + } + + public async Task Update() + { + if(string.IsNullOrEmpty(CampaignName)) + CampaignName = Placeholder; + + var isUpdating = await _automationClient.CampaignExistsAsync(new CampaignRequest { UniqueId = CampaignTemplate.UniqueId }); + + var nameChanged = CampaignTemplate.Name != CampaignName; + if(nameChanged) + { + var campaignExists = await _automationClient.CampaignExistsAsync(new CampaignRequest { CampaignName = CampaignName }); + if(campaignExists.Value) + { + CreationErrorText = "Campaign Name " + CampaignName + " Already Exists!"; + CreationIsErrorFree = false; + return; + } + } + + var template = Save(); + if(isUpdating.Value) + await _automationClient.UpdateCampaignAsync(new AddOrUpdateCampaignRequest() { Template = template }); + + else + await _automationClient.AddCampaignAsync(new AddOrUpdateCampaignRequest() { Template = template }); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignListViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignListViewModel.cs similarity index 92% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CampaignListViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CampaignListViewModel.cs index 61e8d2b9..0abadf7c 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignListViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignListViewModel.cs @@ -1,36 +1,36 @@ -using DynamicData; -using ReactiveUI; -using System.Collections.ObjectModel; -using Ares.Datamodel.Templates; -using Ares.Services; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public class CampaignListViewModel : ReactiveObject -{ - private readonly AresAutomation.AresAutomationClient _automationClient; - public readonly ObservableCollection Templates = []; - - public CampaignListViewModel(AresAutomation.AresAutomationClient automationClient) - { - _automationClient = automationClient; - } - - public async Task GetFullCampaignTemplate(string campaignId) - { - return await _automationClient.GetSingleCampaignAsync(new CampaignRequest { UniqueId = campaignId }); - } - - public async Task RefreshCampaigns() - { - var campaigns = await _automationClient.GetAllCampaignsAsync(new GetAllCampaignsRequest()); - Templates.Clear(); - Templates.AddRange(campaigns.Campaigns); - } - - public async Task DeleteCampaign(Guid campaignId) - { - await _automationClient.RemoveCampaignAsync(new CampaignRequest { UniqueId = campaignId.ToString() }); - await RefreshCampaigns(); - } -} +using DynamicData; +using ReactiveUI; +using System.Collections.ObjectModel; +using Ares.Datamodel.Templates; +using Ares.Services; + +namespace UI.Features.CampaignEdit.ViewModels; + +public class CampaignListViewModel : ReactiveObject +{ + private readonly AresAutomation.AresAutomationClient _automationClient; + public readonly ObservableCollection Templates = []; + + public CampaignListViewModel(AresAutomation.AresAutomationClient automationClient) + { + _automationClient = automationClient; + } + + public async Task GetFullCampaignTemplate(string campaignId) + { + return await _automationClient.GetSingleCampaignAsync(new CampaignRequest { UniqueId = campaignId }); + } + + public async Task RefreshCampaigns() + { + var campaigns = await _automationClient.GetAllCampaignsAsync(new GetAllCampaignsRequest()); + Templates.Clear(); + Templates.AddRange(campaigns.Campaigns); + } + + public async Task DeleteCampaign(Guid campaignId) + { + await _automationClient.RemoveCampaignAsync(new CampaignRequest { UniqueId = campaignId.ToString() }); + await RefreshCampaigns(); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignStartupViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignStartupViewModel.cs similarity index 63% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CampaignStartupViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CampaignStartupViewModel.cs index 40dc21cb..de857ad6 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CampaignStartupViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignStartupViewModel.cs @@ -1,6 +1,6 @@ using ReactiveUI; -namespace UI.Backend.ViewModels.Automation.CampaignEdit +namespace UI.Features.CampaignEdit.ViewModels { public class CampaignStartupViewModel : ReactiveObject { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CloseoutDesignerViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CloseoutDesignerViewModel.cs index 43ffc9c3..78d830a2 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CloseoutDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CloseoutDesignerViewModel.cs @@ -3,7 +3,8 @@ using Radzen; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CommandDesignerViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CommandDesignerViewModel.cs index 8dee55ef..fec1b22e 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CommandDesignerViewModel.cs @@ -3,7 +3,8 @@ using Ares.Services.Device; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandParameterDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Automation/CampaignEdit/CommandParameterDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs index 0616b9c2..81853842 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/CommandParameterDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs @@ -3,7 +3,7 @@ using ReactiveUI; using UI.Backend.Helpers; -namespace UI.Backend.ViewModels.Automation.CampaignEdit; +namespace UI.Features.CampaignEdit.ViewModels; public class CommandParameterDesignerViewModel : ReactiveObject { diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/ExperimentDesignerViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/ExperimentDesignerViewModel.cs index 10f05e0a..1c4c8c9e 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/ExperimentDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/ExperimentDesignerViewModel.cs @@ -1,138 +1,139 @@ -using Ares.Datamodel.Templates; -using Ares.Services; -using Radzen; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public partial class ExperimentDesignerViewModel : ReactiveObject -{ - private readonly StepDesignerFactory _stepDesignerFactory; - private readonly AresValidation.AresValidationClient _validationClient; - private ExperimentTemplate _experimentTemplate = null!; - readonly AresAutomation.AresAutomationClient _automationClient; - private readonly NotificationService _notificationService; - - public ExperimentDesignerViewModel(StepDesignerFactory stepDesignerFactory, - AresAutomation.AresAutomationClient automationClient, - AresValidation.AresValidationClient validationClient, - NotificationService notificationService) - { - _automationClient = automationClient; - _stepDesignerFactory = stepDesignerFactory; - _validationClient = validationClient; - _notificationService = notificationService; - Name = "Unnamed Template"; - ExperimentTemplate = new ExperimentTemplate - { - UniqueId = Guid.NewGuid().ToString(), - Name = "Experiment Template" - }; - } - - public ExperimentDesignerViewModel(ExperimentTemplate existingTemplate, - StepDesignerFactory stepDesignerFactory, - AresAutomation.AresAutomationClient automationClient, - AresValidation.AresValidationClient validationClient, - NotificationService notificationService) : this(stepDesignerFactory, automationClient, validationClient, notificationService) - { - ExperimentTemplate = existingTemplate; - } - - private void Init(ExperimentTemplate existingTemplate) - { - Name = existingTemplate.Name; - StepDesigners = existingTemplate.StepTemplates.Select(template => _stepDesignerFactory.Create(template)).OrderBy(model => model.Index).ToList(); - if(existingTemplate.StepTemplates.Select(step => step.CommandTemplates.Select(cmd => cmd.UserOutputKeyMap)).Any()) - { - var commandDesigners = StepDesigners.SelectMany(model => model.CommandDesigners).Where(model => model.CommandTemplate.UserOutputKeyMap.Any()); - foreach(var designer in commandDesigners) - { - designer.OutputProvider = true; - } - - ExperimentOutputProviderCommand = commandDesigners.Select(designer => designer.CommandTemplate); - } - } - - public ExperimentTemplate Save() - { - if(StepDesigners is null) - { - _notificationService.Notify(NotificationSeverity.Error, "A Step Designer was null! No data saved."); - return ExperimentTemplate; - } - - ExperimentTemplate.Name = Name; - ExperimentTemplate.StepTemplates.Clear(); - ExperimentTemplate.StepTemplates.AddRange(StepDesigners.Select(designer => designer.Save())); - return ExperimentTemplate; - } - - public StepDesignerViewModel AddStep() - { - var stepDesigner = _stepDesignerFactory.Create(); - stepDesigner.Index = StepDesigners.Count; - StepDesigners.Add(stepDesigner); - return stepDesigner; - } - - public void RemoveStep(StepDesignerViewModel vm) - { - if(StepDesigners is not null) - { - StepDesigners.Remove(vm); - ReindexSteps(); - } - } - - public void MoveStepDesignerUp(StepDesignerViewModel vm) - { - if(StepDesigners is null || vm.Index == 0) - return; - - StepDesigners.RemoveAt(vm.Index); - StepDesigners.Insert(vm.Index - 1, vm); - ReindexSteps(); - } - - public void MoveStepDesignerDown(StepDesignerViewModel vm) - { - if(StepDesigners is null || vm.Index == StepDesigners.Count - 1) - return; - - StepDesigners.RemoveAt(vm.Index); - StepDesigners.Insert(vm.Index + 1, vm); - ReindexSteps(); - } - - private void ReindexSteps() - { - if(StepDesigners is not null) - { - var idx = 0; - foreach(var stepDesigner in StepDesigners) - stepDesigner.Index = idx++; - } - } - - public ExperimentTemplate ExperimentTemplate - { - private get => _experimentTemplate; - - set - { - _experimentTemplate = value; - Init(value); - } - } - - [Reactive] - public partial string Name { get; set; } - - public IList StepDesigners { get; private set; } = []; - - public IEnumerable? ExperimentOutputProviderCommand { get; set; } -} +using Ares.Datamodel.Templates; +using Ares.Services; +using Radzen; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; + +namespace UI.Backend.ViewModels.Automation.CampaignEdit; + +public partial class ExperimentDesignerViewModel : ReactiveObject +{ + private readonly StepDesignerFactory _stepDesignerFactory; + private readonly AresValidation.AresValidationClient _validationClient; + private ExperimentTemplate _experimentTemplate = null!; + readonly AresAutomation.AresAutomationClient _automationClient; + private readonly NotificationService _notificationService; + + public ExperimentDesignerViewModel(StepDesignerFactory stepDesignerFactory, + AresAutomation.AresAutomationClient automationClient, + AresValidation.AresValidationClient validationClient, + NotificationService notificationService) + { + _automationClient = automationClient; + _stepDesignerFactory = stepDesignerFactory; + _validationClient = validationClient; + _notificationService = notificationService; + Name = "Unnamed Template"; + ExperimentTemplate = new ExperimentTemplate + { + UniqueId = Guid.NewGuid().ToString(), + Name = "Experiment Template" + }; + } + + public ExperimentDesignerViewModel(ExperimentTemplate existingTemplate, + StepDesignerFactory stepDesignerFactory, + AresAutomation.AresAutomationClient automationClient, + AresValidation.AresValidationClient validationClient, + NotificationService notificationService) : this(stepDesignerFactory, automationClient, validationClient, notificationService) + { + ExperimentTemplate = existingTemplate; + } + + private void Init(ExperimentTemplate existingTemplate) + { + Name = existingTemplate.Name; + StepDesigners = existingTemplate.StepTemplates.Select(template => _stepDesignerFactory.Create(template)).OrderBy(model => model.Index).ToList(); + if(existingTemplate.StepTemplates.Select(step => step.CommandTemplates.Select(cmd => cmd.UserOutputKeyMap)).Any()) + { + var commandDesigners = StepDesigners.SelectMany(model => model.CommandDesigners).Where(model => model.CommandTemplate.UserOutputKeyMap.Any()); + foreach(var designer in commandDesigners) + { + designer.OutputProvider = true; + } + + ExperimentOutputProviderCommand = commandDesigners.Select(designer => designer.CommandTemplate); + } + } + + public ExperimentTemplate Save() + { + if(StepDesigners is null) + { + _notificationService.Notify(NotificationSeverity.Error, "A Step Designer was null! No data saved."); + return ExperimentTemplate; + } + + ExperimentTemplate.Name = Name; + ExperimentTemplate.StepTemplates.Clear(); + ExperimentTemplate.StepTemplates.AddRange(StepDesigners.Select(designer => designer.Save())); + return ExperimentTemplate; + } + + public StepDesignerViewModel AddStep() + { + var stepDesigner = _stepDesignerFactory.Create(); + stepDesigner.Index = StepDesigners.Count; + StepDesigners.Add(stepDesigner); + return stepDesigner; + } + + public void RemoveStep(StepDesignerViewModel vm) + { + if(StepDesigners is not null) + { + StepDesigners.Remove(vm); + ReindexSteps(); + } + } + + public void MoveStepDesignerUp(StepDesignerViewModel vm) + { + if(StepDesigners is null || vm.Index == 0) + return; + + StepDesigners.RemoveAt(vm.Index); + StepDesigners.Insert(vm.Index - 1, vm); + ReindexSteps(); + } + + public void MoveStepDesignerDown(StepDesignerViewModel vm) + { + if(StepDesigners is null || vm.Index == StepDesigners.Count - 1) + return; + + StepDesigners.RemoveAt(vm.Index); + StepDesigners.Insert(vm.Index + 1, vm); + ReindexSteps(); + } + + private void ReindexSteps() + { + if(StepDesigners is not null) + { + var idx = 0; + foreach(var stepDesigner in StepDesigners) + stepDesigner.Index = idx++; + } + } + + public ExperimentTemplate ExperimentTemplate + { + private get => _experimentTemplate; + + set + { + _experimentTemplate = value; + Init(value); + } + } + + [Reactive] + public partial string Name { get; set; } + + public IList StepDesigners { get; private set; } = []; + + public IEnumerable? ExperimentOutputProviderCommand { get; set; } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/MetadataPickerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/MetadataPickerViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Automation/CampaignEdit/MetadataPickerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/MetadataPickerViewModel.cs diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/ParameterEditorViewModel.cs b/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Automation/CampaignEdit/ParameterEditorViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs index eb44862d..a459b4c9 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/ParameterEditorViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs @@ -1,248 +1,248 @@ -using Ares.Datamodel; -using Ares.Datamodel.Extensions; -using Ares.Datamodel.Factories; -using Ares.Datamodel.Templates; -using Humanizer; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using UI.Backend.Helpers; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public partial class ParameterEditorViewModel : ReactiveObject -{ - private readonly UnitCategoryHelper _unitHelper; - private string? _category; - private double _maximum; - private double _minimum; - private string? _name; - private ParameterMetadata _parameterMetadata = null!; - private string? _unit; - private AresValue? _initialValue; - private bool _hasInitialValue; - private AresDataType _dataType; - - public ParameterEditorViewModel(UnitCategoryHelper unitHelper, IEnumerable availableOutputs) - { - _unitHelper = unitHelper; - - ParameterMetadata = new ParameterMetadata - { - UniqueId = Guid.NewGuid().ToString(), - Name = "Param", - Schema = AresSchemaBuilder.Entry(AresDataType.UnspecifiedType).Build(), - Constraints = - { - new Limits() - } - }; - - AvailableOutputs = availableOutputs.ToArray(); - CategoryOptions = []; - UnitOptions = []; - } - - public ParameterEditorViewModel(ParameterMetadata existingMetadata, IEnumerable availableOutputs, UnitCategoryHelper unitHelper) - { - CategoryOptions = []; - UnitOptions = []; - _unitHelper = unitHelper; - ParameterMetadata = existingMetadata; - AvailableOutputs = availableOutputs.ToArray(); - HasInitialValue = existingMetadata.InitialValue != null; - InitialValue = existingMetadata.InitialValue; - } - - public ParameterMetadata ParameterMetadata - { - get => _parameterMetadata; - - set - { - _parameterMetadata = value; - Init(value); - } - } - - public AresDataType DataType - { - get => _dataType; - - set - { - _dataType = value; - - if(HasInitialValue) - InitialValue = AresValueHelper.CreateDefault(value); - } - } - - public string? Category - { - get => _category; - - set - { - var changed = _category != value; - this.RaiseAndSetIfChanged(ref _category, value); - if(value is null || !changed) - return; - - UnitOptions = UnitCategoryHelper.GetTypes(value.Dehumanize()).Select(s => s.Humanize()).ToList(); - - if(value == "None" || UnitOptions.Count == 0) - UnitOptions.Add("None"); - - Unit = UnitOptions.First(); - } - } - - [Reactive] - public partial List CategoryOptions { get; private set; } - - [Reactive] - public partial List UnitOptions { get; private set; } - - public string? Unit - { - get => _unit; - - set - { - this.RaiseAndSetIfChanged(ref _unit, value); - if(value == null) - return; - - Category = _unitHelper.GetCategoryForUnit(_unit.Dehumanize()).Humanize(); - } - } - - public string? Name - { - get => _name; - - set => this.RaiseAndSetIfChanged(ref _name, value); - } - - public double Minimum - { - get => _minimum; - - set => this.RaiseAndSetIfChanged(ref _minimum, value); - } - - public double Maximum - { - get => _maximum; - - set => this.RaiseAndSetIfChanged(ref _maximum, value); - } - - private void Init(ParameterMetadata meta) - { - LockInParams(meta); - Name = meta.Name; - DataType = meta.Schema.Type; - foreach(var metaConstraint in meta.Constraints) - { - Minimum = metaConstraint.Minimum; - Maximum = metaConstraint.Maximum; - } - - SelectedAchievedOutput = string.IsNullOrEmpty(meta.OutputName) ? null : meta.OutputName; - HasAchievedValue = SelectedAchievedOutput is not null; - } - - public ParameterMetadata Save() - { - if(ParameterMetadata.Constraints.Count == 0) - ParameterMetadata.Constraints.Add(new Limits()); - - ParameterMetadata.Schema = AresSchemaBuilder.Entry(DataType).Build(); - ParameterMetadata.Name = Name; - ParameterMetadata.InitialValue ??= InitialValue; - - if(DataType == AresDataType.Number) - { - ParameterMetadata.Constraints[0].Maximum = (float)Maximum; - ParameterMetadata.Constraints[0].Minimum = (float)Minimum; - ParameterMetadata.Unit = Unit.Dehumanize(); - } - - // TODO Should technically set to null but protobuf complains. Maybe investigate further? - ParameterMetadata.OutputName = HasAchievedValue ? SelectedAchievedOutput : ""; - - return ParameterMetadata; - } - - private bool IsValid(AresValue value) - { - switch(ParameterMetadata.Schema.Type) - { - case AresDataType.Number: - if(!value.HasNumberValue) - return false; - - if(ParameterMetadata.Constraints.Count == 0) - return true; - - return ParameterMetadata.Constraints.Any(limits => value.NumberValue >= limits.Minimum && value.NumberValue <= limits.Maximum); - - case AresDataType.String: - return value.HasStringValue; - - default: - return true; - } - } - - public bool HasAchievedValue { get; set; } - - public bool HasInitialValue - { - get => _hasInitialValue; - - set - { - _hasInitialValue = value; - if(value) - InitialValue = AresValueHelper.CreateDefault(DataType); - - else - InitialValue = null; - } - } - - public string? SelectedAchievedOutput { get; set; } - - public string[]? AvailableOutputs { get; set; } - - private void LockInParams(ParameterMetadata meta) - { - var categories = _unitHelper.UnitCategories.Select(s => s.Humanize()).ToList(); - categories.Add("None"); - CategoryOptions = categories; - var unit = meta.Unit; - var cat = _unitHelper.GetCategoryForUnit(unit); - UnitOptions = UnitCategoryHelper.GetTypes(cat).Select(s => s.Humanize()).ToList(); - Category = cat.Humanize(); - Unit = unit.Humanize(); - } - - public AresValue? InitialValue - { - get => _initialValue; - - set - { - this.RaiseAndSetIfChanged(ref _initialValue, value); - if(value is null) - return; - - IsInitialValueValid = IsValid(value); - } - } - - public bool IsInitialValueValid { get; set; } - -} +using Ares.Datamodel; +using Ares.Datamodel.Extensions; +using Ares.Datamodel.Factories; +using Ares.Datamodel.Templates; +using Humanizer; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Backend.Helpers; + +namespace UI.Backend.ViewModels.Automation.CampaignEdit; + +public partial class ParameterEditorViewModel : ReactiveObject +{ + private readonly UnitCategoryHelper _unitHelper; + private string? _category; + private double _maximum; + private double _minimum; + private string? _name; + private ParameterMetadata _parameterMetadata = null!; + private string? _unit; + private AresValue? _initialValue; + private bool _hasInitialValue; + private AresDataType _dataType; + + public ParameterEditorViewModel(UnitCategoryHelper unitHelper, IEnumerable availableOutputs) + { + _unitHelper = unitHelper; + + ParameterMetadata = new ParameterMetadata + { + UniqueId = Guid.NewGuid().ToString(), + Name = "Param", + Schema = AresSchemaBuilder.Entry(AresDataType.UnspecifiedType).Build(), + Constraints = + { + new Limits() + } + }; + + AvailableOutputs = availableOutputs.ToArray(); + CategoryOptions = []; + UnitOptions = []; + } + + public ParameterEditorViewModel(ParameterMetadata existingMetadata, IEnumerable availableOutputs, UnitCategoryHelper unitHelper) + { + CategoryOptions = []; + UnitOptions = []; + _unitHelper = unitHelper; + ParameterMetadata = existingMetadata; + AvailableOutputs = availableOutputs.ToArray(); + HasInitialValue = existingMetadata.InitialValue != null; + InitialValue = existingMetadata.InitialValue; + } + + public ParameterMetadata ParameterMetadata + { + get => _parameterMetadata; + + set + { + _parameterMetadata = value; + Init(value); + } + } + + public AresDataType DataType + { + get => _dataType; + + set + { + _dataType = value; + + if(HasInitialValue) + InitialValue = AresValueHelper.CreateDefault(value); + } + } + + public string? Category + { + get => _category; + + set + { + var changed = _category != value; + this.RaiseAndSetIfChanged(ref _category, value); + if(value is null || !changed) + return; + + UnitOptions = UnitCategoryHelper.GetTypes(value.Dehumanize()).Select(s => s.Humanize()).ToList(); + + if(value == "None" || UnitOptions.Count == 0) + UnitOptions.Add("None"); + + Unit = UnitOptions.First(); + } + } + + [Reactive] + public partial List CategoryOptions { get; private set; } + + [Reactive] + public partial List UnitOptions { get; private set; } + + public string? Unit + { + get => _unit; + + set + { + this.RaiseAndSetIfChanged(ref _unit, value); + if(value == null) + return; + + Category = _unitHelper.GetCategoryForUnit(_unit.Dehumanize()).Humanize(); + } + } + + public string? Name + { + get => _name; + + set => this.RaiseAndSetIfChanged(ref _name, value); + } + + public double Minimum + { + get => _minimum; + + set => this.RaiseAndSetIfChanged(ref _minimum, value); + } + + public double Maximum + { + get => _maximum; + + set => this.RaiseAndSetIfChanged(ref _maximum, value); + } + + private void Init(ParameterMetadata meta) + { + LockInParams(meta); + Name = meta.Name; + DataType = meta.Schema.Type; + foreach(var metaConstraint in meta.Constraints) + { + Minimum = metaConstraint.Minimum; + Maximum = metaConstraint.Maximum; + } + + SelectedAchievedOutput = string.IsNullOrEmpty(meta.OutputName) ? null : meta.OutputName; + HasAchievedValue = SelectedAchievedOutput is not null; + } + + public ParameterMetadata Save() + { + if(ParameterMetadata.Constraints.Count == 0) + ParameterMetadata.Constraints.Add(new Limits()); + + ParameterMetadata.Schema = AresSchemaBuilder.Entry(DataType).Build(); + ParameterMetadata.Name = Name; + ParameterMetadata.InitialValue ??= InitialValue; + + if(DataType == AresDataType.Number) + { + ParameterMetadata.Constraints[0].Maximum = (float)Maximum; + ParameterMetadata.Constraints[0].Minimum = (float)Minimum; + ParameterMetadata.Unit = Unit.Dehumanize(); + } + + // TODO Should technically set to null but protobuf complains. Maybe investigate further? + ParameterMetadata.OutputName = HasAchievedValue ? SelectedAchievedOutput : ""; + + return ParameterMetadata; + } + + private bool IsValid(AresValue value) + { + switch(ParameterMetadata.Schema.Type) + { + case AresDataType.Number: + if(!value.HasNumberValue) + return false; + + if(ParameterMetadata.Constraints.Count == 0) + return true; + + return ParameterMetadata.Constraints.Any(limits => value.NumberValue >= limits.Minimum && value.NumberValue <= limits.Maximum); + + case AresDataType.String: + return value.HasStringValue; + + default: + return true; + } + } + + public bool HasAchievedValue { get; set; } + + public bool HasInitialValue + { + get => _hasInitialValue; + + set + { + _hasInitialValue = value; + if(value) + InitialValue = AresValueHelper.CreateDefault(DataType); + + else + InitialValue = null; + } + } + + public string? SelectedAchievedOutput { get; set; } + + public string[]? AvailableOutputs { get; set; } + + private void LockInParams(ParameterMetadata meta) + { + var categories = _unitHelper.UnitCategories.Select(s => s.Humanize()).ToList(); + categories.Add("None"); + CategoryOptions = categories; + var unit = meta.Unit; + var cat = _unitHelper.GetCategoryForUnit(unit); + UnitOptions = UnitCategoryHelper.GetTypes(cat).Select(s => s.Humanize()).ToList(); + Category = cat.Humanize(); + Unit = unit.Humanize(); + } + + public AresValue? InitialValue + { + get => _initialValue; + + set + { + this.RaiseAndSetIfChanged(ref _initialValue, value); + if(value is null) + return; + + IsInitialValueValid = IsValid(value); + } + } + + public bool IsInitialValueValid { get; set; } + +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs similarity index 90% rename from UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs index 543f4698..65e8b0c8 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannableParameterDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs @@ -1,57 +1,58 @@ -using System.Collections.ObjectModel; -using Ares.Datamodel.Templates; -using DynamicData; -using ReactiveUI; -using UI.Backend.Extensions; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public class PlannableParameterDesignerViewModel : ReactiveObject -{ - private readonly ParameterEditorFactory _editorFactory; - private IEnumerable _parameterMetadata = []; - private ExperimentTemplate? _experimentTemplate; - - public PlannableParameterDesignerViewModel(IEnumerable existingMetadata, ExperimentTemplate? experimentTemplate, ParameterEditorFactory editorFactory) - { - _editorFactory = editorFactory; - _experimentTemplate = experimentTemplate; - ParameterMetadata = existingMetadata; - } - - public ObservableCollection ParameterEditors { get; } = []; - - public IEnumerable ParameterMetadata - { - private get => _parameterMetadata; - - set - { - _parameterMetadata = value; - Init(value); - } - } - - public IEnumerable Save() - { - return ParameterEditors.Select(model => model.Save()); - } - - private void Init(IEnumerable paramMetadata) - { - var outputs = _experimentTemplate?.GetAllOutputCommands().SelectMany(cmd => cmd.UserOutputKeyMap.Select(m => m.Value)).ToArray() ?? []; - ParameterEditors.AddRange(paramMetadata.Select(metadata => _editorFactory.Create(metadata, outputs))); - } - - public void Create() - { - var outputs = _experimentTemplate?.GetAllOutputCommands().SelectMany(cmd => cmd.UserOutputKeyMap.Select(m => m.Value)).ToArray() ?? []; - ParameterEditors.Add(_editorFactory.Create(outputs)); - } - - public void Remove(ParameterEditorViewModel vm) - { - ParameterEditors.Remove(vm); - } -} +using System.Collections.ObjectModel; +using Ares.Datamodel.Templates; +using DynamicData; +using ReactiveUI; +using UI.Backend.Extensions; +using UI.Backend.ViewModels.Automation.CampaignEdit; +using UI.Features.CampaignEdit.Factories; + +namespace UI.Features.CampaignEdit.ViewModels; + +public class PlannableParameterDesignerViewModel : ReactiveObject +{ + private readonly ParameterEditorFactory _editorFactory; + private IEnumerable _parameterMetadata = []; + private ExperimentTemplate? _experimentTemplate; + + public PlannableParameterDesignerViewModel(IEnumerable existingMetadata, ExperimentTemplate? experimentTemplate, ParameterEditorFactory editorFactory) + { + _editorFactory = editorFactory; + _experimentTemplate = experimentTemplate; + ParameterMetadata = existingMetadata; + } + + public ObservableCollection ParameterEditors { get; } = []; + + public IEnumerable ParameterMetadata + { + private get => _parameterMetadata; + + set + { + _parameterMetadata = value; + Init(value); + } + } + + public IEnumerable Save() + { + return ParameterEditors.Select(model => model.Save()); + } + + private void Init(IEnumerable paramMetadata) + { + var outputs = _experimentTemplate?.GetAllOutputCommands().SelectMany(cmd => cmd.UserOutputKeyMap.Select(m => m.Value)).ToArray() ?? []; + ParameterEditors.AddRange(paramMetadata.Select(metadata => _editorFactory.Create(metadata, outputs))); + } + + public void Create() + { + var outputs = _experimentTemplate?.GetAllOutputCommands().SelectMany(cmd => cmd.UserOutputKeyMap.Select(m => m.Value)).ToArray() ?? []; + ParameterEditors.Add(_editorFactory.Create(outputs)); + } + + public void Remove(ParameterEditorViewModel vm) + { + ParameterEditors.Remove(vm); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannerAllocationEditorViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Automation/CampaignEdit/PlannerAllocationEditorViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs index 252ac871..1ba71f84 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/PlannerAllocationEditorViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs @@ -1,107 +1,107 @@ -using Ares.Datamodel.Planning; -using Ares.Datamodel.Templates; -using Ares.Services; -using ReactiveUI; -using ReactiveUI.SourceGenerators; -using UI.Services.Notification; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public partial class PlannerAllocationEditorViewModel : ReactiveObject -{ - private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _plannerClient; - private readonly INotificationReceivingService _notificationService; - private PlannerServiceInfo? _selectedAdapter; - - public PlannerAllocationEditorViewModel(ParameterMetadata metadata, - PlannerServiceInfo? plannerInfo, - IEnumerable plannerAdapters, - AresPlannerManagementService.AresPlannerManagementServiceClient plannerClient, - INotificationReceivingService notificationService) - { - var plannerArray = plannerAdapters.ToArray(); - - ParameterMetadata = metadata; - PlannerServices = plannerArray; - _plannerClient = plannerClient; - _notificationService = notificationService; - - if(plannerInfo is not null) - { - SelectedService = plannerInfo; - SelectedPlannerOption = plannerInfo.Capabilities.AvailablePlanners.FirstOrDefault(p => p.PlannerName == metadata.PlannerName); - } - - PlannerOptions = Enumerable.Empty(); - } - - public PlannerAllocation? Save() - { - if(SelectedService is null) - return null; - - var allocation = new PlannerAllocation - { - Parameter = ParameterMetadata, - Planner = SelectedService, - UniqueId = Guid.NewGuid().ToString() - }; - - allocation.Parameter.PlannerName = SelectedPlannerOption?.PlannerName ?? SelectedService.Name; - allocation.Parameter.PlannerDescription = SelectedPlannerOption?.Description ?? SelectedService.Description; - - return allocation; - } - - public async Task UpdatePlannerOptions() - { - if(SelectedService is null) - return; - - var updatedInfo = await _plannerClient.GetInfoAsync(new PlannerInfoRequest { PlannerId = SelectedService.UniqueId }); - - if(updatedInfo.Info.Name != string.Empty) - { - PlannerOptions = updatedInfo.Info.Capabilities.AvailablePlanners; - - //Not all adapters will have multiple options, if not auto assign the value - if(PlannerOptions.Count() == 1) - SelectedPlannerOption = SelectedService.Capabilities.AvailablePlanners.First(); - } - - else - { - var notification = new AresNotification(); - notification.Title = "Assigned Planner Unavailable!"; - notification.Message = $"This template uses a planner that ARES no longer has a connection with. The template won't be usable until this is resolved."; - notification.NotificationSeverity = Severity.Warning; - notification.Loiter = true; - _notificationService.PushNotification(notification); - } - } - - public PlannerServiceInfo? SelectedService - { - get => _selectedAdapter; - - set - { - if(value is null || _selectedAdapter == value) - return; - - if(_selectedAdapter is not null) - SelectedPlannerOption = _selectedAdapter.Capabilities.AvailablePlanners.FirstOrDefault(); - - _selectedAdapter = value; - _ = UpdatePlannerOptions(); - } - } - - [Reactive] - public partial ParameterMetadata ParameterMetadata { get; set; } - public IEnumerable PlannerServices { get; } - [Reactive] - public partial IEnumerable PlannerOptions { get; set; } - [Reactive] - public partial Planner? SelectedPlannerOption { get; set; } -} +using Ares.Datamodel.Planning; +using Ares.Datamodel.Templates; +using Ares.Services; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Services.Notification; + +namespace UI.Backend.ViewModels.Automation.CampaignEdit; + +public partial class PlannerAllocationEditorViewModel : ReactiveObject +{ + private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _plannerClient; + private readonly INotificationReceivingService _notificationService; + private PlannerServiceInfo? _selectedAdapter; + + public PlannerAllocationEditorViewModel(ParameterMetadata metadata, + PlannerServiceInfo? plannerInfo, + IEnumerable plannerAdapters, + AresPlannerManagementService.AresPlannerManagementServiceClient plannerClient, + INotificationReceivingService notificationService) + { + var plannerArray = plannerAdapters.ToArray(); + + ParameterMetadata = metadata; + PlannerServices = plannerArray; + _plannerClient = plannerClient; + _notificationService = notificationService; + + if(plannerInfo is not null) + { + SelectedService = plannerInfo; + SelectedPlannerOption = plannerInfo.Capabilities.AvailablePlanners.FirstOrDefault(p => p.PlannerName == metadata.PlannerName); + } + + PlannerOptions = Enumerable.Empty(); + } + + public PlannerAllocation? Save() + { + if(SelectedService is null) + return null; + + var allocation = new PlannerAllocation + { + Parameter = ParameterMetadata, + Planner = SelectedService, + UniqueId = Guid.NewGuid().ToString() + }; + + allocation.Parameter.PlannerName = SelectedPlannerOption?.PlannerName ?? SelectedService.Name; + allocation.Parameter.PlannerDescription = SelectedPlannerOption?.Description ?? SelectedService.Description; + + return allocation; + } + + public async Task UpdatePlannerOptions() + { + if(SelectedService is null) + return; + + var updatedInfo = await _plannerClient.GetInfoAsync(new PlannerInfoRequest { PlannerId = SelectedService.UniqueId }); + + if(updatedInfo.Info.Name != string.Empty) + { + PlannerOptions = updatedInfo.Info.Capabilities.AvailablePlanners; + + //Not all adapters will have multiple options, if not auto assign the value + if(PlannerOptions.Count() == 1) + SelectedPlannerOption = SelectedService.Capabilities.AvailablePlanners.First(); + } + + else + { + var notification = new AresNotification(); + notification.Title = "Assigned Planner Unavailable!"; + notification.Message = $"This template uses a planner that ARES no longer has a connection with. The template won't be usable until this is resolved."; + notification.NotificationSeverity = Severity.Warning; + notification.Loiter = true; + _notificationService.PushNotification(notification); + } + } + + public PlannerServiceInfo? SelectedService + { + get => _selectedAdapter; + + set + { + if(value is null || _selectedAdapter == value) + return; + + if(_selectedAdapter is not null) + SelectedPlannerOption = _selectedAdapter.Capabilities.AvailablePlanners.FirstOrDefault(); + + _selectedAdapter = value; + _ = UpdatePlannerOptions(); + } + } + + [Reactive] + public partial ParameterMetadata ParameterMetadata { get; set; } + public IEnumerable PlannerServices { get; } + [Reactive] + public partial IEnumerable PlannerOptions { get; set; } + [Reactive] + public partial Planner? SelectedPlannerOption { get; set; } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Automation/CampaignEdit/PlanningViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index a17fa4d0..847b5e01 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -1,47 +1,48 @@ -using ReactiveUI; -using System.Collections.ObjectModel; -using Ares.Datamodel.Templates; -using Ares.Services; -using Ares.Datamodel.Planning; -using UI.Services.Notification; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public class PlanningViewModel : ReactiveObject -{ - private readonly CampaignTemplate _template; - private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; - private readonly INotificationReceivingService _notificationService; - - public PlanningViewModel(CampaignTemplate template, - IEnumerable plannerAdapters, - AresPlannerManagementService.AresPlannerManagementServiceClient client, - INotificationReceivingService notificationService) - { - _template = template; - _client = client; - _notificationService = notificationService; - PlannerAdapters = new ReadOnlyCollection(plannerAdapters.ToList()); - PlannerAllocationEditors = template.PlannableParameters.Select(metadata => new PlannerAllocationEditorViewModel(metadata, template.PlannerAllocations.FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, client, notificationService)).ToArray(); - } - - public IEnumerable PlannerAllocationEditors { get; private set; } - - public IEnumerable PlannerAdapters { get; } - - public void Save() - { - //We're not updating everything here maybe? Or maybe I'll just update the ARES core stuff. - _template.PlannerAllocations.Clear(); - - _template.PlannerAllocations.AddRange(PlannerAllocationEditors - .Select(editor => editor.Save()) - .Where(allocation => allocation is not null) - .Where(allocation => _template.PlannableParameters.Any(meta => meta.UniqueId == allocation!.Parameter.UniqueId))); - - PlannerAllocationEditors = _template.PlannableParameters - .Select(metadata => new PlannerAllocationEditorViewModel(metadata, _template.PlannerAllocations - .FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, _client, _notificationService)) - .ToArray(); - } -} +using ReactiveUI; +using System.Collections.ObjectModel; +using Ares.Datamodel.Templates; +using Ares.Services; +using Ares.Datamodel.Planning; +using UI.Services.Notification; +using UI.Backend.ViewModels.Automation.CampaignEdit; + +namespace UI.Features.CampaignEdit.ViewModels; + +public class PlanningViewModel : ReactiveObject +{ + private readonly CampaignTemplate _template; + private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; + private readonly INotificationReceivingService _notificationService; + + public PlanningViewModel(CampaignTemplate template, + IEnumerable plannerAdapters, + AresPlannerManagementService.AresPlannerManagementServiceClient client, + INotificationReceivingService notificationService) + { + _template = template; + _client = client; + _notificationService = notificationService; + PlannerAdapters = new ReadOnlyCollection(plannerAdapters.ToList()); + PlannerAllocationEditors = template.PlannableParameters.Select(metadata => new PlannerAllocationEditorViewModel(metadata, template.PlannerAllocations.FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, client, notificationService)).ToArray(); + } + + public IEnumerable PlannerAllocationEditors { get; private set; } + + public IEnumerable PlannerAdapters { get; } + + public void Save() + { + //We're not updating everything here maybe? Or maybe I'll just update the ARES core stuff. + _template.PlannerAllocations.Clear(); + + _template.PlannerAllocations.AddRange(PlannerAllocationEditors + .Select(editor => editor.Save()) + .Where(allocation => allocation is not null) + .Where(allocation => _template.PlannableParameters.Any(meta => meta.UniqueId == allocation!.Parameter.UniqueId))); + + PlannerAllocationEditors = _template.PlannableParameters + .Select(metadata => new PlannerAllocationEditorViewModel(metadata, _template.PlannerAllocations + .FirstOrDefault(allocation => allocation.Parameter.Equals(metadata))?.Planner, PlannerAdapters, _client, _notificationService)) + .ToArray(); + } +} diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/StartupDesignerViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/StartupDesignerViewModel.cs index af10b02b..801c4203 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/StartupDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/StartupDesignerViewModel.cs @@ -3,7 +3,8 @@ using Radzen; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/StepDesignerViewModel.cs similarity index 92% rename from UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs rename to UI/Features/CampaignEdit/ViewModels/StepDesignerViewModel.cs index 1b05a94a..9a3b9232 100644 --- a/UI/Backend/ViewModels/Automation/CampaignEdit/StepDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/StepDesignerViewModel.cs @@ -1,123 +1,124 @@ -using Ares.Datamodel.Templates; -using ReactiveUI; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; - -namespace UI.Backend.ViewModels.Automation.CampaignEdit; - -public class StepDesignerViewModel : ReactiveObject -{ - private readonly CommandDesignerFactory _commandDesignerFactory; - private StepTemplate _stepTemplate = null!; - - public StepDesignerViewModel(CommandDesignerFactory commandDesignerFactory) - { - _commandDesignerFactory = commandDesignerFactory; - StepTemplate = new StepTemplate - { - UniqueId = Guid.NewGuid().ToString(), - Name = "New Step" - }; - } - - public StepDesignerViewModel(StepTemplate existingStepTemplate, CommandDesignerFactory commandDesignerFactory) - { - _commandDesignerFactory = commandDesignerFactory; - StepTemplate = existingStepTemplate; - } - - public string Name - { - get => StepTemplate.Name; - - set - { - StepTemplate.Name = value; - this.RaisePropertyChanged(); - } - } - - public bool Parallel - { - get => StepTemplate.IsParallel; - - set - { - StepTemplate.IsParallel = value; - this.RaisePropertyChanged(); - } - } - - public int Index { get; set; } - - public StepTemplate StepTemplate - { - get => _stepTemplate; - - set - { - _stepTemplate = value; - Init(value); - } - } - - public IList CommandDesigners { get; private set; } = Array.Empty(); - - private void Init(StepTemplate existingTemplate) - { - Name = existingTemplate.Name; - Parallel = existingTemplate.IsParallel; - Index = Convert.ToInt32(existingTemplate.Index); - CommandDesigners = existingTemplate.CommandTemplates.Select(template => _commandDesignerFactory.Create(template)).OrderBy(model => model.Index).ToList(); - } - - public StepTemplate Save() - { - StepTemplate.Name = Name; - StepTemplate.IsParallel = Parallel; - StepTemplate.Index = Index; - StepTemplate.CommandTemplates.Clear(); - StepTemplate.CommandTemplates.AddRange(CommandDesigners.Select(designer => designer.Save())); - return StepTemplate; - } - - public CommandDesignerViewModel AddCommandDesigner() - { - var newDesigner = _commandDesignerFactory.Create(); - newDesigner.Index = CommandDesigners.Count; - CommandDesigners.Add(newDesigner); - return newDesigner; - } - - public void RemoveCommandDesigner(CommandDesignerViewModel vm) - { - CommandDesigners.Remove(vm); - ReindexCommands(); - } - - public void MoveCommandDesignerUp(CommandDesignerViewModel vm) - { - if(vm.Index == 0) - return; - - CommandDesigners.RemoveAt(vm.Index); - CommandDesigners.Insert(vm.Index - 1, vm); - ReindexCommands(); - } - - public void MoveCommandDesignerDown(CommandDesignerViewModel vm) - { - if(vm.Index == CommandDesigners.Count - 1) - return; - - CommandDesigners.RemoveAt(vm.Index); - CommandDesigners.Insert(vm.Index + 1, vm); - ReindexCommands(); - } - - private void ReindexCommands() - { - var idx = 0; - foreach(var commandDesigner in CommandDesigners) - commandDesigner.Index = idx++; - } -} +using Ares.Datamodel.Templates; +using ReactiveUI; +using UI.Backend.ViewModels.Automation.CampaignEdit; +using UI.Features.CampaignEdit.Factories; + +namespace UI.Features.CampaignEdit.ViewModels; + +public class StepDesignerViewModel : ReactiveObject +{ + private readonly CommandDesignerFactory _commandDesignerFactory; + private StepTemplate _stepTemplate = null!; + + public StepDesignerViewModel(CommandDesignerFactory commandDesignerFactory) + { + _commandDesignerFactory = commandDesignerFactory; + StepTemplate = new StepTemplate + { + UniqueId = Guid.NewGuid().ToString(), + Name = "New Step" + }; + } + + public StepDesignerViewModel(StepTemplate existingStepTemplate, CommandDesignerFactory commandDesignerFactory) + { + _commandDesignerFactory = commandDesignerFactory; + StepTemplate = existingStepTemplate; + } + + public string Name + { + get => StepTemplate.Name; + + set + { + StepTemplate.Name = value; + this.RaisePropertyChanged(); + } + } + + public bool Parallel + { + get => StepTemplate.IsParallel; + + set + { + StepTemplate.IsParallel = value; + this.RaisePropertyChanged(); + } + } + + public int Index { get; set; } + + public StepTemplate StepTemplate + { + get => _stepTemplate; + + set + { + _stepTemplate = value; + Init(value); + } + } + + public IList CommandDesigners { get; private set; } = Array.Empty(); + + private void Init(StepTemplate existingTemplate) + { + Name = existingTemplate.Name; + Parallel = existingTemplate.IsParallel; + Index = Convert.ToInt32(existingTemplate.Index); + CommandDesigners = existingTemplate.CommandTemplates.Select(template => _commandDesignerFactory.Create(template)).OrderBy(model => model.Index).ToList(); + } + + public StepTemplate Save() + { + StepTemplate.Name = Name; + StepTemplate.IsParallel = Parallel; + StepTemplate.Index = Index; + StepTemplate.CommandTemplates.Clear(); + StepTemplate.CommandTemplates.AddRange(CommandDesigners.Select(designer => designer.Save())); + return StepTemplate; + } + + public CommandDesignerViewModel AddCommandDesigner() + { + var newDesigner = _commandDesignerFactory.Create(); + newDesigner.Index = CommandDesigners.Count; + CommandDesigners.Add(newDesigner); + return newDesigner; + } + + public void RemoveCommandDesigner(CommandDesignerViewModel vm) + { + CommandDesigners.Remove(vm); + ReindexCommands(); + } + + public void MoveCommandDesignerUp(CommandDesignerViewModel vm) + { + if(vm.Index == 0) + return; + + CommandDesigners.RemoveAt(vm.Index); + CommandDesigners.Insert(vm.Index - 1, vm); + ReindexCommands(); + } + + public void MoveCommandDesignerDown(CommandDesignerViewModel vm) + { + if(vm.Index == CommandDesigners.Count - 1) + return; + + CommandDesigners.RemoveAt(vm.Index); + CommandDesigners.Insert(vm.Index + 1, vm); + ReindexCommands(); + } + + private void ReindexCommands() + { + var idx = 0; + foreach(var commandDesigner in CommandDesigners) + commandDesigner.Index = idx++; + } +} diff --git a/UI/Pages/Automation/CampaignList.razor b/UI/Features/CampaignEdit/Views/CampaignList.razor similarity index 97% rename from UI/Pages/Automation/CampaignList.razor rename to UI/Features/CampaignEdit/Views/CampaignList.razor index aa2bd91a..49750baa 100644 --- a/UI/Pages/Automation/CampaignList.razor +++ b/UI/Features/CampaignEdit/Views/CampaignList.razor @@ -1,107 +1,108 @@ -@page "/automation" -@page "/automation/campaignlist" -@using Ares.Services -@using Newtonsoft.Json -@using UI.Backend.ViewModels.Automation.CampaignEdit -@inject DialogService DialogService -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase -@inject IJSRuntime JS - - -
-
-

Campaigns

-
- - - - -
-
- - - - - - - - - @foreach (var template in ViewModel!.Templates) - { - - - - - - } - -
Campaign NameCampaign ID
- @template.CampaignName - @template.UniqueId -
- - - - - -
-
-
- -@code { - protected override async Task OnInitializedAsync() - { - await ViewModel!.RefreshCampaigns(); - } - - private async void DeleteClick(CampaignTemplateSummary template) - { - var result = await DialogService.OpenAsync($"Delete campaign {template.CampaignName}?", ds => - @
-
- - -
-
- ); - if (result is null || !result) - return; - - await ViewModel!.DeleteCampaign(Guid.Parse(template.UniqueId)); - StateHasChanged(); - } - - private async void Refresh() - { - await ViewModel!.RefreshCampaigns(); - StateHasChanged(); - } - - private async void ExportTemplate(CampaignTemplateSummary templateSummary) - { - //Generate a copy of the template, create a new unique ID and change the name to avoid collisions if the user adds it to their templates folder - var template = await ViewModel!.GetFullCampaignTemplate(templateSummary.UniqueId); - - if(template is not null) - { - var templateCopy = template.Clone(); - templateCopy.UniqueId = Guid.NewGuid().ToString(); - templateCopy.Name = $"{template.Name}-Copy"; - var fileName = templateCopy.UniqueId; - var jsonData = JsonConvert.SerializeObject(templateCopy, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); - await JS.InvokeVoidAsync("downloadJsonFile", fileName, jsonData); - } - - else - { - //Notify of failure..? - } - } - -} +@page "/automation" +@page "/automation/campaignlist" +@using Ares.Services +@using Newtonsoft.Json +@using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels +@inject DialogService DialogService +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase +@inject IJSRuntime JS + + +
+
+

Campaigns

+
+ + + + +
+
+ + + + + + + + + @foreach (var template in ViewModel!.Templates) + { + + + + + + } + +
Campaign NameCampaign ID
+ @template.CampaignName + @template.UniqueId +
+ + + + + +
+
+
+ +@code { + protected override async Task OnInitializedAsync() + { + await ViewModel!.RefreshCampaigns(); + } + + private async void DeleteClick(CampaignTemplateSummary template) + { + var result = await DialogService.OpenAsync($"Delete campaign {template.CampaignName}?", ds => + @
+
+ + +
+
+ ); + if (result is null || !result) + return; + + await ViewModel!.DeleteCampaign(Guid.Parse(template.UniqueId)); + StateHasChanged(); + } + + private async void Refresh() + { + await ViewModel!.RefreshCampaigns(); + StateHasChanged(); + } + + private async void ExportTemplate(CampaignTemplateSummary templateSummary) + { + //Generate a copy of the template, create a new unique ID and change the name to avoid collisions if the user adds it to their templates folder + var template = await ViewModel!.GetFullCampaignTemplate(templateSummary.UniqueId); + + if(template is not null) + { + var templateCopy = template.Clone(); + templateCopy.UniqueId = Guid.NewGuid().ToString(); + templateCopy.Name = $"{template.Name}-Copy"; + var fileName = templateCopy.UniqueId; + var jsonData = JsonConvert.SerializeObject(templateCopy, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); + await JS.InvokeVoidAsync("downloadJsonFile", fileName, jsonData); + } + + else + { + //Notify of failure..? + } + } + +} diff --git a/UI/Pages/Shared/CampaignEdit/CloseoutScriptDesigner.razor b/UI/Features/CampaignEdit/Views/CloseoutScriptDesigner.razor similarity index 98% rename from UI/Pages/Shared/CampaignEdit/CloseoutScriptDesigner.razor rename to UI/Features/CampaignEdit/Views/CloseoutScriptDesigner.razor index 084b8c9f..4e5da7ea 100644 --- a/UI/Pages/Shared/CampaignEdit/CloseoutScriptDesigner.razor +++ b/UI/Features/CampaignEdit/Views/CloseoutScriptDesigner.razor @@ -1,6 +1,7 @@ @inject DialogService DialogService @using Ares.Datamodel.Templates @using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Pages/Shared/CampaignEdit/CommandDesigner.razor b/UI/Features/CampaignEdit/Views/CommandDesigner.razor similarity index 100% rename from UI/Pages/Shared/CampaignEdit/CommandDesigner.razor rename to UI/Features/CampaignEdit/Views/CommandDesigner.razor diff --git a/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor b/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor similarity index 96% rename from UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor rename to UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor index 7bb4546f..e29ff97d 100644 --- a/UI/Pages/Shared/CampaignEdit/CommandDesignerQuickView.razor +++ b/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor @@ -1,105 +1,105 @@ -@using Ares.Datamodel.Templates -@using UI.Backend.ViewModels.Automation.CampaignEdit -@using UI.Infrastructure.Interfaces -@using UI.Pages.Resources - -@inject IUiNotificationService notificationService; - -@if (CommandDesignerViewModel is not null) -{ - if (CommandDesignerViewModel.TemplateCommandName is not null) - { -
- @if((CommandDesignerViewModel.MetadataDeviceName ?? CommandDesignerViewModel.TemplateDeviceName) is null) - { -
- UNKNOWN_DEVICE -
- } - else - { -
- @(CommandDesignerViewModel.MetadataDeviceName ?? CommandDesignerViewModel.TemplateDeviceName) -
- } -
- @CommandDesignerViewModel.TemplateCommandName -
- @if (CommandDesignerViewModel.Arguments.Any()) - { - - } -
- @foreach (var argument in CommandDesignerViewModel.Arguments) - { - @if (argument.PlanningMetadata is not null) - { -
- @ConvertToDisplayData(argument) -
- } - - else if(argument.VariableType > 0) - { -
- @argument.VariableType.ToString() -
- } - - else - { - if(argument.Value is null) - { - NotifyOfParameterError(argument); -
- Error! Parameter Deleted! -
- } - - else - { -
- @ConvertToDisplayData(argument) -
- } - } - } -
-
- -
- } - else - { - Undefined Command - } -} -@code { - [Parameter] - public CommandDesignerViewModel? CommandDesignerViewModel { get; set; } - - public void NotifyOfParameterError(Parameter errorParam) - { - var message = $"A command utilizing the parameter {errorParam.Metadata.Name} needs attention, as this parameter has been deleted"; - notificationService.Error(message, "Experiment Parameter Deleted"); - } - - public object ConvertToDisplayData(Parameter argument) - { - if(argument.PlanningMetadata is not null) - { - return $"{argument.Metadata.Name}: {argument.PlanningMetadata.Name}"; - } - var aresValue = argument.Value; - if(aresValue.HasNumberValue) - return $"{argument.Metadata.Name}: {aresValue.NumberValue} {argument.Metadata.Unit}"; - - else if(aresValue.HasStringValue) - return $"{argument.Metadata.Name}: {aresValue.StringValue} {argument.Metadata.Unit}"; - - else - return $"{argument.Metadata.Name}: Custom Data"; - } -} +@using Ares.Datamodel.Templates +@using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Infrastructure.Interfaces +@using UI.Pages.Resources + +@inject IUiNotificationService notificationService; + +@if (CommandDesignerViewModel is not null) +{ + if (CommandDesignerViewModel.TemplateCommandName is not null) + { +
+ @if((CommandDesignerViewModel.MetadataDeviceName ?? CommandDesignerViewModel.TemplateDeviceName) is null) + { +
+ UNKNOWN_DEVICE +
+ } + else + { +
+ @(CommandDesignerViewModel.MetadataDeviceName ?? CommandDesignerViewModel.TemplateDeviceName) +
+ } +
+ @CommandDesignerViewModel.TemplateCommandName +
+ @if (CommandDesignerViewModel.Arguments.Any()) + { + + } +
+ @foreach (var argument in CommandDesignerViewModel.Arguments) + { + @if (argument.PlanningMetadata is not null) + { +
+ @ConvertToDisplayData(argument) +
+ } + + else if(argument.VariableType > 0) + { +
+ @argument.VariableType.ToString() +
+ } + + else + { + if(argument.Value is null) + { + NotifyOfParameterError(argument); +
+ Error! Parameter Deleted! +
+ } + + else + { +
+ @ConvertToDisplayData(argument) +
+ } + } + } +
+
+ +
+ } + else + { + Undefined Command + } +} +@code { + [Parameter] + public CommandDesignerViewModel? CommandDesignerViewModel { get; set; } + + public void NotifyOfParameterError(Parameter errorParam) + { + var message = $"A command utilizing the parameter {errorParam.Metadata.Name} needs attention, as this parameter has been deleted"; + notificationService.Error(message, "Experiment Parameter Deleted"); + } + + public object ConvertToDisplayData(Parameter argument) + { + if(argument.PlanningMetadata is not null) + { + return $"{argument.Metadata.Name}: {argument.PlanningMetadata.Name}"; + } + var aresValue = argument.Value; + if(aresValue.HasNumberValue) + return $"{argument.Metadata.Name}: {aresValue.NumberValue} {argument.Metadata.Unit}"; + + else if(aresValue.HasStringValue) + return $"{argument.Metadata.Name}: {aresValue.StringValue} {argument.Metadata.Unit}"; + + else + return $"{argument.Metadata.Name}: Custom Data"; + } +} diff --git a/UI/Pages/Shared/CampaignEdit/CommandParameterDesigner.razor b/UI/Features/CampaignEdit/Views/CommandParameterDesigner.razor similarity index 97% rename from UI/Pages/Shared/CampaignEdit/CommandParameterDesigner.razor rename to UI/Features/CampaignEdit/Views/CommandParameterDesigner.razor index f40fdfb1..575d27bc 100644 --- a/UI/Pages/Shared/CampaignEdit/CommandParameterDesigner.razor +++ b/UI/Features/CampaignEdit/Views/CommandParameterDesigner.razor @@ -1,83 +1,84 @@ -@using Ares.Datamodel -@using Ares.Datamodel.Templates -@using UI.Backend.ViewModels.Automation.CampaignEdit -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
-
- @if (ViewModel!.IsPlanned || ViewModel!.IsEnvironmentBased) - { - if(ViewModel!.IsPlanned) - { - - - - - } - - else - { - - } - } - - else - { - - } -
- - @if (ViewModel!.IsEnvironmentBased && ViewModel!.SelectedVariableType == VariableType.PreviousExperimentPath) - { -
- - -
- } - - @if(!DisablePlanning) - { -
- - -
- } - -
- - -
- -
- -@code { - [Parameter] - public bool DisablePlanning { get; set; } - - private Task ValueEditCallback(AresValue value) - { - //Not sure what scenario this would be to be honest - if(ViewModel!.Value is null) - return Task.CompletedTask; - - ViewModel!.Value = value; - return Task.CompletedTask; - } +@using Ares.Datamodel +@using Ares.Datamodel.Templates +@using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+
+ @if (ViewModel!.IsPlanned || ViewModel!.IsEnvironmentBased) + { + if(ViewModel!.IsPlanned) + { + + + + + } + + else + { + + } + } + + else + { + + } +
+ + @if (ViewModel!.IsEnvironmentBased && ViewModel!.SelectedVariableType == VariableType.PreviousExperimentPath) + { +
+ + +
+ } + + @if(!DisablePlanning) + { +
+ + +
+ } + +
+ + +
+ +
+ +@code { + [Parameter] + public bool DisablePlanning { get; set; } + + private Task ValueEditCallback(AresValue value) + { + //Not sure what scenario this would be to be honest + if(ViewModel!.Value is null) + return Task.CompletedTask; + + ViewModel!.Value = value; + return Task.CompletedTask; + } } \ No newline at end of file diff --git a/UI/Pages/Shared/CampaignEdit/CommandViewer.razor b/UI/Features/CampaignEdit/Views/CommandViewer.razor similarity index 96% rename from UI/Pages/Shared/CampaignEdit/CommandViewer.razor rename to UI/Features/CampaignEdit/Views/CommandViewer.razor index 6e33c0db..8a910575 100644 --- a/UI/Pages/Shared/CampaignEdit/CommandViewer.razor +++ b/UI/Features/CampaignEdit/Views/CommandViewer.razor @@ -1,97 +1,97 @@ -@using Ares.Datamodel.Templates -@using Ares.Services.Device - -@inject AresDevices.AresDevicesClient DevicesClient - -@if (CommandTemplate is not null) -{ - if (CommandTemplate.Metadata?.Name is not null) - { -
-
- @DeviceName -
-
- @CommandTemplate.Metadata.Name -
- @if (CommandTemplate.Parameters.Any()) - { - - } - @foreach (var argument in CommandTemplate.Parameters) - { - @if (argument.PlanningMetadata is not null) - { -
- @TryUnpackDisplayData(argument) -
- } - - else if(argument.VariableType > 0) - { -
- @argument.VariableType.ToString() -
- } - else - { - if (argument.Value is null) - { -
- Error! Parameter Deleted! -
- } - - else - { -
- @TryUnpackDisplayData(argument) -
- } - } - } -
- } - else - { - Undefined Command :^( - } -} -@code { - [Parameter] - public CommandTemplate? CommandTemplate { get; set; } - - private string DeviceName { get; set; } = ""; - - public object TryUnpackDisplayData(Parameter argument) - { - if(argument.PlanningMetadata is not null) - { - return $"{argument.Metadata.Name}: {argument.PlanningMetadata.Name}"; - } - var aresValue = argument.Value; - if(aresValue.HasNumberValue) - return $"{argument.Metadata.Name}: {aresValue.NumberValue} {argument.Metadata.Unit}"; - - else if(aresValue.HasStringValue) - return $"{argument.Metadata.Name}: {aresValue.StringValue} {argument.Metadata.Unit}"; - - else - return $"{argument.Metadata.Name}: Custom Data"; - } - - private async Task GetDeviceName(string deviceId) - { - var info = await DevicesClient.GetDeviceInfoAsync(new DeviceInfoRequest { DeviceId = deviceId }); - - return info.Name; - } - - protected override async Task OnInitializedAsync() - { - if(CommandTemplate is null) - return; - - DeviceName = await GetDeviceName(CommandTemplate.Metadata.DeviceId); - } -} +@using Ares.Datamodel.Templates +@using Ares.Services.Device + +@inject AresDevices.AresDevicesClient DevicesClient + +@if (CommandTemplate is not null) +{ + if (CommandTemplate.Metadata?.Name is not null) + { +
+
+ @DeviceName +
+
+ @CommandTemplate.Metadata.Name +
+ @if (CommandTemplate.Parameters.Any()) + { + + } + @foreach (var argument in CommandTemplate.Parameters) + { + @if (argument.PlanningMetadata is not null) + { +
+ @TryUnpackDisplayData(argument) +
+ } + + else if(argument.VariableType > 0) + { +
+ @argument.VariableType.ToString() +
+ } + else + { + if (argument.Value is null) + { +
+ Error! Parameter Deleted! +
+ } + + else + { +
+ @TryUnpackDisplayData(argument) +
+ } + } + } +
+ } + else + { + Undefined Command :^( + } +} +@code { + [Parameter] + public CommandTemplate? CommandTemplate { get; set; } + + private string DeviceName { get; set; } = ""; + + public object TryUnpackDisplayData(Parameter argument) + { + if(argument.PlanningMetadata is not null) + { + return $"{argument.Metadata.Name}: {argument.PlanningMetadata.Name}"; + } + var aresValue = argument.Value; + if(aresValue.HasNumberValue) + return $"{argument.Metadata.Name}: {aresValue.NumberValue} {argument.Metadata.Unit}"; + + else if(aresValue.HasStringValue) + return $"{argument.Metadata.Name}: {aresValue.StringValue} {argument.Metadata.Unit}"; + + else + return $"{argument.Metadata.Name}: Custom Data"; + } + + private async Task GetDeviceName(string deviceId) + { + var info = await DevicesClient.GetDeviceInfoAsync(new DeviceInfoRequest { DeviceId = deviceId }); + + return info.Name; + } + + protected override async Task OnInitializedAsync() + { + if(CommandTemplate is null) + return; + + DeviceName = await GetDeviceName(CommandTemplate.Metadata.DeviceId); + } +} diff --git a/UI/Pages/Shared/CampaignEdit/ExperimentDesigner.razor b/UI/Features/CampaignEdit/Views/ExperimentDesigner.razor similarity index 97% rename from UI/Pages/Shared/CampaignEdit/ExperimentDesigner.razor rename to UI/Features/CampaignEdit/Views/ExperimentDesigner.razor index 22fcd4c7..57aa7fbc 100644 --- a/UI/Pages/Shared/CampaignEdit/ExperimentDesigner.razor +++ b/UI/Features/CampaignEdit/Views/ExperimentDesigner.razor @@ -1,74 +1,75 @@ -@inject DialogService DialogService -@using Ares.Datamodel.Templates -@using UI.Backend.ViewModels.Automation.CampaignEdit -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
-
-
- @foreach (var stepDesigner in ViewModel!.StepDesigners) - { -
-
- - -
-
- -
-
- } -
- -
-
- - -@code { - [Parameter] - public ExperimentTemplate? ExperimentTemplate { get; set; } - - private void RemoveStepCallback(StepDesignerViewModel vm) - { - ViewModel!.RemoveStep(vm); - StateHasChanged(); - } - - private async void AddStepClick() - { - var stepDesigner = ViewModel!.AddStep(); - string name = stepDesigner.Name; - bool isParallel = stepDesigner.Parallel; - var result = await DialogService.OpenAsync("Step Create", ds => - @
- -
- - -
-
- ); - if (result ?? false) - { - stepDesigner.Name = name; - stepDesigner.Parallel = isParallel; - } - else - { - ViewModel!.RemoveStep(stepDesigner); - StateHasChanged(); - } - } - -} -@code { - -} +@inject DialogService DialogService +@using Ares.Datamodel.Templates +@using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+
+
+ @foreach (var stepDesigner in ViewModel!.StepDesigners) + { +
+
+ + +
+
+ +
+
+ } +
+ +
+
+ + +@code { + [Parameter] + public ExperimentTemplate? ExperimentTemplate { get; set; } + + private void RemoveStepCallback(StepDesignerViewModel vm) + { + ViewModel!.RemoveStep(vm); + StateHasChanged(); + } + + private async void AddStepClick() + { + var stepDesigner = ViewModel!.AddStep(); + string name = stepDesigner.Name; + bool isParallel = stepDesigner.Parallel; + var result = await DialogService.OpenAsync("Step Create", ds => + @
+ +
+ + +
+
+ ); + if (result ?? false) + { + stepDesigner.Name = name; + stepDesigner.Parallel = isParallel; + } + else + { + ViewModel!.RemoveStep(stepDesigner); + StateHasChanged(); + } + } + +} +@code { + +} diff --git a/UI/Pages/Shared/CampaignEdit/ExperimentViewer.razor b/UI/Features/CampaignEdit/Views/ExperimentViewer.razor similarity index 100% rename from UI/Pages/Shared/CampaignEdit/ExperimentViewer.razor rename to UI/Features/CampaignEdit/Views/ExperimentViewer.razor diff --git a/UI/Pages/Shared/CampaignEdit/MetadataPicker.razor b/UI/Features/CampaignEdit/Views/MetadataPicker.razor similarity index 100% rename from UI/Pages/Shared/CampaignEdit/MetadataPicker.razor rename to UI/Features/CampaignEdit/Views/MetadataPicker.razor diff --git a/UI/Pages/Shared/CampaignEdit/PlannableParameterDesigner.razor b/UI/Features/CampaignEdit/Views/PlannableParameterDesigner.razor similarity index 94% rename from UI/Pages/Shared/CampaignEdit/PlannableParameterDesigner.razor rename to UI/Features/CampaignEdit/Views/PlannableParameterDesigner.razor index b895394f..65e93e73 100644 --- a/UI/Pages/Shared/CampaignEdit/PlannableParameterDesigner.razor +++ b/UI/Features/CampaignEdit/Views/PlannableParameterDesigner.razor @@ -1,22 +1,23 @@ -@using UI.Backend.ViewModels.Automation.CampaignEdit -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
- @foreach (var parameterEditorVm in ViewModel!.ParameterEditors) - { - - } -
- -
-
- -@code { - private void NewParameterClick() => ViewModel!.Create(); - - private void DeleteParameterCallback(ParameterEditorViewModel paramEditor) - { - ViewModel!.Remove(paramEditor); - StateHasChanged(); - } +@using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+ @foreach (var parameterEditorVm in ViewModel!.ParameterEditors) + { + + } +
+ +
+
+ +@code { + private void NewParameterClick() => ViewModel!.Create(); + + private void DeleteParameterCallback(ParameterEditorViewModel paramEditor) + { + ViewModel!.Remove(paramEditor); + StateHasChanged(); + } } \ No newline at end of file diff --git a/UI/Pages/Shared/CampaignEdit/PlannableParameterEditor.razor b/UI/Features/CampaignEdit/Views/PlannableParameterEditor.razor similarity index 97% rename from UI/Pages/Shared/CampaignEdit/PlannableParameterEditor.razor rename to UI/Features/CampaignEdit/Views/PlannableParameterEditor.razor index b97eff36..0edd628a 100644 --- a/UI/Pages/Shared/CampaignEdit/PlannableParameterEditor.razor +++ b/UI/Features/CampaignEdit/Views/PlannableParameterEditor.razor @@ -1,113 +1,113 @@ -@using Ares.Datamodel -@using UI.Backend.ViewModels.Automation.CampaignEdit -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
-
-
- - - -
- -
- -
- - - -
- - @if(ViewModel!.DataType == AresDataType.Number) - { -
-
- - - -
- -
- - - -
-
- -
-
- - - -
- -
- - - -
-
- } - - @if(ViewModel!.HasInitialValue) - { - - } - - -
- - - - - - - - - - - - @if (ViewModel!.AvailableOutputs is not null && ViewModel!.HasAchievedValue) - { -
- - - -
- } -
- -@code { - - private IEnumerable? FilteredTypes; - - [Parameter] - public Action DeleteParameterCallback { get; set; } = delegate { }; - - private void DeleteParameterClick() - { - DeleteParameterCallback(ViewModel!); - } - - private Task ValueEditCallback(AresValue value) - { - //Not sure what scenario this would be to be honest - if(value is null) - return Task.CompletedTask; - - if(ViewModel!.InitialValue is null) - ViewModel!.InitialValue = new AresValue(); - - ViewModel!.InitialValue = value; - return Task.CompletedTask; - } - - protected override void OnInitialized() - { - FilteredTypes = Enum.GetValues(typeof(AresDataType)).Cast().Where(s => s != AresDataType.Null && s != AresDataType.UnspecifiedType); - } +@using Ares.Datamodel +@using UI.Backend.ViewModels.Automation.CampaignEdit +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+
+
+ + + +
+ +
+ +
+ + + +
+ + @if(ViewModel!.DataType == AresDataType.Number) + { +
+
+ + + +
+ +
+ + + +
+
+ +
+
+ + + +
+ +
+ + + +
+
+ } + + @if(ViewModel!.HasInitialValue) + { + + } + + +
+ + + + + + + + + + + + @if (ViewModel!.AvailableOutputs is not null && ViewModel!.HasAchievedValue) + { +
+ + + +
+ } +
+ +@code { + + private IEnumerable? FilteredTypes; + + [Parameter] + public Action DeleteParameterCallback { get; set; } = delegate { }; + + private void DeleteParameterClick() + { + DeleteParameterCallback(ViewModel!); + } + + private Task ValueEditCallback(AresValue value) + { + //Not sure what scenario this would be to be honest + if(value is null) + return Task.CompletedTask; + + if(ViewModel!.InitialValue is null) + ViewModel!.InitialValue = new AresValue(); + + ViewModel!.InitialValue = value; + return Task.CompletedTask; + } + + protected override void OnInitialized() + { + FilteredTypes = Enum.GetValues(typeof(AresDataType)).Cast().Where(s => s != AresDataType.Null && s != AresDataType.UnspecifiedType); + } } \ No newline at end of file diff --git a/UI/Pages/Shared/CampaignEdit/PlannerAllocationEditor.razor b/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor similarity index 97% rename from UI/Pages/Shared/CampaignEdit/PlannerAllocationEditor.razor rename to UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor index cb2f88fc..303fac81 100644 --- a/UI/Pages/Shared/CampaignEdit/PlannerAllocationEditor.razor +++ b/UI/Features/CampaignEdit/Views/PlannerAllocationEditor.razor @@ -1,56 +1,56 @@ -@using Ares.Datamodel.Planning; -@inject DialogService DialogService -@inherits ReactiveUI.Blazor.ReactiveComponentBase - -
- - - - @ViewModel!.ParameterMetadata.Name - - - - - - - - - - @if(ViewModel!.PlannerOptions.Count() > 1) - { - - - - - - @context.PlannerName - - - - } - - - - Planner Description: - @(ViewModel!.SelectedPlannerOption?.Description ?? ViewModel!.SelectedService?.Description ?? string.Empty) - - - -
- -@code { - private async Task ShowPlannerOptionTip() - { - await DialogService.OpenAsync("Planner Selection", ds => - @
- In ARES, some planner services report the capability of using multiple types of planners. - In this scenario, a seperate dropdown allows you to choose what planning method to use from the services advertised options. -
- , new DialogOptions() { CloseDialogOnOverlayClick = true }); - } - -} +@using Ares.Datamodel.Planning; +@inject DialogService DialogService +@inherits ReactiveUI.Blazor.ReactiveComponentBase + +
+ + + + @ViewModel!.ParameterMetadata.Name + + + + + + + + + + @if(ViewModel!.PlannerOptions.Count() > 1) + { + + + + + + @context.PlannerName + + + + } + + + + Planner Description: + @(ViewModel!.SelectedPlannerOption?.Description ?? ViewModel!.SelectedService?.Description ?? string.Empty) + + + +
+ +@code { + private async Task ShowPlannerOptionTip() + { + await DialogService.OpenAsync("Planner Selection", ds => + @
+ In ARES, some planner services report the capability of using multiple types of planners. + In this scenario, a seperate dropdown allows you to choose what planning method to use from the services advertised options. +
+ , new DialogOptions() { CloseDialogOnOverlayClick = true }); + } + +} diff --git a/UI/Pages/Shared/CampaignEdit/StartupScriptDesigner.razor b/UI/Features/CampaignEdit/Views/StartupScriptDesigner.razor similarity index 98% rename from UI/Pages/Shared/CampaignEdit/StartupScriptDesigner.razor rename to UI/Features/CampaignEdit/Views/StartupScriptDesigner.razor index f21c3de9..6e4eaf7d 100644 --- a/UI/Pages/Shared/CampaignEdit/StartupScriptDesigner.razor +++ b/UI/Features/CampaignEdit/Views/StartupScriptDesigner.razor @@ -1,6 +1,7 @@ @inject DialogService DialogService @using Ares.Datamodel.Templates @using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Pages/Shared/CampaignEdit/StepDesigner.razor b/UI/Features/CampaignEdit/Views/StepDesigner.razor similarity index 99% rename from UI/Pages/Shared/CampaignEdit/StepDesigner.razor rename to UI/Features/CampaignEdit/Views/StepDesigner.razor index bea52dac..efac9fe7 100644 --- a/UI/Pages/Shared/CampaignEdit/StepDesigner.razor +++ b/UI/Features/CampaignEdit/Views/StepDesigner.razor @@ -1,5 +1,6 @@ @inject DialogService DialogService @using UI.Backend.ViewModels.Automation.CampaignEdit +@using UI.Features.CampaignEdit.ViewModels @inherits ReactiveUI.Blazor.ReactiveComponentBase
diff --git a/UI/Pages/Shared/CampaignEdit/StepPropertyEditor.razor b/UI/Features/CampaignEdit/Views/StepPropertyEditor.razor similarity index 97% rename from UI/Pages/Shared/CampaignEdit/StepPropertyEditor.razor rename to UI/Features/CampaignEdit/Views/StepPropertyEditor.razor index 695806ad..f3cde674 100644 --- a/UI/Pages/Shared/CampaignEdit/StepPropertyEditor.razor +++ b/UI/Features/CampaignEdit/Views/StepPropertyEditor.razor @@ -1,39 +1,39 @@ -
-
-
- Name -
- -
-
- - -
-
- -@code { - [Parameter] - public string? Name { get; set; } - - [Parameter] - public EventCallback NameChanged { get; set; } - - [Parameter] - public bool IsParallel { get; set; } - - [Parameter] - public EventCallback IsParallelChanged { get; set; } - - private void ParallelChangedCallback(ChangeEventArgs obj) - { - IsParallelChanged.InvokeAsync((bool)(obj?.Value ?? throw new InvalidOperationException())); - } - - private void NameChangedCallback(ChangeEventArgs obj) - { - NameChanged.InvokeAsync(obj?.Value?.ToString() ?? throw new InvalidOperationException()); - } - -} +
+
+
+ Name +
+ +
+
+ + +
+
+ +@code { + [Parameter] + public string? Name { get; set; } + + [Parameter] + public EventCallback NameChanged { get; set; } + + [Parameter] + public bool IsParallel { get; set; } + + [Parameter] + public EventCallback IsParallelChanged { get; set; } + + private void ParallelChangedCallback(ChangeEventArgs obj) + { + IsParallelChanged.InvokeAsync((bool)(obj?.Value ?? throw new InvalidOperationException())); + } + + private void NameChangedCallback(ChangeEventArgs obj) + { + NameChanged.InvokeAsync(obj?.Value?.ToString() ?? throw new InvalidOperationException()); + } + +} diff --git a/UI/Pages/Shared/CampaignEdit/StepViewer.razor b/UI/Features/CampaignEdit/Views/StepViewer.razor similarity index 99% rename from UI/Pages/Shared/CampaignEdit/StepViewer.razor rename to UI/Features/CampaignEdit/Views/StepViewer.razor index bb2befb4..973268c1 100644 --- a/UI/Pages/Shared/CampaignEdit/StepViewer.razor +++ b/UI/Features/CampaignEdit/Views/StepViewer.razor @@ -14,9 +14,9 @@ @CommandTemplates.Count
- @if(HasExperimentOutputs) - { - + @if(HasExperimentOutputs) + { + }
diff --git a/UI/Pages/Automation/ExecutionHistory.razor b/UI/Features/ExecutionHistory/ExecutionHistory.razor similarity index 97% rename from UI/Pages/Automation/ExecutionHistory.razor rename to UI/Features/ExecutionHistory/ExecutionHistory.razor index b4de0da9..7f7ad8b9 100644 --- a/UI/Pages/Automation/ExecutionHistory.razor +++ b/UI/Features/ExecutionHistory/ExecutionHistory.razor @@ -1,62 +1,62 @@ -@page "/automation/executionhistory" -@using Ares.Services -@using UI.Backend.Helpers -@inject ContextMenuService ContextMenuService -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -Execution History - -

Execution History

- - - -@if(ViewModel!.LoadingExecutionHistory) -{ - -} - -else -{ - @if(ViewModel!.CampaignSummaries.Any()) - { - - - - - - - - - - - - - } - - else - { -

No Execution History to Display

- } -} - -
- - -@code { - LogicalFilterOperator logicalFilterOperator = LogicalFilterOperator.And; - FilterCaseSensitivity filterCaseSensitivity = FilterCaseSensitivity.CaseInsensitive; - - protected override async Task OnInitializedAsync() - { - ViewModel!.LoadingExecutionHistory = true; - await ViewModel!.UpdateExecutionSummaries(); - ViewModel!.LoadingExecutionHistory = false; - StateHasChanged(); - } +@page "/automation/executionhistory" +@using Ares.Services +@using UI.Backend.Helpers +@inject ContextMenuService ContextMenuService +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +Execution History + +

Execution History

+ + + +@if(ViewModel!.LoadingExecutionHistory) +{ + +} + +else +{ + @if(ViewModel!.CampaignSummaries.Any()) + { + + + + + + + + + + + + + } + + else + { +

No Execution History to Display

+ } +} + +
+ + +@code { + LogicalFilterOperator logicalFilterOperator = LogicalFilterOperator.And; + FilterCaseSensitivity filterCaseSensitivity = FilterCaseSensitivity.CaseInsensitive; + + protected override async Task OnInitializedAsync() + { + ViewModel!.LoadingExecutionHistory = true; + await ViewModel!.UpdateExecutionSummaries(); + ViewModel!.LoadingExecutionHistory = false; + StateHasChanged(); + } } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Automation/ExecutionHistoryViewModel.cs b/UI/Features/ExecutionHistory/ExecutionHistoryViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Automation/ExecutionHistoryViewModel.cs rename to UI/Features/ExecutionHistory/ExecutionHistoryViewModel.cs index 691d2f40..8fcad154 100644 --- a/UI/Backend/ViewModels/Automation/ExecutionHistoryViewModel.cs +++ b/UI/Features/ExecutionHistory/ExecutionHistoryViewModel.cs @@ -1,34 +1,34 @@ -using Ares.Services; -using DynamicData; -using Google.Protobuf.WellKnownTypes; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Automation; - -public partial class ExecutionHistoryViewModel : ReactiveObject -{ - private readonly AresAutomation.AresAutomationClient _automationClient; - - public ExecutionHistoryViewModel(AresAutomation.AresAutomationClient automationClient) - { - _automationClient = automationClient; - CampaignSummaries = []; - LoadingExecutionHistory = false; - } - - public async Task UpdateExecutionSummaries() - { - LoadingExecutionHistory = true; - CampaignSummaries.Clear(); - var response = await _automationClient.GetAvailableCampaignExecutionSummariesAsync(new Empty()); - CampaignSummaries.AddRange(response.AvailableCampaignSummaries); - LoadingExecutionHistory = false; - } - - [Reactive] - public partial IList CampaignSummaries { get; set; } - - [Reactive] - public partial bool LoadingExecutionHistory { get; set; } +using Ares.Services; +using DynamicData; +using Google.Protobuf.WellKnownTypes; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace UI.Backend.ViewModels.Automation; + +public partial class ExecutionHistoryViewModel : ReactiveObject +{ + private readonly AresAutomation.AresAutomationClient _automationClient; + + public ExecutionHistoryViewModel(AresAutomation.AresAutomationClient automationClient) + { + _automationClient = automationClient; + CampaignSummaries = []; + LoadingExecutionHistory = false; + } + + public async Task UpdateExecutionSummaries() + { + LoadingExecutionHistory = true; + CampaignSummaries.Clear(); + var response = await _automationClient.GetAvailableCampaignExecutionSummariesAsync(new Empty()); + CampaignSummaries.AddRange(response.AvailableCampaignSummaries); + LoadingExecutionHistory = false; + } + + [Reactive] + public partial IList CampaignSummaries { get; set; } + + [Reactive] + public partial bool LoadingExecutionHistory { get; set; } } \ No newline at end of file diff --git a/UI/Pages/Automation/Execution.razor b/UI/Pages/Automation/Execution.razor index 353f61ba..9eb64b5b 100644 --- a/UI/Pages/Automation/Execution.razor +++ b/UI/Pages/Automation/Execution.razor @@ -7,7 +7,6 @@ @using Radzen.Blazor @using UI.Backend.Extensions @using UI.Infrastructure.Interfaces -@using UI.Pages.Shared.CampaignEdit @using UI.Pages.Shared.Execution.Planning @inject ContextMenuService ContextMenuService @inject AresAutomation.AresAutomationClient AutomationClient diff --git a/UI/Pages/Automation/Planning.razor b/UI/Pages/Automation/Planning.razor index a3616b80..82d2dbb1 100644 --- a/UI/Pages/Automation/Planning.razor +++ b/UI/Pages/Automation/Planning.razor @@ -1,5 +1,5 @@ -@using UI.Pages.Shared.CampaignEdit -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Features.CampaignEdit.ViewModels +@inherits ReactiveUI.Blazor.ReactiveComponentBase
diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index f9615ffa..239f45ad 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -28,7 +28,6 @@ using UI.Backend.ViewModels; using UI.Backend.ViewModels.Automation; using UI.Backend.ViewModels.Automation.CampaignEdit; -using UI.Backend.ViewModels.Automation.CampaignEdit.Factories; using UI.Backend.ViewModels.Automation.Planning; using UI.Backend.ViewModels.Misc; using UI.Backend.ViewModels.Settings.Analysis; @@ -48,6 +47,9 @@ using UI.Backend.ViewModels.Settings.Device.VerdiLaser; using UI.Backend.ViewModels.Settings.Logging; using UI.Backend.ViewModels.Settings.Planning; +using UI.Features.Auth; +using UI.Features.CampaignEdit.Factories; +using UI.Features.CampaignEdit.ViewModels; using UI.Features.Devices.ChemyxPump; using UI.Features.Devices.CM3Camera; using UI.Features.Devices.Mfc; diff --git a/UI/UI.csproj b/UI/UI.csproj index 4b91b4b4..f605669e 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -7,6 +7,14 @@ aspnet-UI-87962D8C-B573-4F3B-852F-E1DA10F52308 + + + + + + + + @@ -38,10 +46,8 @@ - + - - From 97c134cb30f9581b26aa17d0d4932acb8d48ac4a Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 12:37:40 -0500 Subject: [PATCH 04/17] Execution, Analysis, and Planner component moves. --- .../Automation/CustomStepBuilderViewModel.cs | 7 - .../Settings}/AnalyzerConfigEditView.razor | 0 .../AnalyzerSettingsEditorView.razor | 0 .../Settings}/AnalyzerSettingsList.razor | 2 +- .../Settings}/AnalyzerSettingsView.razor | 1 - .../Layout/AnalyzerSettingsContainer.razor | 0 .../AnalyzerSettingsContainer.razor.css | 0 .../Layout/AnalyzerSettingsLayout.razor | 0 .../CampaignEdit/CampaignEditContext.cs | 28 +- .../CommandParameterDesignerFactory.cs | 1 - .../ViewModels/CampaignDesignerViewModel.cs | 2 +- .../Views}/AnalyzerInputDesigner.razor | 0 .../{ => Views}/CampaignDesigner.razor | 0 .../Views/CommandDesignerQuickView.razor | 1 - .../Views}/ExperimentOutputIcon.razor | 0 .../CampaignEdit/Views}/Planning.razor | 30 +- .../ManualDeviceLoggerWidget.razor} | 168 ++-- .../ManualDeviceLoggerWidget.razor.css} | 34 +- .../ManualDeviceLoggerWidgetViewModel.cs} | 6 +- .../Execution}/Execution.razor | 880 +++++++++--------- .../Execution}/Execution.razor.css | 138 +-- .../Execution}/ExecutionViewModel.cs | 0 .../Planning/ManualPlannerDisplayObject.cs | 2 +- .../Planning/ManualPlannerViewModel.cs | 225 ++--- UI/Pages/Automation/CustomStepBuilder.razor | 27 - UI/Pages/Index.razor | 1 - .../Execution/Planning/ManualPlanEditor.razor | 1 + UI/ServiceCollectionExtensions.cs | 6 +- UI/UI.csproj | 4 +- 29 files changed, 763 insertions(+), 801 deletions(-) delete mode 100644 UI/Backend/ViewModels/Automation/CustomStepBuilderViewModel.cs rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerConfigEditView.razor (100%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsEditorView.razor (100%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsList.razor (97%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsView.razor (98%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/Layout/AnalyzerSettingsContainer.razor (100%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/Layout/AnalyzerSettingsContainer.razor.css (100%) rename UI/{Pages/Shared/Settings/Analysis => Features/Analyzing/Settings}/Layout/AnalyzerSettingsLayout.razor (100%) rename UI/{Services => Features}/CampaignEdit/CampaignEditContext.cs (92%) rename UI/{Pages/Automation => Features/CampaignEdit/Views}/AnalyzerInputDesigner.razor (100%) rename UI/Features/CampaignEdit/{ => Views}/CampaignDesigner.razor (100%) rename UI/{Pages/Resources => Features/CampaignEdit/Views}/ExperimentOutputIcon.razor (100%) rename UI/{Pages/Automation => Features/CampaignEdit/Views}/Planning.razor (95%) rename UI/{Pages/Shared/Misc/ManualExecutionWidget.razor => Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor} (89%) rename UI/{Pages/Shared/Misc/ManualExecutionWidget.razor.css => Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor.css} (92%) rename UI/{Backend/ViewModels/Misc/ManualExecutionWidgetViewModel.cs => Features/DeviceStateLogging/ManualDeviceLoggerWidgetViewModel.cs} (87%) rename UI/{Pages/Automation => Features/Execution}/Execution.razor (97%) rename UI/{Pages/Automation => Features/Execution}/Execution.razor.css (96%) rename UI/{Backend/ViewModels/Automation => Features/Execution}/ExecutionViewModel.cs (100%) rename UI/{Backend/ViewModels/Automation => Features}/Planning/ManualPlannerDisplayObject.cs (79%) rename UI/{Backend/ViewModels/Automation => Features}/Planning/ManualPlannerViewModel.cs (96%) delete mode 100644 UI/Pages/Automation/CustomStepBuilder.razor diff --git a/UI/Backend/ViewModels/Automation/CustomStepBuilderViewModel.cs b/UI/Backend/ViewModels/Automation/CustomStepBuilderViewModel.cs deleted file mode 100644 index 52ea6e50..00000000 --- a/UI/Backend/ViewModels/Automation/CustomStepBuilderViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -using ReactiveUI; - -namespace UI.Backend.ViewModels.Automation; - -public class CustomStepBuilderViewModel : ReactiveObject -{ -} \ No newline at end of file diff --git a/UI/Pages/Shared/Settings/Analysis/AnalyzerConfigEditView.razor b/UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Analysis/AnalyzerConfigEditView.razor rename to UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor diff --git a/UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsEditorView.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor similarity index 100% rename from UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsEditorView.razor rename to UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor diff --git a/UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsList.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor similarity index 97% rename from UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsList.razor rename to UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor index 69ec813c..71326ae9 100644 --- a/UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsList.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor @@ -2,7 +2,7 @@ @using Ares.Services @using Google.Protobuf.WellKnownTypes @using UI.Backend.ViewModels.Settings.Analysis -@using UI.Pages.Shared.Settings.Analysis.Layout +@using UI.Features.Analyzing.Settings.Layout @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @layout AnalyzerSettingsLayout diff --git a/UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsView.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor similarity index 98% rename from UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsView.razor rename to UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor index b80f4260..36bfb645 100644 --- a/UI/Pages/Shared/Settings/Analysis/AnalyzerSettingsView.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor @@ -1,6 +1,5 @@ @using Ares.Services @using Google.Protobuf.WellKnownTypes -@using UI.Pages.Shared.Settings.Analysis.Layout @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService diff --git a/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsContainer.razor b/UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsContainer.razor similarity index 100% rename from UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsContainer.razor rename to UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsContainer.razor diff --git a/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsContainer.razor.css b/UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsContainer.razor.css similarity index 100% rename from UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsContainer.razor.css rename to UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsContainer.razor.css diff --git a/UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor b/UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsLayout.razor similarity index 100% rename from UI/Pages/Shared/Settings/Analysis/Layout/AnalyzerSettingsLayout.razor rename to UI/Features/Analyzing/Settings/Layout/AnalyzerSettingsLayout.razor diff --git a/UI/Services/CampaignEdit/CampaignEditContext.cs b/UI/Features/CampaignEdit/CampaignEditContext.cs similarity index 92% rename from UI/Services/CampaignEdit/CampaignEditContext.cs rename to UI/Features/CampaignEdit/CampaignEditContext.cs index e676ea48..37416b59 100644 --- a/UI/Services/CampaignEdit/CampaignEditContext.cs +++ b/UI/Features/CampaignEdit/CampaignEditContext.cs @@ -1,14 +1,14 @@ -using Ares.Datamodel.Templates; - -namespace UI.Services.CampaignEdit; - -/// -/// Kind of a hacky way to give certain designers a way to reference any necessary info from the campaign itself. -/// Ex.: Campaign template has plannable parameters, but the command parameter designer does not have a way to get to those -/// unless they are passed through all the way from the campaign template to those designers or if we use something -/// like this context in scoped context (one scope can edit one campaign at a time anyways). -/// -public class CampaignEditContext -{ - public CampaignTemplate? CurrentlyEditingCampaign { get; set; } -} +using Ares.Datamodel.Templates; + +namespace UI.Features.CampaignEdit; + +/// +/// Kind of a hacky way to give certain designers a way to reference any necessary info from the campaign itself. +/// Ex.: Campaign template has plannable parameters, but the command parameter designer does not have a way to get to those +/// unless they are passed through all the way from the campaign template to those designers or if we use something +/// like this context in scoped context (one scope can edit one campaign at a time anyways). +/// +public class CampaignEditContext +{ + public CampaignTemplate? CurrentlyEditingCampaign { get; set; } +} diff --git a/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs index 69bb9cbd..826ecfef 100644 --- a/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs @@ -1,7 +1,6 @@ using Ares.Datamodel.Templates; using UI.Backend.Helpers; using UI.Features.CampaignEdit.ViewModels; -using UI.Services.CampaignEdit; namespace UI.Features.CampaignEdit.Factories; diff --git a/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs index 28654e0a..a32993f7 100644 --- a/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CampaignDesignerViewModel.cs @@ -2,9 +2,9 @@ using Ares.Services; using ReactiveUI; using ReactiveUI.SourceGenerators; +using UI.Features.CampaignEdit; using UI.Features.CampaignEdit.Factories; using UI.Features.CampaignEdit.ViewModels; -using UI.Services.CampaignEdit; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Pages/Automation/AnalyzerInputDesigner.razor b/UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor similarity index 100% rename from UI/Pages/Automation/AnalyzerInputDesigner.razor rename to UI/Features/CampaignEdit/Views/AnalyzerInputDesigner.razor diff --git a/UI/Features/CampaignEdit/CampaignDesigner.razor b/UI/Features/CampaignEdit/Views/CampaignDesigner.razor similarity index 100% rename from UI/Features/CampaignEdit/CampaignDesigner.razor rename to UI/Features/CampaignEdit/Views/CampaignDesigner.razor diff --git a/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor b/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor index e29ff97d..bd2521e4 100644 --- a/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor +++ b/UI/Features/CampaignEdit/Views/CommandDesignerQuickView.razor @@ -1,7 +1,6 @@ @using Ares.Datamodel.Templates @using UI.Backend.ViewModels.Automation.CampaignEdit @using UI.Infrastructure.Interfaces -@using UI.Pages.Resources @inject IUiNotificationService notificationService; diff --git a/UI/Pages/Resources/ExperimentOutputIcon.razor b/UI/Features/CampaignEdit/Views/ExperimentOutputIcon.razor similarity index 100% rename from UI/Pages/Resources/ExperimentOutputIcon.razor rename to UI/Features/CampaignEdit/Views/ExperimentOutputIcon.razor diff --git a/UI/Pages/Automation/Planning.razor b/UI/Features/CampaignEdit/Views/Planning.razor similarity index 95% rename from UI/Pages/Automation/Planning.razor rename to UI/Features/CampaignEdit/Views/Planning.razor index 82d2dbb1..46213ad3 100644 --- a/UI/Pages/Automation/Planning.razor +++ b/UI/Features/CampaignEdit/Views/Planning.razor @@ -1,16 +1,16 @@ -@using UI.Features.CampaignEdit.ViewModels -@inherits ReactiveUI.Blazor.ReactiveComponentBase - - -
-

Planning

- @foreach (var editor in ViewModel!.PlannerAllocationEditors) - { - - } -
- - -@code { - +@using UI.Features.CampaignEdit.ViewModels +@inherits ReactiveUI.Blazor.ReactiveComponentBase + + +
+

Planning

+ @foreach (var editor in ViewModel!.PlannerAllocationEditors) + { + + } +
+ + +@code { + } \ No newline at end of file diff --git a/UI/Pages/Shared/Misc/ManualExecutionWidget.razor b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor similarity index 89% rename from UI/Pages/Shared/Misc/ManualExecutionWidget.razor rename to UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor index 59f0b356..259249c6 100644 --- a/UI/Pages/Shared/Misc/ManualExecutionWidget.razor +++ b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor @@ -1,84 +1,84 @@ -@using ReactiveUI.Blazor; -@using System.Diagnostics; -@using Ares.Services -@using UI.Backend.ViewModels.Misc; -@using UI.Backend.Extensions; - -@inherits ReactiveInjectableComponentBase - - -
- @if (ViewModel!.IsCollecting) - { - - } - else - { - - } -
-
- @(_stopwatch.Elapsed.Minutes.ToString("00")):@(_stopwatch.Elapsed.Seconds.ToString("00")):@(_stopwatch.Elapsed.Milliseconds.ToString("000")) -
- @if (ViewModel!.HasData) - { -
- Start - @ViewModel!.CollectionStarted - End - @ViewModel!.CollectionFinished -
- } - -
-
-
-@inject IJSRuntime JS -@code { - private string _error = string.Empty; - - private Stopwatch _stopwatch = new Stopwatch(); - - private async Task StartCollecting() - { - await ViewModel!.StartDataCollection(); - await StartStopwatch(); - } - - private void StopCollecting() - { - ViewModel!.StopDataCollection(); - _stopwatch.Stop(); - } - - private async Task StartStopwatch() - { - _stopwatch.Restart(); - while (ViewModel!.IsCollecting) - { - StateHasChanged(); - await Task.Delay(100); - } - } - - private async Task Export() - { - _error = string.Empty; - var fileData = await ViewModel!.GetExportData(); - if (fileData is null) - { - _error = "Unable to get a state stream. Maybe provider wasn't selected."; - StateHasChanged(); - return; - } - - var fileName = $"{DateTime.Now}"; - fileName = Path.ChangeExtension(fileName, ExportType.Combined.ToFileExtension()); - - using var stream = new MemoryStream(fileData); - using var streamRef = new DotNetStreamReference(stream: stream); - - await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); - StateHasChanged(); - } -} +@using System.Diagnostics; +@using Ares.Services +@using ReactiveUI.Blazor; +@using UI.Backend.Extensions; +@using UI.Features.DeviceStateLogging + +@inherits ReactiveInjectableComponentBase + + +
+ @if (ViewModel!.IsCollecting) + { + + } + else + { + + } +
+
+ @(_stopwatch.Elapsed.Minutes.ToString("00")):@(_stopwatch.Elapsed.Seconds.ToString("00")):@(_stopwatch.Elapsed.Milliseconds.ToString("000")) +
+ @if (ViewModel!.HasData) + { +
+ Start + @ViewModel!.CollectionStarted + End + @ViewModel!.CollectionFinished +
+ } + +
+
+
+@inject IJSRuntime JS +@code { + private string _error = string.Empty; + + private Stopwatch _stopwatch = new Stopwatch(); + + private async Task StartCollecting() + { + await ViewModel!.StartDataCollection(); + await StartStopwatch(); + } + + private void StopCollecting() + { + ViewModel!.StopDataCollection(); + _stopwatch.Stop(); + } + + private async Task StartStopwatch() + { + _stopwatch.Restart(); + while (ViewModel!.IsCollecting) + { + StateHasChanged(); + await Task.Delay(100); + } + } + + private async Task Export() + { + _error = string.Empty; + var fileData = await ViewModel!.GetExportData(); + if (fileData is null) + { + _error = "Unable to get a state stream. Maybe provider wasn't selected."; + StateHasChanged(); + return; + } + + var fileName = $"{DateTime.Now}"; + fileName = Path.ChangeExtension(fileName, ExportType.Combined.ToFileExtension()); + + using var stream = new MemoryStream(fileData); + using var streamRef = new DotNetStreamReference(stream: stream); + + await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + StateHasChanged(); + } +} diff --git a/UI/Pages/Shared/Misc/ManualExecutionWidget.razor.css b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor.css similarity index 92% rename from UI/Pages/Shared/Misc/ManualExecutionWidget.razor.css rename to UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor.css index e12510a0..cf6a25aa 100644 --- a/UI/Pages/Shared/Misc/ManualExecutionWidget.razor.css +++ b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor.css @@ -1,18 +1,18 @@ -.stopwatch { - font-size: 1.5em; -} - -.collection-date { - font-size: 0.7em; -} - -.date-label { - font-size: 0.5em; - opacity: 0.8; - align-self: center; -} - -.date-grid { - display: grid; - grid-template-columns: 1fr auto; +.stopwatch { + font-size: 1.5em; +} + +.collection-date { + font-size: 0.7em; +} + +.date-label { + font-size: 0.5em; + opacity: 0.8; + align-self: center; +} + +.date-grid { + display: grid; + grid-template-columns: 1fr auto; } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Misc/ManualExecutionWidgetViewModel.cs b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidgetViewModel.cs similarity index 87% rename from UI/Backend/ViewModels/Misc/ManualExecutionWidgetViewModel.cs rename to UI/Features/DeviceStateLogging/ManualDeviceLoggerWidgetViewModel.cs index dbb0c8b2..db75896d 100644 --- a/UI/Backend/ViewModels/Misc/ManualExecutionWidgetViewModel.cs +++ b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidgetViewModel.cs @@ -6,15 +6,15 @@ using ReactiveUI; using DeviceStateRequest = Ares.Services.DeviceStateRequest; -namespace UI.Backend.ViewModels.Misc; +namespace UI.Features.DeviceStateLogging; -public class ManualExecutionWidgetViewModel : ReactiveObject +public class ManualDeviceLoggerWidgetViewModel : ReactiveObject { private readonly DeviceStateExportService.DeviceStateExportServiceClient _stateExportClient; readonly AresDevices.AresDevicesClient _devicesClient; private IEnumerable _activeDevices = Array.Empty(); - public ManualExecutionWidgetViewModel(DeviceStateExportService.DeviceStateExportServiceClient stateExportClient, AresDevices.AresDevicesClient devicesClient) + public ManualDeviceLoggerWidgetViewModel(DeviceStateExportService.DeviceStateExportServiceClient stateExportClient, AresDevices.AresDevicesClient devicesClient) { _stateExportClient = stateExportClient; _devicesClient = devicesClient; diff --git a/UI/Pages/Automation/Execution.razor b/UI/Features/Execution/Execution.razor similarity index 97% rename from UI/Pages/Automation/Execution.razor rename to UI/Features/Execution/Execution.razor index 9eb64b5b..7b0748da 100644 --- a/UI/Pages/Automation/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -1,440 +1,440 @@ -@page "/automation/execution" -@using System.Collections.ObjectModel -@using System.Threading.Tasks -@using Ares.Datamodel -@using Ares.Services -@using Google.Protobuf.WellKnownTypes -@using Radzen.Blazor -@using UI.Backend.Extensions -@using UI.Infrastructure.Interfaces -@using UI.Pages.Shared.Execution.Planning -@inject ContextMenuService ContextMenuService -@inject AresAutomation.AresAutomationClient AutomationClient -@inject IUiNotificationService NotificationService -@inject Radzen.DialogService DialogService -@implements IDisposable -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -Execution - -
-
-
-
- -
- -
-
- @if (ViewModel!.PlannerAdapterInfos.Any(p => p is not null)) - { -
-
Planners
- @foreach (var planner in ViewModel!.PlannerAdapterInfos.Where(info => info is not null)) - { - @if(planner!.Name == "Manual Planner") - { - - } - - else - { -
-

@planner!.Name

- @if(ViewModel!.PlannerState == PlannerState.PlanningComplete) - { - - } - else if(ViewModel!.PlannerState == PlannerState.PlanningInProgress) - { - - } - else if(ViewModel!.PlannerState == PlannerState.PlanningError) - { - - } -
- } - } -
- } - - @if(ViewModel!.AnalyzerInfo is not null && ViewModel!.AnalyzerInfo.Name != "NONE") - { -
-
Analyzer
-
-

@ViewModel!.AnalyzerInfo.Name

- - @if(ViewModel!.AnalysisState == AnalysisState.AnalysisComplete) - { - - } - else if(ViewModel!.AnalysisState == AnalysisState.AnalysisInProgress) - { - - } - else if(ViewModel!.AnalysisState == AnalysisState.AnalysisError) - { - - } -
-
- } -
-
- -
-
-
- - -
-
- - - @if (ViewModel!.DisplayExecutionSummary && (CampaignStartupStatuses.Any() || ExperimentExecutionStatuses.Any())) - { -
- @if(ExperimentExecutionStatuses.Any()) - { - @for(var i = 0; i < ExperimentExecutionStatuses.Count; i++) - { - var experimentStatus = ExperimentExecutionStatuses[i]; - var latestExperiment = i == ExperimentExecutionStatuses.Count - 1; - var hasStartup = ViewModel!.CampaignTemplate?.StartupTemplate.StepTemplates.Count != 0; - var hasCloseout = ViewModel!.CampaignTemplate?.CloseoutTemplate.StepTemplates.Count != 0; - var expDisplayString = hasStartup ? $"Experiment {i}" : $"Experiment {i + 1}"; -
    -
  • - @if(i == 0 && hasStartup) - { -
    Startup Script
    - } - - else if(hasStartup && hasCloseout && i == ViewModel!.ExperimentsToRun + 1) - { -
    Closeout Script
    - } - - else if(!hasStartup && hasCloseout && i == ViewModel!.ExperimentsToRun) - { -
    Closeout Script
    - } - - else - { -
    @expDisplayString
    - } - -
      - @foreach(var stepStatus in experimentStatus.StepExecutionStatuses) - { -
    • -
      - @stepStatus.StepName -
        - @foreach(var commandStatus in stepStatus.CommandExecutionStatuses) - { -
      • -
        @commandStatus.DeviceName: @commandStatus.CommandName -- @commandStatus.State
        -
      • - } -
      -
      -
    • - } -
    -
  • -
- } - } -
- } - - - @if (!ViewModel!.DisplayExecutionSummary) - { - @if (ViewModel!.CampaignTemplate is not null) - { -
- -
- } - } - -
-
-
-
- - - - -
- - @if (ViewModel!.CurrentStopCondition is not null) - { -
-
-
- Condition: @GenerateStopConditionDisplayName() -
-
@ViewModel!.CurrentStopCondition.Description
-
Campaign will re-plan every @ViewModel!.DesiredReplanRate.ToString() experiment(s)
-
-
- } - -
-
Desired number of experiments
- - -
- -
-
Re-Planning Rate
- - -
- -
-
Desired experiment result
-
- - - -
- -
- -
-
Experiment Notes
-
- -
- -
- -
-
Experiment Tags
- - - @((context.TagName as string)) - - - - - @* --- Section for Adding New Tags --- *@ -
- - - -
-
-
- -
- -
- - - 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 { - List campaigns = new(); - Task? _experimentStatusWatcher; - Task? _campaignStateWatcher; - CancellationTokenSource? _experimentStatusWatcherCancellationTokenSource; - CancellationTokenSource? _campaignStateWatcherCancellationTokenSource; - ObservableCollection ExperimentExecutionStatuses { get; } = new ObservableCollection(); - ObservableCollection CampaignStartupStatuses { get; } = new ObservableCollection(); - ObservableCollection CampaignCloseoutStatuses { get; } = new ObservableCollection(); - - private async Task CampaignSelected() - { - await ViewModel!.UpdateCurrentTemplate(); - } - - protected override async Task OnInitializedAsync() - { - await ViewModel!.RefreshCampaigns(); - await ViewModel!.SelectCampaignTemplate(ViewModel!.CampaignTemplate); - await ViewModel!.UpdateCurrentTemplate(); - await ViewModel!.GetAllTags(); - campaigns = ViewModel!.CampaignTemplateSummaries.ToList(); - - var currentStatus = await ViewModel!.GetCampaignExecutionStatus(); - if (currentStatus is not null) - { - foreach (var experimentStatus in currentStatus.ExperimentExecutionStatuses) - { - ExperimentExecutionStatuses.Add(experimentStatus); - } - } - _experimentStatusWatcherCancellationTokenSource = new CancellationTokenSource(); - _experimentStatusWatcher = SetupExperimentWatch(_experimentStatusWatcherCancellationTokenSource.Token); - - _campaignStateWatcherCancellationTokenSource = new CancellationTokenSource(); - _campaignStateWatcher = SetupCampaignStateWatch(_campaignStateWatcherCancellationTokenSource.Token); - } - - protected override void Dispose(bool disposing) - { - _experimentStatusWatcherCancellationTokenSource?.Cancel(); - _campaignStateWatcherCancellationTokenSource?.Cancel(); - base.Dispose(disposing); - } - - private bool ShouldDisplayExperimentSteps(int currentStatusIndex) - { - var isCurrentExperiment = currentStatusIndex == ExperimentExecutionStatuses.Count - 1; - - if (!CampaignCloseoutStatuses.Any()) - return isCurrentExperiment; - - return CampaignCloseoutStatuses.Count != CampaignStartupStatuses.Count && currentStatusIndex == ExperimentExecutionStatuses.Count - 1; - } - - private string GenerateStopConditionDisplayName() - { - if (ViewModel!.CurrentStopCondition is null) - return ""; - - if (ViewModel!.CurrentStopCondition.ActiveCondition == "NumExperimentsRun") - return "Number of Experiments"; - - else if (ViewModel!.CurrentStopCondition.ActiveCondition == "DesiredAnalysisResult") - return "Desired Analysis Result"; - - else - return ""; - } - - protected Task SetupCampaignStateWatch(CancellationToken token) - { - return Task.Run(async () => - { - Thread.CurrentThread.Name = "Campaign State Watch Thread"; - var stream = AutomationClient.GetCampaignExecutionStateStream(new Empty()); - while (await stream.ResponseStream.MoveNext(token) && !token.IsCancellationRequested) - { - var state = stream.ResponseStream.Current; - - ViewModel!.CampaignActive = state.IsActive(); - ViewModel!.CampaignPaused = state.IsPaused(); - ViewModel!.CampaignExecutionState = state.State; - ViewModel!.AnalysisState = state.AnalysisState; - ViewModel!.PlannerState = state.PlannerState; - - if(ViewModel!.CampaignExecutionState == ExecutionState.AwaitingUser) - { - await ViewModel!.ReqeustUserConfirmation(); - } - - - await InvokeAsync(() => StateHasChanged()); - } - }, token); - } - - protected Task SetupExperimentWatch(CancellationToken token) - { - return Task.Run(async () => - { - Thread.CurrentThread.Name = "Experiment State Watch Thread"; - var stream = AutomationClient.GetExecutionStatusStream(new Empty()); - while (await stream.ResponseStream.MoveNext(token) && !token.IsCancellationRequested) - { - var status = stream.ResponseStream.Current; - var existingStatus = ExperimentExecutionStatuses.FirstOrDefault(s => s.ExperimentId == status.ExperimentId); - if (existingStatus is null) - { - ExperimentExecutionStatuses.Add(status); - existingStatus = status; - continue; - } - - var incomingCommands = status.GetCommandExecutionStatuses(); - var existingCommands = existingStatus.GetCommandExecutionStatuses(); - - foreach (var existingCommand in existingCommands) - { - var newCommand = incomingCommands.FirstOrDefault(c => c.CommandId == existingCommand.CommandId); - existingCommand.State = newCommand?.State ?? ExecutionState.Undefined; - } - - await InvokeAsync(() => StateHasChanged()); - } - }, token); - } - - protected async void StartCampaign() - { - var executionEligibility = await AutomationClient.CheckExecutionEligibilityAsync(new Empty()); - - if (!executionEligibility.IsEligible) - { - NotificationService.Error(executionEligibility.Error); - return; - } - - ExperimentExecutionStatuses.Clear(); - var request = new StartCampaignRequest() { UserNotes = ViewModel!.ExecutionNotes}; - if(ViewModel!.SelectedTags is not null) - request.CampaignTags.AddRange(ViewModel!.SelectedTags); - AutomationClient.StartExecution(request); - ViewModel!.DisplayExecutionSummary = true; - } - - System.Type? GetTypeFromString(string? type) - { - return type is null ? null : System.Type.GetType($"UI.Pages.Shared.Execution.Planning.{type}"); - } - - private async Task ExecutionNotesUploaded(UploadChangeEventArgs args) - { - FileInfo? file = args.Files?.FirstOrDefault(); - if(file is null) - return; - - const long maxFileSize = 100; - using var stream = file.OpenReadStream(maxFileSize); - await ViewModel!.ExecutionNotesUploaded(stream); - } -} - - - +@page "/automation/execution" +@using System.Collections.ObjectModel +@using System.Threading.Tasks +@using Ares.Datamodel +@using Ares.Services +@using Google.Protobuf.WellKnownTypes +@using Radzen.Blazor +@using UI.Backend.Extensions +@using UI.Infrastructure.Interfaces +@using UI.Pages.Shared.Execution.Planning +@inject ContextMenuService ContextMenuService +@inject AresAutomation.AresAutomationClient AutomationClient +@inject IUiNotificationService NotificationService +@inject Radzen.DialogService DialogService +@implements IDisposable +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + +Execution + +
+
+
+
+ +
+ +
+
+ @if (ViewModel!.PlannerAdapterInfos.Any(p => p is not null)) + { +
+
Planners
+ @foreach (var planner in ViewModel!.PlannerAdapterInfos.Where(info => info is not null)) + { + @if(planner!.Name == "Manual Planner") + { + + } + + else + { +
+

@planner!.Name

+ @if(ViewModel!.PlannerState == PlannerState.PlanningComplete) + { + + } + else if(ViewModel!.PlannerState == PlannerState.PlanningInProgress) + { + + } + else if(ViewModel!.PlannerState == PlannerState.PlanningError) + { + + } +
+ } + } +
+ } + + @if(ViewModel!.AnalyzerInfo is not null && ViewModel!.AnalyzerInfo.Name != "NONE") + { +
+
Analyzer
+
+

@ViewModel!.AnalyzerInfo.Name

+ + @if(ViewModel!.AnalysisState == AnalysisState.AnalysisComplete) + { + + } + else if(ViewModel!.AnalysisState == AnalysisState.AnalysisInProgress) + { + + } + else if(ViewModel!.AnalysisState == AnalysisState.AnalysisError) + { + + } +
+
+ } +
+
+ +
+
+
+ + +
+
+ + + @if (ViewModel!.DisplayExecutionSummary && (CampaignStartupStatuses.Any() || ExperimentExecutionStatuses.Any())) + { +
+ @if(ExperimentExecutionStatuses.Any()) + { + @for(var i = 0; i < ExperimentExecutionStatuses.Count; i++) + { + var experimentStatus = ExperimentExecutionStatuses[i]; + var latestExperiment = i == ExperimentExecutionStatuses.Count - 1; + var hasStartup = ViewModel!.CampaignTemplate?.StartupTemplate.StepTemplates.Count != 0; + var hasCloseout = ViewModel!.CampaignTemplate?.CloseoutTemplate.StepTemplates.Count != 0; + var expDisplayString = hasStartup ? $"Experiment {i}" : $"Experiment {i + 1}"; +
    +
  • + @if(i == 0 && hasStartup) + { +
    Startup Script
    + } + + else if(hasStartup && hasCloseout && i == ViewModel!.ExperimentsToRun + 1) + { +
    Closeout Script
    + } + + else if(!hasStartup && hasCloseout && i == ViewModel!.ExperimentsToRun) + { +
    Closeout Script
    + } + + else + { +
    @expDisplayString
    + } + +
      + @foreach(var stepStatus in experimentStatus.StepExecutionStatuses) + { +
    • +
      + @stepStatus.StepName +
        + @foreach(var commandStatus in stepStatus.CommandExecutionStatuses) + { +
      • +
        @commandStatus.DeviceName: @commandStatus.CommandName -- @commandStatus.State
        +
      • + } +
      +
      +
    • + } +
    +
  • +
+ } + } +
+ } + + + @if (!ViewModel!.DisplayExecutionSummary) + { + @if (ViewModel!.CampaignTemplate is not null) + { +
+ +
+ } + } + +
+
+
+
+ + + + +
+ + @if (ViewModel!.CurrentStopCondition is not null) + { +
+
+
+ Condition: @GenerateStopConditionDisplayName() +
+
@ViewModel!.CurrentStopCondition.Description
+
Campaign will re-plan every @ViewModel!.DesiredReplanRate.ToString() experiment(s)
+
+
+ } + +
+
Desired number of experiments
+ + +
+ +
+
Re-Planning Rate
+ + +
+ +
+
Desired experiment result
+
+ + + +
+ +
+ +
+
Experiment Notes
+
+ +
+ +
+ +
+
Experiment Tags
+ + + @((context.TagName as string)) + + + + + @* --- Section for Adding New Tags --- *@ +
+ + + +
+
+
+ +
+ +
+ + + 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 { + List campaigns = new(); + Task? _experimentStatusWatcher; + Task? _campaignStateWatcher; + CancellationTokenSource? _experimentStatusWatcherCancellationTokenSource; + CancellationTokenSource? _campaignStateWatcherCancellationTokenSource; + ObservableCollection ExperimentExecutionStatuses { get; } = new ObservableCollection(); + ObservableCollection CampaignStartupStatuses { get; } = new ObservableCollection(); + ObservableCollection CampaignCloseoutStatuses { get; } = new ObservableCollection(); + + private async Task CampaignSelected() + { + await ViewModel!.UpdateCurrentTemplate(); + } + + protected override async Task OnInitializedAsync() + { + await ViewModel!.RefreshCampaigns(); + await ViewModel!.SelectCampaignTemplate(ViewModel!.CampaignTemplate); + await ViewModel!.UpdateCurrentTemplate(); + await ViewModel!.GetAllTags(); + campaigns = ViewModel!.CampaignTemplateSummaries.ToList(); + + var currentStatus = await ViewModel!.GetCampaignExecutionStatus(); + if (currentStatus is not null) + { + foreach (var experimentStatus in currentStatus.ExperimentExecutionStatuses) + { + ExperimentExecutionStatuses.Add(experimentStatus); + } + } + _experimentStatusWatcherCancellationTokenSource = new CancellationTokenSource(); + _experimentStatusWatcher = SetupExperimentWatch(_experimentStatusWatcherCancellationTokenSource.Token); + + _campaignStateWatcherCancellationTokenSource = new CancellationTokenSource(); + _campaignStateWatcher = SetupCampaignStateWatch(_campaignStateWatcherCancellationTokenSource.Token); + } + + protected override void Dispose(bool disposing) + { + _experimentStatusWatcherCancellationTokenSource?.Cancel(); + _campaignStateWatcherCancellationTokenSource?.Cancel(); + base.Dispose(disposing); + } + + private bool ShouldDisplayExperimentSteps(int currentStatusIndex) + { + var isCurrentExperiment = currentStatusIndex == ExperimentExecutionStatuses.Count - 1; + + if (!CampaignCloseoutStatuses.Any()) + return isCurrentExperiment; + + return CampaignCloseoutStatuses.Count != CampaignStartupStatuses.Count && currentStatusIndex == ExperimentExecutionStatuses.Count - 1; + } + + private string GenerateStopConditionDisplayName() + { + if (ViewModel!.CurrentStopCondition is null) + return ""; + + if (ViewModel!.CurrentStopCondition.ActiveCondition == "NumExperimentsRun") + return "Number of Experiments"; + + else if (ViewModel!.CurrentStopCondition.ActiveCondition == "DesiredAnalysisResult") + return "Desired Analysis Result"; + + else + return ""; + } + + protected Task SetupCampaignStateWatch(CancellationToken token) + { + return Task.Run(async () => + { + Thread.CurrentThread.Name = "Campaign State Watch Thread"; + var stream = AutomationClient.GetCampaignExecutionStateStream(new Empty()); + while (await stream.ResponseStream.MoveNext(token) && !token.IsCancellationRequested) + { + var state = stream.ResponseStream.Current; + + ViewModel!.CampaignActive = state.IsActive(); + ViewModel!.CampaignPaused = state.IsPaused(); + ViewModel!.CampaignExecutionState = state.State; + ViewModel!.AnalysisState = state.AnalysisState; + ViewModel!.PlannerState = state.PlannerState; + + if(ViewModel!.CampaignExecutionState == ExecutionState.AwaitingUser) + { + await ViewModel!.ReqeustUserConfirmation(); + } + + + await InvokeAsync(() => StateHasChanged()); + } + }, token); + } + + protected Task SetupExperimentWatch(CancellationToken token) + { + return Task.Run(async () => + { + Thread.CurrentThread.Name = "Experiment State Watch Thread"; + var stream = AutomationClient.GetExecutionStatusStream(new Empty()); + while (await stream.ResponseStream.MoveNext(token) && !token.IsCancellationRequested) + { + var status = stream.ResponseStream.Current; + var existingStatus = ExperimentExecutionStatuses.FirstOrDefault(s => s.ExperimentId == status.ExperimentId); + if (existingStatus is null) + { + ExperimentExecutionStatuses.Add(status); + existingStatus = status; + continue; + } + + var incomingCommands = status.GetCommandExecutionStatuses(); + var existingCommands = existingStatus.GetCommandExecutionStatuses(); + + foreach (var existingCommand in existingCommands) + { + var newCommand = incomingCommands.FirstOrDefault(c => c.CommandId == existingCommand.CommandId); + existingCommand.State = newCommand?.State ?? ExecutionState.Undefined; + } + + await InvokeAsync(() => StateHasChanged()); + } + }, token); + } + + protected async void StartCampaign() + { + var executionEligibility = await AutomationClient.CheckExecutionEligibilityAsync(new Empty()); + + if (!executionEligibility.IsEligible) + { + NotificationService.Error(executionEligibility.Error); + return; + } + + ExperimentExecutionStatuses.Clear(); + var request = new StartCampaignRequest() { UserNotes = ViewModel!.ExecutionNotes}; + if(ViewModel!.SelectedTags is not null) + request.CampaignTags.AddRange(ViewModel!.SelectedTags); + AutomationClient.StartExecution(request); + ViewModel!.DisplayExecutionSummary = true; + } + + System.Type? GetTypeFromString(string? type) + { + return type is null ? null : System.Type.GetType($"UI.Pages.Shared.Execution.Planning.{type}"); + } + + private async Task ExecutionNotesUploaded(UploadChangeEventArgs args) + { + FileInfo? file = args.Files?.FirstOrDefault(); + if(file is null) + return; + + const long maxFileSize = 100; + using var stream = file.OpenReadStream(maxFileSize); + await ViewModel!.ExecutionNotesUploaded(stream); + } +} + + + diff --git a/UI/Pages/Automation/Execution.razor.css b/UI/Features/Execution/Execution.razor.css similarity index 96% rename from UI/Pages/Automation/Execution.razor.css rename to UI/Features/Execution/Execution.razor.css index 178c2666..be6b2f86 100644 --- a/UI/Pages/Automation/Execution.razor.css +++ b/UI/Features/Execution/Execution.razor.css @@ -1,70 +1,70 @@ -.execution-button { - max-width: 50px; -} - -.analyzer-display { - width: 50%; -} - -.planner-display { - width: 50%; -} - -.padded-item { - padding-top: 0.5rem; -} - -.scrollable-summary { - max-height: 75vh; - overflow: auto; -} - -/* 1. The Container that holds Main Content and Drawer side-by-side */ -.drawer-layout-container { - display: flex; /* This enables the side-by-side layout */ - flex-direction: row; - width: 100%; - height: 100vh; /* Full viewport height */ - overflow: hidden; /* Prevents scrollbar on the container itself */ -} - -/* 2. Main Content Area (The part that shrinks) */ -.main-content-area { - flex-grow: 1; /* Take all available space */ - transition: width 0.3s ease; /* Smooth "shove" animation */ - overflow-y: auto; /* Scroll vertically INSIDE this area */ - min-width: 0; /* Prevents flex items from breaking */ - padding: 1rem; -} - -/* 3. The Side Drawer Wrapper (Animated Width) */ -.side-drawer { - width: 0; /* Default state: Closed (Invisible) */ - flex-shrink: 0; /* Prevent it from being squished */ - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Smooth animation */ - overflow-x: hidden; /* Hide content when width is 0 */ - background-color: var(--rz-base-background-color); /* Match Radzen theme */ - border-left: 1px solid var(--rz-border-color); /* Match Radzen border */ - padding: 0rem; - border-radius: .25rem; - height: 100vh; -} - - /* The 'Open' state - toggled via Blazor logic */ - .side-drawer.drawer-open { - width: 400px; /* Target width when open */ - } - -/* 4. The Content Wrapper (Fixed Width) */ -/* Critical: Prevents text reflow/wrapping during the slide animation */ -.drawer-content { - width: 400px; /* Must match the open width */ - height: 100%; - overflow-y: auto; /* Allow scrolling inside the drawer */ -} - -.drawer-inner-card { - height: inherit; - border: none; - border-radius: 0; +.execution-button { + max-width: 50px; +} + +.analyzer-display { + width: 50%; +} + +.planner-display { + width: 50%; +} + +.padded-item { + padding-top: 0.5rem; +} + +.scrollable-summary { + max-height: 75vh; + overflow: auto; +} + +/* 1. The Container that holds Main Content and Drawer side-by-side */ +.drawer-layout-container { + display: flex; /* This enables the side-by-side layout */ + flex-direction: row; + width: 100%; + height: 100vh; /* Full viewport height */ + overflow: hidden; /* Prevents scrollbar on the container itself */ +} + +/* 2. Main Content Area (The part that shrinks) */ +.main-content-area { + flex-grow: 1; /* Take all available space */ + transition: width 0.3s ease; /* Smooth "shove" animation */ + overflow-y: auto; /* Scroll vertically INSIDE this area */ + min-width: 0; /* Prevents flex items from breaking */ + padding: 1rem; +} + +/* 3. The Side Drawer Wrapper (Animated Width) */ +.side-drawer { + width: 0; /* Default state: Closed (Invisible) */ + flex-shrink: 0; /* Prevent it from being squished */ + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Smooth animation */ + overflow-x: hidden; /* Hide content when width is 0 */ + background-color: var(--rz-base-background-color); /* Match Radzen theme */ + border-left: 1px solid var(--rz-border-color); /* Match Radzen border */ + padding: 0rem; + border-radius: .25rem; + height: 100vh; +} + + /* The 'Open' state - toggled via Blazor logic */ + .side-drawer.drawer-open { + width: 400px; /* Target width when open */ + } + +/* 4. The Content Wrapper (Fixed Width) */ +/* Critical: Prevents text reflow/wrapping during the slide animation */ +.drawer-content { + width: 400px; /* Must match the open width */ + height: 100%; + overflow-y: auto; /* Allow scrolling inside the drawer */ +} + +.drawer-inner-card { + height: inherit; + border: none; + border-radius: 0; } \ No newline at end of file diff --git a/UI/Backend/ViewModels/Automation/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/Automation/ExecutionViewModel.cs rename to UI/Features/Execution/ExecutionViewModel.cs diff --git a/UI/Backend/ViewModels/Automation/Planning/ManualPlannerDisplayObject.cs b/UI/Features/Planning/ManualPlannerDisplayObject.cs similarity index 79% rename from UI/Backend/ViewModels/Automation/Planning/ManualPlannerDisplayObject.cs rename to UI/Features/Planning/ManualPlannerDisplayObject.cs index 8401268e..07672333 100644 --- a/UI/Backend/ViewModels/Automation/Planning/ManualPlannerDisplayObject.cs +++ b/UI/Features/Planning/ManualPlannerDisplayObject.cs @@ -1,6 +1,6 @@ using Ares.Datamodel.Planning; -namespace UI.Backend.ViewModels.Automation.Planning; +namespace UI.Features.Planning; public class ManualPlannerDisplayObject { diff --git a/UI/Backend/ViewModels/Automation/Planning/ManualPlannerViewModel.cs b/UI/Features/Planning/ManualPlannerViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Automation/Planning/ManualPlannerViewModel.cs rename to UI/Features/Planning/ManualPlannerViewModel.cs index c9725c16..a98953cf 100644 --- a/UI/Backend/ViewModels/Automation/Planning/ManualPlannerViewModel.cs +++ b/UI/Features/Planning/ManualPlannerViewModel.cs @@ -1,112 +1,113 @@ -using Ares.Datamodel; -using Ares.Datamodel.Extensions; -using Ares.Datamodel.Planning; -using Ares.Services; -using Google.Protobuf.WellKnownTypes; -using Microsoft.AspNetCore.Components.Forms; -using ReactiveUI; -using ReactiveUI.SourceGenerators; - -namespace UI.Backend.ViewModels.Automation.Planning; - -public partial class ManualPlannerViewModel : ReactiveObject -{ - private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; - - public ManualPlannerViewModel(AresPlannerManagementService.AresPlannerManagementServiceClient client) - { - _client = client; - ManualPlannerValues = []; - _ = UpdatePlannerValues(); - } - - public async Task UpdatePlannerValues() - { - var collection = await _client.GetManualPlannerSeedAsync(new Empty()); - ManualPlannerValues = collection.PlannedValues; - } - - public async Task FileUploaded(IBrowserFile file) - { - NumberOfPlannedExperiments = 0; - var collection = new ManualPlannerSetCollection(); - var stream = file.OpenReadStream(); - var reader = new StreamReader(stream); - var result = await reader.ReadLineAsync(); - var header = result?.Split(',', StringSplitOptions.TrimEntries); - if(header is null) - return false; - - PlannerValueHeaders = header.ToList(); - - result = await reader.ReadLineAsync(); - while(result is not null) - { - try - { - var splitResult = result.Split(','); - var plannerSet = new ManualPlannerSet(); - plannerSet.ParameterValues.AddRange(splitResult.Select((s, i) => new ParameterNameValuePair { Name = header[i], Value = ParseToAresValue(s)})); - collection.PlannedValues.Add(plannerSet); - NumberOfPlannedExperiments += 1; - } - catch(Exception) - { - return false; - } - - result = await reader.ReadLineAsync(); - } - - if(!collection.PlannedValues.Any()) - return true; - - await _client.SeedManualPlannerAsync(new ManualPlannerSeed { PlannerValues = collection }); - await UpdatePlannerValues(); - - return true; - } - - private AresValue ParseToAresValue(string item) - { - if(string.IsNullOrEmpty(item)) - return AresValueHelper.CreateNull(); - - if(item.StartsWith("\"") && item.EndsWith("\"") && item.Length > 1) - { - return AresValueHelper.CreateString(item); - } - - var parsed = double.TryParse(item, out var value); - - if(!parsed) - return AresValueHelper.CreateNull(); - - return AresValueHelper.CreateNumber(value); - } - - public Task CreateDisplayData() - { - DisplayObjects.Clear(); - var experimentNumber = 1; - foreach(var item in ManualPlannerValues) - { - var displayObject = new ManualPlannerDisplayObject(); - displayObject.ExperimentNumber = $"{experimentNumber}"; - displayObject.Parameters = item; - DisplayObjects.Add(displayObject); - experimentNumber++; - } - - return Task.CompletedTask; - } - - [Reactive] - public partial IEnumerable ManualPlannerValues { get; private set; } - - public List DisplayObjects { get; set; } = []; - - public List PlannerValueHeaders { get; set; } = []; - - public int NumberOfPlannedExperiments { get; set; } = 0; -} +using Ares.Datamodel; +using Ares.Datamodel.Extensions; +using Ares.Datamodel.Planning; +using Ares.Services; +using Google.Protobuf.WellKnownTypes; +using Microsoft.AspNetCore.Components.Forms; +using ReactiveUI; +using ReactiveUI.SourceGenerators; +using UI.Features.Planning; + +namespace UI.Backend.ViewModels.Automation.Planning; + +public partial class ManualPlannerViewModel : ReactiveObject +{ + private readonly AresPlannerManagementService.AresPlannerManagementServiceClient _client; + + public ManualPlannerViewModel(AresPlannerManagementService.AresPlannerManagementServiceClient client) + { + _client = client; + ManualPlannerValues = []; + _ = UpdatePlannerValues(); + } + + public async Task UpdatePlannerValues() + { + var collection = await _client.GetManualPlannerSeedAsync(new Empty()); + ManualPlannerValues = collection.PlannedValues; + } + + public async Task FileUploaded(IBrowserFile file) + { + NumberOfPlannedExperiments = 0; + var collection = new ManualPlannerSetCollection(); + var stream = file.OpenReadStream(); + var reader = new StreamReader(stream); + var result = await reader.ReadLineAsync(); + var header = result?.Split(',', StringSplitOptions.TrimEntries); + if(header is null) + return false; + + PlannerValueHeaders = header.ToList(); + + result = await reader.ReadLineAsync(); + while(result is not null) + { + try + { + var splitResult = result.Split(','); + var plannerSet = new ManualPlannerSet(); + plannerSet.ParameterValues.AddRange(splitResult.Select((s, i) => new ParameterNameValuePair { Name = header[i], Value = ParseToAresValue(s)})); + collection.PlannedValues.Add(plannerSet); + NumberOfPlannedExperiments += 1; + } + catch(Exception) + { + return false; + } + + result = await reader.ReadLineAsync(); + } + + if(!collection.PlannedValues.Any()) + return true; + + await _client.SeedManualPlannerAsync(new ManualPlannerSeed { PlannerValues = collection }); + await UpdatePlannerValues(); + + return true; + } + + private AresValue ParseToAresValue(string item) + { + if(string.IsNullOrEmpty(item)) + return AresValueHelper.CreateNull(); + + if(item.StartsWith("\"") && item.EndsWith("\"") && item.Length > 1) + { + return AresValueHelper.CreateString(item); + } + + var parsed = double.TryParse(item, out var value); + + if(!parsed) + return AresValueHelper.CreateNull(); + + return AresValueHelper.CreateNumber(value); + } + + public Task CreateDisplayData() + { + DisplayObjects.Clear(); + var experimentNumber = 1; + foreach(var item in ManualPlannerValues) + { + var displayObject = new ManualPlannerDisplayObject(); + displayObject.ExperimentNumber = $"{experimentNumber}"; + displayObject.Parameters = item; + DisplayObjects.Add(displayObject); + experimentNumber++; + } + + return Task.CompletedTask; + } + + [Reactive] + public partial IEnumerable ManualPlannerValues { get; private set; } + + public List DisplayObjects { get; set; } = []; + + public List PlannerValueHeaders { get; set; } = []; + + public int NumberOfPlannedExperiments { get; set; } = 0; +} diff --git a/UI/Pages/Automation/CustomStepBuilder.razor b/UI/Pages/Automation/CustomStepBuilder.razor deleted file mode 100644 index 4134efbe..00000000 --- a/UI/Pages/Automation/CustomStepBuilder.razor +++ /dev/null @@ -1,27 +0,0 @@ -@page "/automation/customstepbuilder" -@inject ContextMenuService ContextMenuService -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - -Custom Step Builder - - - - -@code { - - - void ShowContextMenuWithItems(MouseEventArgs args) - { - ContextMenuService.Open(args, - new List { - new(){ Text = "Context menu item 1", Value = 1 }, - new(){ Text = "Context menu item 2", Value = 2 }, - new(){ Text = "Context menu item 3", Value = 3 }, - }, OnMenuItemClick); - } - - void OnMenuItemClick(MenuItemEventArgs args) - { - ContextMenuService.Close(); - } -} \ No newline at end of file diff --git a/UI/Pages/Index.razor b/UI/Pages/Index.razor index 13fb27b0..0ebec1b1 100644 --- a/UI/Pages/Index.razor +++ b/UI/Pages/Index.razor @@ -1,7 +1,6 @@ @page "/" @using UI.Backend.Repos @using UI.Backend.ViewModels -@using UI.Pages.Shared.Misc @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject IJSRuntime JS diff --git a/UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor b/UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor index f67107d4..d50d0ab2 100644 --- a/UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor +++ b/UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor @@ -1,5 +1,6 @@ @using Ares.Datamodel.Extensions @using UI.Backend.ViewModels.Automation.Planning +@using UI.Features.Planning @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService dialogService diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 239f45ad..4ce77170 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -29,7 +29,6 @@ using UI.Backend.ViewModels.Automation; using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Backend.ViewModels.Automation.Planning; -using UI.Backend.ViewModels.Misc; using UI.Backend.ViewModels.Settings.Analysis; using UI.Backend.ViewModels.Settings.Device.ChemyxPump; using UI.Backend.ViewModels.Settings.Device.CM3Camera; @@ -48,6 +47,7 @@ using UI.Backend.ViewModels.Settings.Logging; using UI.Backend.ViewModels.Settings.Planning; using UI.Features.Auth; +using UI.Features.CampaignEdit; using UI.Features.CampaignEdit.Factories; using UI.Features.CampaignEdit.ViewModels; using UI.Features.Devices.ChemyxPump; @@ -65,7 +65,6 @@ using UI.Features.DeviceStateLogging; using UI.Infrastructure.Interfaces; using UI.Infrastructure.Monaco.Interops; -using UI.Services.CampaignEdit; using UI.Services.Dialog; using UI.Services.Grpc; using UI.Services.Notification; @@ -162,7 +161,6 @@ private static void BindViewModels(this IServiceCollection services) services.AddScoped(); services.AddTransient(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -191,7 +189,7 @@ private static void BindViewModels(this IServiceCollection services) services.AddTransient(); services.AddTransient(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); } private static void BindViewModelFactories(this IServiceCollection services) diff --git a/UI/UI.csproj b/UI/UI.csproj index f605669e..103e5f0f 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -46,8 +46,8 @@ - - + + From c8aa2ff74435878029171eb1dd30d27c25705beb Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 12:48:04 -0500 Subject: [PATCH 05/17] Rest of logging, analyzing, and planning moves. --- .../Analyzing/Settings/AnalyzerConfigEditView.razor | 2 +- .../Analyzing/Settings}/AnalyzerConfigEditViewModel.cs | 4 ++-- .../Analyzing/Settings/AnalyzerSettingsEditorView.razor | 4 ++-- .../Settings}/AnalyzerSettingsEditorViewModel.cs | 4 ++-- UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor | 4 ++-- .../Analyzing/Settings}/AnalyzerSettingsListViewModel.cs | 4 ++-- UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor | 5 +++-- .../Analyzing/Settings}/AnalyzerSettingsViewModel.cs | 4 ++-- .../Settings}/LoggingSettingsEdit.razor | 2 +- .../Settings}/LoggingSettingsList.razor | 2 +- .../Settings}/LoggingSettingsListViewModel.cs | 2 +- .../Settings}/LoggingSettingsViewModel.cs | 2 +- .../Settings}/Layout/PlannerSettingsContainer.razor | 0 .../Settings}/Layout/PlannerSettingsContainer.razor.css | 0 .../Planning/Settings}/Layout/PlannerSettingsLayout.razor | 0 .../Planning/Settings}/PlannerConfigEditView.razor | 2 +- .../Planning/Settings}/PlannerConfigEditViewModel.cs | 4 ++-- .../Planning/Settings}/PlannerSettingsEditorView.razor | 4 ++-- .../Planning/Settings}/PlannerSettingsEditorViewModel.cs | 4 ++-- .../Planning/Settings}/PlannerSettingsList.razor | 6 +++--- .../Planning/Settings}/PlannerSettingsListViewModel.cs | 4 ++-- .../Planning/Settings}/PlannerSettingsView.razor | 6 +++--- .../Planning/Settings}/PlannerSettingsViewModel.cs | 4 ++-- UI/Pages/Index.razor | 3 ++- UI/ServiceCollectionExtensions.cs | 8 ++++---- 25 files changed, 43 insertions(+), 41 deletions(-) rename UI/{Backend/ViewModels/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerConfigEditViewModel.cs (92%) rename UI/{Backend/ViewModels/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsEditorViewModel.cs (95%) rename UI/{Backend/ViewModels/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsListViewModel.cs (96%) rename UI/{Backend/ViewModels/Settings/Analysis => Features/Analyzing/Settings}/AnalyzerSettingsViewModel.cs (97%) rename UI/{Pages/Shared/Settings/Logging => Features/DeviceStateLogging/Settings}/LoggingSettingsEdit.razor (98%) rename UI/{Pages/Shared/Settings/Logging => Features/DeviceStateLogging/Settings}/LoggingSettingsList.razor (96%) rename UI/{Backend/ViewModels/Settings/Logging => Features/DeviceStateLogging/Settings}/LoggingSettingsListViewModel.cs (97%) rename UI/{Backend/ViewModels/Settings/Logging => Features/DeviceStateLogging/Settings}/LoggingSettingsViewModel.cs (98%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/Layout/PlannerSettingsContainer.razor (100%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/Layout/PlannerSettingsContainer.razor.css (100%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/Layout/PlannerSettingsLayout.razor (100%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/PlannerConfigEditView.razor (84%) rename UI/{Backend/ViewModels/Settings/Planning => Features/Planning/Settings}/PlannerConfigEditViewModel.cs (93%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/PlannerSettingsEditorView.razor (84%) rename UI/{Backend/ViewModels/Settings/Planning => Features/Planning/Settings}/PlannerSettingsEditorViewModel.cs (95%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/PlannerSettingsList.razor (94%) rename UI/{Backend/ViewModels/Settings/Planning => Features/Planning/Settings}/PlannerSettingsListViewModel.cs (95%) rename UI/{Pages/Shared/Settings/Planning => Features/Planning/Settings}/PlannerSettingsView.razor (92%) rename UI/{Backend/ViewModels/Settings/Planning => Features/Planning/Settings}/PlannerSettingsViewModel.cs (96%) diff --git a/UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor b/UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor index ebf73156..ab025887 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerConfigEditView.razor @@ -1,4 +1,4 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase
diff --git a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerConfigEditViewModel.cs b/UI/Features/Analyzing/Settings/AnalyzerConfigEditViewModel.cs similarity index 92% rename from UI/Backend/ViewModels/Settings/Analysis/AnalyzerConfigEditViewModel.cs rename to UI/Features/Analyzing/Settings/AnalyzerConfigEditViewModel.cs index 52af8d6b..67a33d32 100644 --- a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerConfigEditViewModel.cs +++ b/UI/Features/Analyzing/Settings/AnalyzerConfigEditViewModel.cs @@ -1,8 +1,8 @@ -using Ares.Datamodel.Analyzing; +using Ares.Datamodel.Analyzing; using Ares.Services; using ReactiveUI; -namespace UI.Backend.ViewModels.Settings.Analysis; +namespace UI.Features.Analyzing.Settings; public class AnalyzerConfigEditViewModel : ReactiveObject { diff --git a/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor index 9ca50956..76d6f618 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorView.razor @@ -1,5 +1,5 @@ -@using Ares.Datamodel -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using Ares.Datamodel +@inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsEditorViewModel.cs b/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsEditorViewModel.cs rename to UI/Features/Analyzing/Settings/AnalyzerSettingsEditorViewModel.cs index 427ba9a8..3e5680ca 100644 --- a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsEditorViewModel.cs +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsEditorViewModel.cs @@ -1,10 +1,10 @@ -using Ares.Datamodel; +using Ares.Datamodel; using Ares.Datamodel.Analyzing; using Ares.Services; using Grpc.Core; using ReactiveUI; -namespace UI.Backend.ViewModels.Settings.Analysis; +namespace UI.Features.Analyzing.Settings; public class AnalyzerSettingsEditorViewModel : ReactiveObject { diff --git a/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor index 71326ae9..096dee19 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsList.razor @@ -1,7 +1,7 @@ -@page "/settings/analysis" +@page "/settings/analysis" @using Ares.Services @using Google.Protobuf.WellKnownTypes -@using UI.Backend.ViewModels.Settings.Analysis +@using UI.Features.Analyzing.Settings @using UI.Features.Analyzing.Settings.Layout @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @layout AnalyzerSettingsLayout diff --git a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsListViewModel.cs b/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs similarity index 96% rename from UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsListViewModel.cs rename to UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs index 2cce139d..8307cfd1 100644 --- a/UI/Backend/ViewModels/Settings/Analysis/AnalyzerSettingsListViewModel.cs +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs @@ -1,11 +1,11 @@ -using Ares.Datamodel.Analyzing; +using Ares.Datamodel.Analyzing; using Ares.Services; using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; using UI.Services.Notification; -namespace UI.Backend.ViewModels.Settings.Analysis; +namespace UI.Features.Analyzing.Settings; public partial class AnalyzerSettingsListViewModel : ReactiveObject { diff --git a/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor b/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor index 36bfb645..609ca0dc 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsView.razor @@ -1,6 +1,7 @@ -@using Ares.Services +@using Ares.Services @using Google.Protobuf.WellKnownTypes -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Features.Analyzing.Settings.Layout +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService diff --git a/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor similarity index 96% rename from UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor rename to UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor index 50e85020..c477d0fc 100644 --- a/UI/Pages/Shared/Settings/Logging/LoggingSettingsList.razor +++ b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor @@ -1,6 +1,6 @@ @page "/settings/logging" @using ReactiveUI.Blazor -@using UI.Backend.ViewModels.Settings.Logging +@using UI.Features.DeviceStateLogging.Settings @using UI.Components.Layouts @layout SettingsLayout @inherits ReactiveInjectableComponentBase diff --git a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs similarity index 97% rename from UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs rename to UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs index 9dc78ab6..c93ff569 100644 --- a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsListViewModel.cs +++ b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs @@ -7,7 +7,7 @@ using UI.Features.DeviceStateLogging; using UI.Services.Notification; -namespace UI.Backend.ViewModels.Settings.Logging; +namespace UI.Features.DeviceStateLogging.Settings; public partial class LoggingSettingsListViewModel : ReactiveObject { diff --git a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsViewModel.cs b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsViewModel.cs similarity index 98% rename from UI/Backend/ViewModels/Settings/Logging/LoggingSettingsViewModel.cs rename to UI/Features/DeviceStateLogging/Settings/LoggingSettingsViewModel.cs index 615fb4f3..1d360ea8 100644 --- a/UI/Backend/ViewModels/Settings/Logging/LoggingSettingsViewModel.cs +++ b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsViewModel.cs @@ -4,7 +4,7 @@ using ReactiveUI; using ReactiveUI.SourceGenerators; -namespace UI.Backend.ViewModels.Settings.Logging; +namespace UI.Features.DeviceStateLogging.Settings; public partial class LoggingSettingsViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsContainer.razor b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor similarity index 100% rename from UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsContainer.razor rename to UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor diff --git a/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsContainer.razor.css b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor.css similarity index 100% rename from UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsContainer.razor.css rename to UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor.css diff --git a/UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor b/UI/Features/Planning/Settings/Layout/PlannerSettingsLayout.razor similarity index 100% rename from UI/Pages/Shared/Settings/Planning/Layout/PlannerSettingsLayout.razor rename to UI/Features/Planning/Settings/Layout/PlannerSettingsLayout.razor diff --git a/UI/Pages/Shared/Settings/Planning/PlannerConfigEditView.razor b/UI/Features/Planning/Settings/PlannerConfigEditView.razor similarity index 84% rename from UI/Pages/Shared/Settings/Planning/PlannerConfigEditView.razor rename to UI/Features/Planning/Settings/PlannerConfigEditView.razor index 189ec137..de406062 100644 --- a/UI/Pages/Shared/Settings/Planning/PlannerConfigEditView.razor +++ b/UI/Features/Planning/Settings/PlannerConfigEditView.razor @@ -1,4 +1,4 @@ -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@inherits ReactiveUI.Blazor.ReactiveComponentBase
diff --git a/UI/Backend/ViewModels/Settings/Planning/PlannerConfigEditViewModel.cs b/UI/Features/Planning/Settings/PlannerConfigEditViewModel.cs similarity index 93% rename from UI/Backend/ViewModels/Settings/Planning/PlannerConfigEditViewModel.cs rename to UI/Features/Planning/Settings/PlannerConfigEditViewModel.cs index 96f9aeb1..99b698fa 100644 --- a/UI/Backend/ViewModels/Settings/Planning/PlannerConfigEditViewModel.cs +++ b/UI/Features/Planning/Settings/PlannerConfigEditViewModel.cs @@ -1,9 +1,9 @@ -using Ares.Datamodel.Planning; +using Ares.Datamodel.Planning; using Ares.Services; using ReactiveUI; -namespace UI.Backend.ViewModels.Settings.Planning +namespace UI.Features.Planning.Settings { public class PlannerConfigEditViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Settings/Planning/PlannerSettingsEditorView.razor b/UI/Features/Planning/Settings/PlannerSettingsEditorView.razor similarity index 84% rename from UI/Pages/Shared/Settings/Planning/PlannerSettingsEditorView.razor rename to UI/Features/Planning/Settings/PlannerSettingsEditorView.razor index c8d995c2..a8aa9ed4 100644 --- a/UI/Pages/Shared/Settings/Planning/PlannerSettingsEditorView.razor +++ b/UI/Features/Planning/Settings/PlannerSettingsEditorView.razor @@ -1,5 +1,5 @@ -@using Ares.Datamodel; -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using Ares.Datamodel; +@inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Backend/ViewModels/Settings/Planning/PlannerSettingsEditorViewModel.cs b/UI/Features/Planning/Settings/PlannerSettingsEditorViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Settings/Planning/PlannerSettingsEditorViewModel.cs rename to UI/Features/Planning/Settings/PlannerSettingsEditorViewModel.cs index a446c857..c0558630 100644 --- a/UI/Backend/ViewModels/Settings/Planning/PlannerSettingsEditorViewModel.cs +++ b/UI/Features/Planning/Settings/PlannerSettingsEditorViewModel.cs @@ -1,10 +1,10 @@ -using Ares.Datamodel; +using Ares.Datamodel; using Ares.Datamodel.Planning; using Ares.Services; using Grpc.Core; using ReactiveUI; -namespace UI.Backend.ViewModels.Settings.Planning; +namespace UI.Features.Planning.Settings; public class PlannerSettingsEditorViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Settings/Planning/PlannerSettingsList.razor b/UI/Features/Planning/Settings/PlannerSettingsList.razor similarity index 94% rename from UI/Pages/Shared/Settings/Planning/PlannerSettingsList.razor rename to UI/Features/Planning/Settings/PlannerSettingsList.razor index b3ef3a2e..57a80bdc 100644 --- a/UI/Pages/Shared/Settings/Planning/PlannerSettingsList.razor +++ b/UI/Features/Planning/Settings/PlannerSettingsList.razor @@ -1,8 +1,8 @@ -@page "/settings/planning" +@page "/settings/planning" @using Ares.Services @using Google.Protobuf.WellKnownTypes -@using UI.Backend.ViewModels.Settings.Planning -@using UI.Pages.Shared.Settings.Planning.Layout +@using UI.Features.Planning.Settings +@using UI.Features.Planning.Settings.Layout @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @layout PlannerSettingsLayout diff --git a/UI/Backend/ViewModels/Settings/Planning/PlannerSettingsListViewModel.cs b/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs similarity index 95% rename from UI/Backend/ViewModels/Settings/Planning/PlannerSettingsListViewModel.cs rename to UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs index 62ae2b59..d8e7795c 100644 --- a/UI/Backend/ViewModels/Settings/Planning/PlannerSettingsListViewModel.cs +++ b/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs @@ -1,4 +1,4 @@ -using Ares.Datamodel.Planning; +using Ares.Datamodel.Planning; using Ares.Services; using Google.Protobuf.WellKnownTypes; using ReactiveUI; @@ -6,7 +6,7 @@ using UI.Services.Notification; -namespace UI.Backend.ViewModels.Settings.Planning; +namespace UI.Features.Planning.Settings; public partial class PlannerSettingsListViewModel : ReactiveObject { diff --git a/UI/Pages/Shared/Settings/Planning/PlannerSettingsView.razor b/UI/Features/Planning/Settings/PlannerSettingsView.razor similarity index 92% rename from UI/Pages/Shared/Settings/Planning/PlannerSettingsView.razor rename to UI/Features/Planning/Settings/PlannerSettingsView.razor index 980d9663..32128580 100644 --- a/UI/Pages/Shared/Settings/Planning/PlannerSettingsView.razor +++ b/UI/Features/Planning/Settings/PlannerSettingsView.razor @@ -1,7 +1,7 @@ -@using Ares.Services +@using Ares.Services @using Google.Protobuf.WellKnownTypes -@using UI.Pages.Shared.Settings.Planning.Layout -@inherits ReactiveUI.Blazor.ReactiveComponentBase +@using UI.Features.Planning.Settings.Layout +@inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService @inject IJSRuntime JS @@ -9,7 +10,7 @@ Dashboard
- +
diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 4ce77170..4f758d6f 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using Ares.Alicat.Mfc.Messaging; +using Ares.Alicat.Mfc.Messaging; using Ares.Messages; using Ares.Messages.DeviceStates.Chiller; using Ares.Messages.DeviceStates.Mfc; @@ -29,7 +29,7 @@ using UI.Backend.ViewModels.Automation; using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Backend.ViewModels.Automation.Planning; -using UI.Backend.ViewModels.Settings.Analysis; +using UI.Features.Analyzing.Settings; using UI.Backend.ViewModels.Settings.Device.ChemyxPump; using UI.Backend.ViewModels.Settings.Device.CM3Camera; using UI.Backend.ViewModels.Settings.Device.LaserChiller; @@ -44,8 +44,8 @@ using UI.Backend.ViewModels.Settings.Device.TubeFurnace; using UI.Backend.ViewModels.Settings.Device.ValveController; using UI.Backend.ViewModels.Settings.Device.VerdiLaser; -using UI.Backend.ViewModels.Settings.Logging; -using UI.Backend.ViewModels.Settings.Planning; +using UI.Features.DeviceStateLogging.Settings; +using UI.Features.Planning.Settings; using UI.Features.Auth; using UI.Features.CampaignEdit; using UI.Features.CampaignEdit.Factories; From 360900cbc844aa47a90ee97fe4482202ca1a5e28 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 13:13:39 -0500 Subject: [PATCH 06/17] More moves --- UI/Backend/ViewModels/DataViewerViewModel.cs | 2 +- .../NotificationHistoryViewModel.cs | 4 +- .../CommandParameterDesignerFactory.cs | 4 +- .../Factories/ParameterEditorFactory.cs | 4 +- .../CommandParameterDesignerViewModel.cs | 4 +- .../ViewModels/ParameterEditorViewModel.cs | 4 +- .../PlannableParameterDesignerViewModel.cs | 4 +- .../DeviceStateExporter.razor | 4 +- .../ManualDeviceLoggerWidget.razor | 4 +- .../CM3CamDeviceControlViewModelFactory.cs | 4 +- .../ChemyxPumpControlViewModelFactory.cs | 4 +- .../Mfc/MFCDeviceControlViewModelFactory.cs | 4 +- .../RemoteDeviceControlViewModelFactory.cs | 4 +- .../ServoDeviceControlViewModelFactory.cs | 4 +- .../DeviceConnectorViewModelFactory.cs | 4 +- ...ControllerDeviceControlViewModelFactory.cs | 4 +- ...yringePumpDeviceControlViewModelFactory.cs | 2 +- .../Tc0304DeviceControlViewModelFactory.cs | 4 +- ...ubeFurnaceDeviceControlViewModelFactory.cs | 4 +- ...ControllerDeviceControlViewModelFactory.cs | 4 +- .../Components}/ExecutionController.razor | 10 +-- .../Components}/ExecutionMonitor.razor | 10 +-- UI/Features/Execution/Execution.razor | 6 +- UI/Features/Execution/ExecutionViewModel.cs | 2 +- .../Execution/Planning/AresDemoPlanner.razor | 10 +-- .../Execution/Planning/BoraasPlanner.razor | 0 .../Execution/Planning/HolmesPlanner.razor | 0 .../Execution/Planning/ManualPlanEditor.razor | 0 .../Execution/Planning/ManualPlanner.razor | 74 ++++++++-------- .../ExecutionHistory/ExecutionHistory.razor | 4 +- .../ExperimentTemplateExtensions.cs | 4 +- .../Extensions/ExportTypeExtensions.cs | 4 +- .../Extensions/StatusExtensions.cs | 4 +- .../Helpers/DisplayFormatHelper.cs | 4 +- .../Helpers/UnitCategoryHelper.cs | 86 +++++++++---------- .../Notifications/INotificationRepository.cs | 4 +- .../Notifications/NotificationRepository.cs | 4 +- .../Repos/DeviceControlViewModelRepo.cs | 4 +- .../Repos/IDeviceControlViewModelRepo.cs | 4 +- UI/Pages/DataViewer.razor | 4 +- UI/Pages/Index.razor | 2 +- .../Notification/NotificationHistory.razor | 4 +- UI/Program.cs | 2 +- UI/ServiceCollectionExtensions.cs | 6 +- UI/ServiceStarter.cs | 2 +- .../NotificationReceivingService.cs | 2 +- 46 files changed, 166 insertions(+), 166 deletions(-) rename UI/{Backend/Factories => Features/Devices/Shared}/DeviceConnectorViewModelFactory.cs (97%) rename UI/{Pages/Shared/Execution => Features/Execution/Components}/ExecutionController.razor (89%) rename UI/{Pages/Shared/Execution => Features/Execution/Components}/ExecutionMonitor.razor (89%) rename UI/{Pages/Shared => Features}/Execution/Planning/AresDemoPlanner.razor (88%) rename UI/{Pages/Shared => Features}/Execution/Planning/BoraasPlanner.razor (100%) rename UI/{Pages/Shared => Features}/Execution/Planning/HolmesPlanner.razor (100%) rename UI/{Pages/Shared => Features}/Execution/Planning/ManualPlanEditor.razor (100%) rename UI/{Pages/Shared => Features}/Execution/Planning/ManualPlanner.razor (97%) rename UI/{Backend => Infrastructure}/Extensions/ExperimentTemplateExtensions.cs (92%) rename UI/{Backend => Infrastructure}/Extensions/ExportTypeExtensions.cs (86%) rename UI/{Backend => Infrastructure}/Extensions/StatusExtensions.cs (96%) rename UI/{Backend => Infrastructure}/Helpers/DisplayFormatHelper.cs (72%) rename UI/{Backend => Infrastructure}/Helpers/UnitCategoryHelper.cs (91%) rename UI/{Backend => Infrastructure}/Notifications/INotificationRepository.cs (55%) rename UI/{Backend => Infrastructure}/Notifications/NotificationRepository.cs (58%) rename UI/{Backend => Infrastructure}/Repos/DeviceControlViewModelRepo.cs (95%) rename UI/{Backend => Infrastructure}/Repos/IDeviceControlViewModelRepo.cs (74%) diff --git a/UI/Backend/ViewModels/DataViewerViewModel.cs b/UI/Backend/ViewModels/DataViewerViewModel.cs index dfd07964..96da8fe3 100644 --- a/UI/Backend/ViewModels/DataViewerViewModel.cs +++ b/UI/Backend/ViewModels/DataViewerViewModel.cs @@ -3,7 +3,7 @@ using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.Helpers; +using UI.Infrastructure.Helpers; namespace UI.Backend.ViewModels; diff --git a/UI/Backend/ViewModels/NotificationHistoryViewModel.cs b/UI/Backend/ViewModels/NotificationHistoryViewModel.cs index 04ca8b53..7745e773 100644 --- a/UI/Backend/ViewModels/NotificationHistoryViewModel.cs +++ b/UI/Backend/ViewModels/NotificationHistoryViewModel.cs @@ -1,6 +1,6 @@ -using Ares.Services; +using Ares.Services; using ReactiveUI; -using UI.Backend.Notifications; +using UI.Infrastructure.Notifications; namespace UI.Backend.ViewModels; diff --git a/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs index 826ecfef..6d5bb1ab 100644 --- a/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/CommandParameterDesignerFactory.cs @@ -1,5 +1,5 @@ -using Ares.Datamodel.Templates; -using UI.Backend.Helpers; +using Ares.Datamodel.Templates; +using UI.Infrastructure.Helpers; using UI.Features.CampaignEdit.ViewModels; namespace UI.Features.CampaignEdit.Factories; diff --git a/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs b/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs index c7788229..7799574a 100644 --- a/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs +++ b/UI/Features/CampaignEdit/Factories/ParameterEditorFactory.cs @@ -1,5 +1,5 @@ -using Ares.Datamodel.Templates; -using UI.Backend.Helpers; +using Ares.Datamodel.Templates; +using UI.Infrastructure.Helpers; using UI.Backend.ViewModels.Automation.CampaignEdit; namespace UI.Features.CampaignEdit.Factories; diff --git a/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs index 81853842..7d518ab5 100644 --- a/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/CommandParameterDesignerViewModel.cs @@ -1,7 +1,7 @@ -using Ares.Datamodel; +using Ares.Datamodel; using Ares.Datamodel.Templates; using ReactiveUI; -using UI.Backend.Helpers; +using UI.Infrastructure.Helpers; namespace UI.Features.CampaignEdit.ViewModels; diff --git a/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs b/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs index a459b4c9..9c1db735 100644 --- a/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/ParameterEditorViewModel.cs @@ -1,11 +1,11 @@ -using Ares.Datamodel; +using Ares.Datamodel; using Ares.Datamodel.Extensions; using Ares.Datamodel.Factories; using Ares.Datamodel.Templates; using Humanizer; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Backend.Helpers; +using UI.Infrastructure.Helpers; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs index 65e8b0c8..2a305148 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlannableParameterDesignerViewModel.cs @@ -1,8 +1,8 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using Ares.Datamodel.Templates; using DynamicData; using ReactiveUI; -using UI.Backend.Extensions; +using UI.Infrastructure.Extensions; using UI.Backend.ViewModels.Automation.CampaignEdit; using UI.Features.CampaignEdit.Factories; diff --git a/UI/Features/DeviceStateExport/DeviceStateExporter.razor b/UI/Features/DeviceStateExport/DeviceStateExporter.razor index 474e377e..795f3c85 100644 --- a/UI/Features/DeviceStateExport/DeviceStateExporter.razor +++ b/UI/Features/DeviceStateExport/DeviceStateExporter.razor @@ -1,5 +1,5 @@ -@using Ares.Services -@using UI.Backend.Extensions; +@using Ares.Services +@using UI.Infrastructure.Extensions; @using UI.Backend.ViewModels.DeviceStateLogging; @using UI.Features.DeviceStateLogging diff --git a/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor index 259249c6..39cdefe2 100644 --- a/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor +++ b/UI/Features/DeviceStateLogging/ManualDeviceLoggerWidget.razor @@ -1,7 +1,7 @@ -@using System.Diagnostics; +@using System.Diagnostics; @using Ares.Services @using ReactiveUI.Blazor; -@using UI.Backend.Extensions; +@using UI.Infrastructure.Extensions; @using UI.Features.DeviceStateLogging @inherits ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs b/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs index 0584ee36..1ac55bc5 100644 --- a/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/CM3Camera/CM3CamDeviceControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using FlirCM3.Services; using Google.Protobuf.WellKnownTypes; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.CM3Camera; diff --git a/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs b/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs index 096c569a..1197313d 100644 --- a/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using ChemyxPumpPlugin.Services; using DynamicData; using Google.Protobuf.WellKnownTypes; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; namespace UI.Features.Devices.ChemyxPump; diff --git a/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs b/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs index 44f558c5..7d510924 100644 --- a/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Mfc/MFCDeviceControlViewModelFactory.cs @@ -1,10 +1,10 @@ -using Ares.Alicat.Mfc.Messaging; +using Ares.Alicat.Mfc.Messaging; using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using System.Reactive.Linq; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.Mfc; diff --git a/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs b/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs index 0d680f14..c5eedfe7 100644 --- a/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceControlViewModelFactory.cs @@ -1,8 +1,8 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Devices.Remote; using UI.Features.Devices.Shared; diff --git a/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs b/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs index 4b0aa482..f08d4578 100644 --- a/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Servo/ServoDeviceControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using HerkulexDRS.Services; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; namespace UI.Features.Devices.Servo; diff --git a/UI/Backend/Factories/DeviceConnectorViewModelFactory.cs b/UI/Features/Devices/Shared/DeviceConnectorViewModelFactory.cs similarity index 97% rename from UI/Backend/Factories/DeviceConnectorViewModelFactory.cs rename to UI/Features/Devices/Shared/DeviceConnectorViewModelFactory.cs index 0dbdfb2a..41808584 100644 --- a/UI/Backend/Factories/DeviceConnectorViewModelFactory.cs +++ b/UI/Features/Devices/Shared/DeviceConnectorViewModelFactory.cs @@ -1,11 +1,11 @@ -using Ares.Datamodel.Device; +using Ares.Datamodel.Device; using Ares.Services.Device; using ReactiveUI; using System.Collections.ObjectModel; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; namespace UI.Backend.Factories; diff --git a/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs b/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs index d8dbc546..e11476d9 100644 --- a/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/StepperController/StepperControllerDeviceControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using TicStepperController.Messaging; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.StepperController; diff --git a/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs b/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs index 6736fe9d..9e915271 100644 --- a/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/SyringePump/SyringePumpDeviceControlViewModelFactory.cs @@ -4,7 +4,7 @@ using Google.Protobuf.WellKnownTypes; using System.Reactive.Linq; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.SyringePump; diff --git a/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs b/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs index a636c935..4d430f5e 100644 --- a/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/Tc0304/Tc0304DeviceControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using Tc0304.Services; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Tc0304; diff --git a/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs b/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs index b1c0bff8..46540e83 100644 --- a/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceDeviceControlViewModelFactory.cs @@ -1,9 +1,9 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using TubeFurnace.Messaging; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.TubeFurnace; diff --git a/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs b/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs index 7b5e465a..49c600e2 100644 --- a/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs +++ b/UI/Features/Devices/ValveController/ValveControllerDeviceControlViewModelFactory.cs @@ -1,8 +1,8 @@ -using Ares.Services.Device; +using Ares.Services.Device; using DynamicData; using Google.Protobuf.WellKnownTypes; using UI.Backend.Factories; -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using ValveController.Services; diff --git a/UI/Pages/Shared/Execution/ExecutionController.razor b/UI/Features/Execution/Components/ExecutionController.razor similarity index 89% rename from UI/Pages/Shared/Execution/ExecutionController.razor rename to UI/Features/Execution/Components/ExecutionController.razor index d15bb347..bf5fa301 100644 --- a/UI/Pages/Shared/Execution/ExecutionController.razor +++ b/UI/Features/Execution/Components/ExecutionController.razor @@ -1,5 +1,5 @@ -

ExecutionController

- -@code { - -} +

ExecutionController

+ +@code { + +} diff --git a/UI/Pages/Shared/Execution/ExecutionMonitor.razor b/UI/Features/Execution/Components/ExecutionMonitor.razor similarity index 89% rename from UI/Pages/Shared/Execution/ExecutionMonitor.razor rename to UI/Features/Execution/Components/ExecutionMonitor.razor index fbfbe07d..c2fff20e 100644 --- a/UI/Pages/Shared/Execution/ExecutionMonitor.razor +++ b/UI/Features/Execution/Components/ExecutionMonitor.razor @@ -1,5 +1,5 @@ -

ExecutionMonitor

- -@code { - -} +

ExecutionMonitor

+ +@code { + +} diff --git a/UI/Features/Execution/Execution.razor b/UI/Features/Execution/Execution.razor index 7b0748da..8a581507 100644 --- a/UI/Features/Execution/Execution.razor +++ b/UI/Features/Execution/Execution.razor @@ -5,9 +5,9 @@ @using Ares.Services @using Google.Protobuf.WellKnownTypes @using Radzen.Blazor -@using UI.Backend.Extensions +@using UI.Infrastructure.Extensions @using UI.Infrastructure.Interfaces -@using UI.Pages.Shared.Execution.Planning +@using UI.Features.Execution.Planning @inject ContextMenuService ContextMenuService @inject AresAutomation.AresAutomationClient AutomationClient @inject IUiNotificationService NotificationService @@ -421,7 +421,7 @@ System.Type? GetTypeFromString(string? type) { - return type is null ? null : System.Type.GetType($"UI.Pages.Shared.Execution.Planning.{type}"); + return type is null ? null : System.Type.GetType($"UI.Features.Execution.Planning.{type}"); } private async Task ExecutionNotesUploaded(UploadChangeEventArgs args) diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 99b8b059..62ecc913 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -7,7 +7,7 @@ using Ares.Datamodel.Analyzing; using Ares.Datamodel.Templates; using Ares.Services; -using UI.Backend.Extensions; +using UI.Infrastructure.Extensions; using UI.Services.Notification; using Ares.Datamodel.Planning; using System.ComponentModel; diff --git a/UI/Pages/Shared/Execution/Planning/AresDemoPlanner.razor b/UI/Features/Execution/Planning/AresDemoPlanner.razor similarity index 88% rename from UI/Pages/Shared/Execution/Planning/AresDemoPlanner.razor rename to UI/Features/Execution/Planning/AresDemoPlanner.razor index 05539166..bbf014a2 100644 --- a/UI/Pages/Shared/Execution/Planning/AresDemoPlanner.razor +++ b/UI/Features/Execution/Planning/AresDemoPlanner.razor @@ -1,5 +1,5 @@ -

Demo Planner

- -@code { - -} +

Demo Planner

+ +@code { + +} diff --git a/UI/Pages/Shared/Execution/Planning/BoraasPlanner.razor b/UI/Features/Execution/Planning/BoraasPlanner.razor similarity index 100% rename from UI/Pages/Shared/Execution/Planning/BoraasPlanner.razor rename to UI/Features/Execution/Planning/BoraasPlanner.razor diff --git a/UI/Pages/Shared/Execution/Planning/HolmesPlanner.razor b/UI/Features/Execution/Planning/HolmesPlanner.razor similarity index 100% rename from UI/Pages/Shared/Execution/Planning/HolmesPlanner.razor rename to UI/Features/Execution/Planning/HolmesPlanner.razor diff --git a/UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor b/UI/Features/Execution/Planning/ManualPlanEditor.razor similarity index 100% rename from UI/Pages/Shared/Execution/Planning/ManualPlanEditor.razor rename to UI/Features/Execution/Planning/ManualPlanEditor.razor diff --git a/UI/Pages/Shared/Execution/Planning/ManualPlanner.razor b/UI/Features/Execution/Planning/ManualPlanner.razor similarity index 97% rename from UI/Pages/Shared/Execution/Planning/ManualPlanner.razor rename to UI/Features/Execution/Planning/ManualPlanner.razor index 39144f2a..ca5a54e3 100644 --- a/UI/Pages/Shared/Execution/Planning/ManualPlanner.razor +++ b/UI/Features/Execution/Planning/ManualPlanner.razor @@ -1,37 +1,37 @@ -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase -@inject DialogService dialogService - - - - - - - - - -@code { - [Parameter] - public string Name { get; set; } = "Manual Planner"; - - [Parameter] - public string Type { get; set; } = "ManualPlanner"; - - [Parameter] - public Version Version { get; set; } = new (1, 0); - - private async Task EditManualPlanClick() - { - await dialogService.OpenAsync("Edit Manual Plan"); - } - - private async Task DisplayManualPlannerHelpTab() - { - await dialogService.OpenAsync("The Manual Planner", ds => - @
- ARES provides a manual planner to give the user a way to feed pre-determined values into an experiment manually. - The manual planner accepts both strings and numbers as input. Any entries surrounded by quotation marks (e.g. "value") will be parsed as a string, - otherwise ARES will attempt to parse the entry as a number. -
- , new DialogOptions() { CloseDialogOnOverlayClick = true }); - } -} +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase +@inject DialogService dialogService + + + + + + + + + +@code { + [Parameter] + public string Name { get; set; } = "Manual Planner"; + + [Parameter] + public string Type { get; set; } = "ManualPlanner"; + + [Parameter] + public Version Version { get; set; } = new (1, 0); + + private async Task EditManualPlanClick() + { + await dialogService.OpenAsync("Edit Manual Plan"); + } + + private async Task DisplayManualPlannerHelpTab() + { + await dialogService.OpenAsync("The Manual Planner", ds => + @
+ ARES provides a manual planner to give the user a way to feed pre-determined values into an experiment manually. + The manual planner accepts both strings and numbers as input. Any entries surrounded by quotation marks (e.g. "value") will be parsed as a string, + otherwise ARES will attempt to parse the entry as a number. +
+ , new DialogOptions() { CloseDialogOnOverlayClick = true }); + } +} diff --git a/UI/Features/ExecutionHistory/ExecutionHistory.razor b/UI/Features/ExecutionHistory/ExecutionHistory.razor index 7f7ad8b9..fb57eb28 100644 --- a/UI/Features/ExecutionHistory/ExecutionHistory.razor +++ b/UI/Features/ExecutionHistory/ExecutionHistory.razor @@ -1,6 +1,6 @@ -@page "/automation/executionhistory" +@page "/automation/executionhistory" @using Ares.Services -@using UI.Backend.Helpers +@using UI.Infrastructure.Helpers @inject ContextMenuService ContextMenuService @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Backend/Extensions/ExperimentTemplateExtensions.cs b/UI/Infrastructure/Extensions/ExperimentTemplateExtensions.cs similarity index 92% rename from UI/Backend/Extensions/ExperimentTemplateExtensions.cs rename to UI/Infrastructure/Extensions/ExperimentTemplateExtensions.cs index 08196bf8..77c95eea 100644 --- a/UI/Backend/Extensions/ExperimentTemplateExtensions.cs +++ b/UI/Infrastructure/Extensions/ExperimentTemplateExtensions.cs @@ -1,6 +1,6 @@ -using Ares.Datamodel.Templates; +using Ares.Datamodel.Templates; -namespace UI.Backend.Extensions; +namespace UI.Infrastructure.Extensions; internal static class ExperimentTemplateExtensions { diff --git a/UI/Backend/Extensions/ExportTypeExtensions.cs b/UI/Infrastructure/Extensions/ExportTypeExtensions.cs similarity index 86% rename from UI/Backend/Extensions/ExportTypeExtensions.cs rename to UI/Infrastructure/Extensions/ExportTypeExtensions.cs index a9e92b1b..409a56b6 100644 --- a/UI/Backend/Extensions/ExportTypeExtensions.cs +++ b/UI/Infrastructure/Extensions/ExportTypeExtensions.cs @@ -1,7 +1,7 @@ - + using Ares.Services; -namespace UI.Backend.Extensions; +namespace UI.Infrastructure.Extensions; public static class ExportTypeExtensions { diff --git a/UI/Backend/Extensions/StatusExtensions.cs b/UI/Infrastructure/Extensions/StatusExtensions.cs similarity index 96% rename from UI/Backend/Extensions/StatusExtensions.cs rename to UI/Infrastructure/Extensions/StatusExtensions.cs index c9ced341..04ebe314 100644 --- a/UI/Backend/Extensions/StatusExtensions.cs +++ b/UI/Infrastructure/Extensions/StatusExtensions.cs @@ -1,6 +1,6 @@ -using Ares.Datamodel; +using Ares.Datamodel; -namespace UI.Backend.Extensions; +namespace UI.Infrastructure.Extensions; public static class StatusExtensions { diff --git a/UI/Backend/Helpers/DisplayFormatHelper.cs b/UI/Infrastructure/Helpers/DisplayFormatHelper.cs similarity index 72% rename from UI/Backend/Helpers/DisplayFormatHelper.cs rename to UI/Infrastructure/Helpers/DisplayFormatHelper.cs index 93224141..b38cf7e2 100644 --- a/UI/Backend/Helpers/DisplayFormatHelper.cs +++ b/UI/Infrastructure/Helpers/DisplayFormatHelper.cs @@ -1,6 +1,6 @@ -using Google.Protobuf.WellKnownTypes; +using Google.Protobuf.WellKnownTypes; -namespace UI.Backend.Helpers; +namespace UI.Infrastructure.Helpers; public static class DisplayFormatHelper { diff --git a/UI/Backend/Helpers/UnitCategoryHelper.cs b/UI/Infrastructure/Helpers/UnitCategoryHelper.cs similarity index 91% rename from UI/Backend/Helpers/UnitCategoryHelper.cs rename to UI/Infrastructure/Helpers/UnitCategoryHelper.cs index 547df327..7febe69d 100644 --- a/UI/Backend/Helpers/UnitCategoryHelper.cs +++ b/UI/Infrastructure/Helpers/UnitCategoryHelper.cs @@ -1,43 +1,43 @@ -using UnitsNet; - -namespace UI.Backend.Helpers; - -public class UnitCategoryHelper -{ - private readonly Dictionary _cachedLookups = []; - - public UnitCategoryHelper() - { - UnitCategories = Quantity.Infos.Select(i => i.Name).ToArray(); - Task.Run(BuildLookup); - } - - public string[] UnitCategories { get; } - - private void BuildLookup() - { - foreach(var quantityInfo in Quantity.Infos) - foreach(var unit in quantityInfo.UnitInfos) - // var humanUnit = unit.Name.Humanize(); - _cachedLookups.TryAdd(unit.Name, quantityInfo); - } - - public static string[] GetTypes(string unitCategory) - { - var selection = Quantity.Infos.FirstOrDefault(i => i.Name.Equals(unitCategory)); - if(selection == null) - return Array.Empty(); - - var units = selection.UnitInfos; - return units.Select(i => i.Name).ToArray(); - } - - public string GetCategoryForUnit(string unit) - { - if(unit == "None") - return unit; - - else - return _cachedLookups.ContainsKey(unit) ? _cachedLookups[unit].Name : string.Empty; - } -} +using UnitsNet; + +namespace UI.Infrastructure.Helpers; + +public class UnitCategoryHelper +{ + private readonly Dictionary _cachedLookups = []; + + public UnitCategoryHelper() + { + UnitCategories = Quantity.Infos.Select(i => i.Name).ToArray(); + Task.Run(BuildLookup); + } + + public string[] UnitCategories { get; } + + private void BuildLookup() + { + foreach(var quantityInfo in Quantity.Infos) + foreach(var unit in quantityInfo.UnitInfos) + // var humanUnit = unit.Name.Humanize(); + _cachedLookups.TryAdd(unit.Name, quantityInfo); + } + + public static string[] GetTypes(string unitCategory) + { + var selection = Quantity.Infos.FirstOrDefault(i => i.Name.Equals(unitCategory)); + if(selection == null) + return Array.Empty(); + + var units = selection.UnitInfos; + return units.Select(i => i.Name).ToArray(); + } + + public string GetCategoryForUnit(string unit) + { + if(unit == "None") + return unit; + + else + return _cachedLookups.ContainsKey(unit) ? _cachedLookups[unit].Name : string.Empty; + } +} diff --git a/UI/Backend/Notifications/INotificationRepository.cs b/UI/Infrastructure/Notifications/INotificationRepository.cs similarity index 55% rename from UI/Backend/Notifications/INotificationRepository.cs rename to UI/Infrastructure/Notifications/INotificationRepository.cs index d67b08c1..34c159f9 100644 --- a/UI/Backend/Notifications/INotificationRepository.cs +++ b/UI/Infrastructure/Notifications/INotificationRepository.cs @@ -1,6 +1,6 @@ -using Ares.Services; +using Ares.Services; -namespace UI.Backend.Notifications; +namespace UI.Infrastructure.Notifications; public interface INotificationRepository : ICollection { diff --git a/UI/Backend/Notifications/NotificationRepository.cs b/UI/Infrastructure/Notifications/NotificationRepository.cs similarity index 58% rename from UI/Backend/Notifications/NotificationRepository.cs rename to UI/Infrastructure/Notifications/NotificationRepository.cs index 9b330b8a..aadffc8d 100644 --- a/UI/Backend/Notifications/NotificationRepository.cs +++ b/UI/Infrastructure/Notifications/NotificationRepository.cs @@ -1,6 +1,6 @@ -using Ares.Services; +using Ares.Services; -namespace UI.Backend.Notifications; +namespace UI.Infrastructure.Notifications; public class NotificationRepository : List, INotificationRepository { diff --git a/UI/Backend/Repos/DeviceControlViewModelRepo.cs b/UI/Infrastructure/Repos/DeviceControlViewModelRepo.cs similarity index 95% rename from UI/Backend/Repos/DeviceControlViewModelRepo.cs rename to UI/Infrastructure/Repos/DeviceControlViewModelRepo.cs index df820fce..a2ad33af 100644 --- a/UI/Backend/Repos/DeviceControlViewModelRepo.cs +++ b/UI/Infrastructure/Repos/DeviceControlViewModelRepo.cs @@ -1,9 +1,9 @@ -using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging; using DynamicData; using UI.Backend.ViewModels; using UI.Features.Devices.Shared; -namespace UI.Backend.Repos +namespace UI.Infrastructure.Repos { public class DeviceControlViewModelRepo : IDeviceControlViewModelRepo { diff --git a/UI/Backend/Repos/IDeviceControlViewModelRepo.cs b/UI/Infrastructure/Repos/IDeviceControlViewModelRepo.cs similarity index 74% rename from UI/Backend/Repos/IDeviceControlViewModelRepo.cs rename to UI/Infrastructure/Repos/IDeviceControlViewModelRepo.cs index fbe93429..4bebce7d 100644 --- a/UI/Backend/Repos/IDeviceControlViewModelRepo.cs +++ b/UI/Infrastructure/Repos/IDeviceControlViewModelRepo.cs @@ -1,7 +1,7 @@ -using DynamicData; +using DynamicData; using UI.Backend.ViewModels; -namespace UI.Backend.Repos +namespace UI.Infrastructure.Repos { public interface IDeviceControlViewModelRepo : ISourceList { diff --git a/UI/Pages/DataViewer.razor b/UI/Pages/DataViewer.razor index 3faef207..65eeaea4 100644 --- a/UI/Pages/DataViewer.razor +++ b/UI/Pages/DataViewer.razor @@ -1,9 +1,9 @@ -@page "/dataviewer" +@page "/dataviewer" @using System.Web @using Ares.Datamodel @using Ares.Datamodel.Extensions @using Ares.Services -@using UI.Backend.Helpers +@using UI.Infrastructure.Helpers @inject ContextMenuService ContextMenuService @inject NavigationManager NavigationManager @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Pages/Index.razor b/UI/Pages/Index.razor index 4ecefd00..29381b14 100644 --- a/UI/Pages/Index.razor +++ b/UI/Pages/Index.razor @@ -1,5 +1,5 @@ @page "/" -@using UI.Backend.Repos +@using UI.Infrastructure.Repos @using UI.Backend.ViewModels @using UI.Features.DeviceStateLogging diff --git a/UI/Pages/Notification/NotificationHistory.razor b/UI/Pages/Notification/NotificationHistory.razor index 2c232885..61eeb561 100644 --- a/UI/Pages/Notification/NotificationHistory.razor +++ b/UI/Pages/Notification/NotificationHistory.razor @@ -1,6 +1,6 @@ -@page "/notifications" +@page "/notifications" @using Ares.Services -@using UI.Backend.Notifications +@using UI.Infrastructure.Notifications @inject ContextMenuService ContextMenuService @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Program.cs b/UI/Program.cs index 1616a745..01aeff6c 100644 --- a/UI/Program.cs +++ b/UI/Program.cs @@ -3,7 +3,7 @@ using Radzen; using Serilog; using UI; -using UI.Backend.Helpers; +using UI.Infrastructure.Helpers; using UI.Domain; using UI.Services.Grpc; using UI.Services.Notification; diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 4f758d6f..3d4e2d3d 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -22,9 +22,9 @@ using TicStepperController.Messaging; using TubeFurnace.Messaging; using UI.Authentication; -using UI.Backend.Helpers; -using UI.Backend.Notifications; -using UI.Backend.Repos; +using UI.Infrastructure.Helpers; +using UI.Infrastructure.Notifications; +using UI.Infrastructure.Repos; using UI.Backend.ViewModels; using UI.Backend.ViewModels.Automation; using UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/ServiceStarter.cs b/UI/ServiceStarter.cs index 3f47c271..4bf9dbeb 100644 --- a/UI/ServiceStarter.cs +++ b/UI/ServiceStarter.cs @@ -1,4 +1,4 @@ -using UI.Backend.Repos; +using UI.Infrastructure.Repos; using UI.Features.Devices.ChemyxPump; using UI.Features.Devices.CM3Camera; using UI.Features.Devices.Mfc; diff --git a/UI/Services/Notification/NotificationReceivingService.cs b/UI/Services/Notification/NotificationReceivingService.cs index f004072e..bf8ddb35 100644 --- a/UI/Services/Notification/NotificationReceivingService.cs +++ b/UI/Services/Notification/NotificationReceivingService.cs @@ -2,7 +2,7 @@ using Google.Protobuf.WellKnownTypes; using Grpc.Core; using NuGet.Packaging; -using UI.Backend.Notifications; +using UI.Infrastructure.Notifications; using UI.Infrastructure.Interfaces; namespace UI.Services.Notification; From ee27751c8cef4ce16eca8cdc59fdb55f30474611 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 13:17:32 -0500 Subject: [PATCH 07/17] Script playground and dataviewer moves --- UI/Backend/ViewModels/SettingsViewModel.cs | 7 - .../DataViewer}/DataViewer.razor | 644 +++++++++--------- .../DataViewer}/DataViewerViewModel.cs | 0 .../ScriptPlayground}/ScriptPlayground.razor | 0 .../ScriptPlayground.razor.css | 0 .../ScriptPlaygroundViewModel.cs | 0 UI/ServiceCollectionExtensions.cs | 1 - UI/UI.csproj | 1 - 8 files changed, 322 insertions(+), 331 deletions(-) delete mode 100644 UI/Backend/ViewModels/SettingsViewModel.cs rename UI/{Pages => Features/DataViewer}/DataViewer.razor (97%) rename UI/{Backend/ViewModels => Features/DataViewer}/DataViewerViewModel.cs (100%) rename UI/{Pages => Features/ScriptPlayground}/ScriptPlayground.razor (100%) rename UI/{Pages => Features/ScriptPlayground}/ScriptPlayground.razor.css (100%) rename UI/{Backend/ViewModels => Features/ScriptPlayground}/ScriptPlaygroundViewModel.cs (100%) diff --git a/UI/Backend/ViewModels/SettingsViewModel.cs b/UI/Backend/ViewModels/SettingsViewModel.cs deleted file mode 100644 index 92a811e4..00000000 --- a/UI/Backend/ViewModels/SettingsViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -using ReactiveUI; - -namespace UI.Backend.ViewModels; - -public class SettingsViewModel : ReactiveObject -{ -} \ No newline at end of file diff --git a/UI/Pages/DataViewer.razor b/UI/Features/DataViewer/DataViewer.razor similarity index 97% rename from UI/Pages/DataViewer.razor rename to UI/Features/DataViewer/DataViewer.razor index 65eeaea4..a54c3e6f 100644 --- a/UI/Pages/DataViewer.razor +++ b/UI/Features/DataViewer/DataViewer.razor @@ -1,324 +1,324 @@ -@page "/dataviewer" -@using System.Web -@using Ares.Datamodel -@using Ares.Datamodel.Extensions -@using Ares.Services -@using UI.Infrastructure.Helpers -@inject ContextMenuService ContextMenuService -@inject NavigationManager NavigationManager -@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase - - -Data Viewer - -
-
-
-
- @if(ViewModel!.LoadingAvailableSumarries) - { - - } - - else - { - - - - } - -
- -
- @if(ViewModel!.FullSelectedSummary is not null) - { -
-
-

@ViewModel!.FullSelectedSummary.CampaignName - @ViewModel!.FullSelectedSummary.CampaignId

-
-
+@page "/dataviewer" +@using System.Web +@using Ares.Datamodel +@using Ares.Datamodel.Extensions +@using Ares.Services +@using UI.Infrastructure.Helpers +@inject ContextMenuService ContextMenuService +@inject NavigationManager NavigationManager +@inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase + + +Data Viewer + +
+
+
+
+ @if(ViewModel!.LoadingAvailableSumarries) + { + + } + + else + { + + + + } + +
+ +
+ @if(ViewModel!.FullSelectedSummary is not null) + { +
+
+

@ViewModel!.FullSelectedSummary.CampaignName - @ViewModel!.FullSelectedSummary.CampaignId

+
+
- - - - - - - - - - - - @foreach(var exp in ViewModel!.FullSelectedSummary.ExperimentSummaries) - { - - - @foreach(var step in exp.StepSummaries) - { - - - - - - - - - @foreach(var cmd in step.CommandSummaries) - { - - - - - - - - - - - - - } - - - - - } - - - - - - - - - - - - - - @foreach(var setting in exp.ExperimentOverview.AnalysisOverview.AnalyzerInfo.Capabilities.SettingsSchema?.Fields ?? []) - { - //TODO: Finish - - - - @if(setting.Value.StringChoices.Strings.Count != 0) - { - - } - - else if(setting.Value.NumberChoices.Numbers.Count != 0) - { - - } - - - - - - } - - - - - - - - - - - - @foreach(var param in exp.ExperimentOverview.Parameters.Where(p => p.PlanningMetadata is not null)) - { - - - - - - - - - - - - } - - - - - - - - - } - -
-
- } -
-
-
-
-

Campaign Summary

- - @if(ViewModel!.FullSelectedSummary is null) - { -

No Campaign Selected

- } - - else - { -
- Time Started: @ViewModel!.SelectedSummaryStartTime - Time Completed: @ViewModel!.SelectedSummaryFinishTime - Number of Experiments: @ViewModel!.SelectedSummaryNumberOfExperiments - Tags: @ViewModel!.SelectedSummaryTags - Notes: @ViewModel!.SelectedSummaryNotes -
- - - @if(ViewModel!.SelectedExperimentSummary is not null) - { -

Experiment Summary

- Experiment Started: @ViewModel!.SelectedExperimentSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() - Experiment Finished: @(ViewModel!.SelectedExperimentSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp()) - Analysis Result: @ViewModel!.SelectedExperimentSummary.ExperimentOverview.AnalysisOverview.Result - @if(ViewModel!.SelectedExperimentSummary.ExperimentOverview.Result.Fields.Any()) - { - Experiment Results - @foreach(var result in ViewModel!.SelectedExperimentSummary.ExperimentOverview.Result.Fields) - { - if(AresValueHelper.IsPrimitiveType(result.Value)) - { - @result.Key : @result.Value - } - else - { - @result.Key : Complex Data - } - - @result.Key : @result.Value - } - } - } - - @if(ViewModel!.SelectedStepSummary is not null) - { -

Step Summary

- Step Started: @ViewModel!.SelectedStepSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() - Step Finished: @ViewModel!.SelectedStepSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp() - Commands Executed: @ViewModel!.SelectedStepSummary.CommandSummaries.Count - } - - @if(ViewModel!.SelectedCommandSummary is not null) - { -

Command Summary

- Command Name: @ViewModel!.SelectedCommandSummary.CommandName - Command Description: @ViewModel!.SelectedCommandSummary.CommandDescription - Command Result: @CreateDeviceCommandResultDisplayString(ViewModel!.SelectedCommandSummary.Result) - Command Started: @ViewModel!.SelectedCommandSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() - Command Finished: @ViewModel!.SelectedCommandSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp() - } -
- } -
-
-
-
- -@code { - protected override void OnInitialized() - { - var parsedQueryString = HttpUtility.ParseQueryString(NavigationManager.Uri); - - if(parsedQueryString.HasKeys()) - { - var idKey = parsedQueryString.AllKeys.First(); - var idValue = parsedQueryString.Get(idKey); - - if(idValue is null || idValue == string.Empty) - return; - - _ = ViewModel!.GetSummaryFromLinkId(idValue); - } - - else - _ = ViewModel!.UpdateAvailableSummaries(); - - } - - private string CreateDeviceCommandResultDisplayString(CommandResult commandResult) - { - if(commandResult.Result is null) - return "Command Succeeded!"; - - return CreateAresValueDisplayString(commandResult.Result.Fields.FirstOrDefault().Value); - } - - private string CreateAresValueDisplayString(AresValue value) - { - if(value is null) - return "No Value"; - - if(value.HasNumberValue) - return $"{value.NumberValue}"; - - if(value.HasBoolValue) - return $"{value.BoolValue}"; - - if(value.HasStringValue) - return $"{value.StringValue}"; - - if(value.HasBytesValue) - return "Byte Array"; - - else - return "Custom Data"; - } - - private string GetLowerParameterConstraint(List limits) - { - if(limits.Count == 0) - return "No Min"; - - return $"Minimum: {limits.First().Minimum}"; - } - - private string GetHigherParameterConstraint(List limits) - { - if(limits.Count == 0) - return "No Max"; - - return $"Maximum: {limits.First().Maximum}"; - } - + + + + + + + + + + + + @foreach(var exp in ViewModel!.FullSelectedSummary.ExperimentSummaries) + { + + + @foreach(var step in exp.StepSummaries) + { + + + + + + + + + @foreach(var cmd in step.CommandSummaries) + { + + + + + + + + + + + + + } + + + + + } + + + + + + + + + + + + + + @foreach(var setting in exp.ExperimentOverview.AnalysisOverview.AnalyzerInfo.Capabilities.SettingsSchema?.Fields ?? []) + { + //TODO: Finish + + + + @if(setting.Value.StringChoices.Strings.Count != 0) + { + + } + + else if(setting.Value.NumberChoices.Numbers.Count != 0) + { + + } + + + + + + } + + + + + + + + + + + + @foreach(var param in exp.ExperimentOverview.Parameters.Where(p => p.PlanningMetadata is not null)) + { + + + + + + + + + + + + } + + + + + + + + + } + +
+
+ } +
+
+
+
+

Campaign Summary

+ + @if(ViewModel!.FullSelectedSummary is null) + { +

No Campaign Selected

+ } + + else + { +
+ Time Started: @ViewModel!.SelectedSummaryStartTime + Time Completed: @ViewModel!.SelectedSummaryFinishTime + Number of Experiments: @ViewModel!.SelectedSummaryNumberOfExperiments + Tags: @ViewModel!.SelectedSummaryTags + Notes: @ViewModel!.SelectedSummaryNotes +
+ + + @if(ViewModel!.SelectedExperimentSummary is not null) + { +

Experiment Summary

+ Experiment Started: @ViewModel!.SelectedExperimentSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() + Experiment Finished: @(ViewModel!.SelectedExperimentSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp()) + Analysis Result: @ViewModel!.SelectedExperimentSummary.ExperimentOverview.AnalysisOverview.Result + @if(ViewModel!.SelectedExperimentSummary.ExperimentOverview.Result.Fields.Any()) + { + Experiment Results + @foreach(var result in ViewModel!.SelectedExperimentSummary.ExperimentOverview.Result.Fields) + { + if(AresValueHelper.IsPrimitiveType(result.Value)) + { + @result.Key : @result.Value + } + else + { + @result.Key : Complex Data + } + + @result.Key : @result.Value + } + } + } + + @if(ViewModel!.SelectedStepSummary is not null) + { +

Step Summary

+ Step Started: @ViewModel!.SelectedStepSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() + Step Finished: @ViewModel!.SelectedStepSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp() + Commands Executed: @ViewModel!.SelectedStepSummary.CommandSummaries.Count + } + + @if(ViewModel!.SelectedCommandSummary is not null) + { +

Command Summary

+ Command Name: @ViewModel!.SelectedCommandSummary.CommandName + Command Description: @ViewModel!.SelectedCommandSummary.CommandDescription + Command Result: @CreateDeviceCommandResultDisplayString(ViewModel!.SelectedCommandSummary.Result) + Command Started: @ViewModel!.SelectedCommandSummary.ExecutionInfo.TimeStarted.ToReadableTimestamp() + Command Finished: @ViewModel!.SelectedCommandSummary.ExecutionInfo.TimeFinished.ToReadableTimestamp() + } +
+ } +
+
+
+
+ +@code { + protected override void OnInitialized() + { + var parsedQueryString = HttpUtility.ParseQueryString(NavigationManager.Uri); + + if(parsedQueryString.HasKeys()) + { + var idKey = parsedQueryString.AllKeys.First(); + var idValue = parsedQueryString.Get(idKey); + + if(idValue is null || idValue == string.Empty) + return; + + _ = ViewModel!.GetSummaryFromLinkId(idValue); + } + + else + _ = ViewModel!.UpdateAvailableSummaries(); + + } + + private string CreateDeviceCommandResultDisplayString(CommandResult commandResult) + { + if(commandResult.Result is null) + return "Command Succeeded!"; + + return CreateAresValueDisplayString(commandResult.Result.Fields.FirstOrDefault().Value); + } + + private string CreateAresValueDisplayString(AresValue value) + { + if(value is null) + return "No Value"; + + if(value.HasNumberValue) + return $"{value.NumberValue}"; + + if(value.HasBoolValue) + return $"{value.BoolValue}"; + + if(value.HasStringValue) + return $"{value.StringValue}"; + + if(value.HasBytesValue) + return "Byte Array"; + + else + return "Custom Data"; + } + + private string GetLowerParameterConstraint(List limits) + { + if(limits.Count == 0) + return "No Min"; + + return $"Minimum: {limits.First().Minimum}"; + } + + private string GetHigherParameterConstraint(List limits) + { + if(limits.Count == 0) + return "No Max"; + + return $"Maximum: {limits.First().Maximum}"; + } + } diff --git a/UI/Backend/ViewModels/DataViewerViewModel.cs b/UI/Features/DataViewer/DataViewerViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/DataViewerViewModel.cs rename to UI/Features/DataViewer/DataViewerViewModel.cs diff --git a/UI/Pages/ScriptPlayground.razor b/UI/Features/ScriptPlayground/ScriptPlayground.razor similarity index 100% rename from UI/Pages/ScriptPlayground.razor rename to UI/Features/ScriptPlayground/ScriptPlayground.razor diff --git a/UI/Pages/ScriptPlayground.razor.css b/UI/Features/ScriptPlayground/ScriptPlayground.razor.css similarity index 100% rename from UI/Pages/ScriptPlayground.razor.css rename to UI/Features/ScriptPlayground/ScriptPlayground.razor.css diff --git a/UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs b/UI/Features/ScriptPlayground/ScriptPlaygroundViewModel.cs similarity index 100% rename from UI/Backend/ViewModels/ScriptPlaygroundViewModel.cs rename to UI/Features/ScriptPlayground/ScriptPlaygroundViewModel.cs diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 3d4e2d3d..9637655c 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -158,7 +158,6 @@ private static void BindViewModels(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddTransient(); services.AddScoped(); services.AddScoped(); diff --git a/UI/UI.csproj b/UI/UI.csproj index 103e5f0f..e550705a 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -46,7 +46,6 @@ - From ae22c6149b287e4144b0ecc080c67028c0178425 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 13:22:04 -0500 Subject: [PATCH 08/17] More cleanup --- .../Shared => Components}/AresFooter.razor | 16 +- .../DashboardWidget.razor | 14 +- .../DashboardWidget.razor.css | 12 +- .../Layouts}/ErrorLayout.razor | 36 +-- .../Layouts}/ErrorLayout.razor.css | 140 +++++------ .../LoadingSpinner.razor | 40 +-- .../LoadingSpinner.razor.css | 236 +++++++++--------- .../Nav}/AresNavFooterItem.razor | 28 +-- .../Nav}/AresNavItem.razor | 2 +- .../Nav}/AresNavMenu.razor | 70 +++--- .../Shared => Components}/ScriptEditor.razor | 0 .../ScriptEditor.razor.css | 0 .../Shared => Components}/SpinnerSize.cs | 14 +- .../ScriptPlayground/ScriptPlayground.razor | 1 + 14 files changed, 305 insertions(+), 304 deletions(-) rename UI/{Pages/Shared => Components}/AresFooter.razor (94%) rename UI/{Pages/Shared => Components}/DashboardWidget.razor (95%) rename UI/{Pages/Shared => Components}/DashboardWidget.razor.css (96%) rename UI/{Pages/Shared => Components/Layouts}/ErrorLayout.razor (95%) rename UI/{Pages/Shared => Components/Layouts}/ErrorLayout.razor.css (94%) rename UI/{Pages/Shared => Components}/LoadingSpinner.razor (94%) rename UI/{Pages/Shared => Components}/LoadingSpinner.razor.css (94%) rename UI/{Pages/Shared => Components/Nav}/AresNavFooterItem.razor (95%) rename UI/{Pages/Shared => Components/Nav}/AresNavItem.razor (99%) rename UI/{Pages/Shared => Components/Nav}/AresNavMenu.razor (96%) rename UI/{Pages/Shared => Components}/ScriptEditor.razor (100%) rename UI/{Pages/Shared => Components}/ScriptEditor.razor.css (100%) rename UI/{Pages/Shared => Components}/SpinnerSize.cs (55%) diff --git a/UI/Pages/Shared/AresFooter.razor b/UI/Components/AresFooter.razor similarity index 94% rename from UI/Pages/Shared/AresFooter.razor rename to UI/Components/AresFooter.razor index 2758d4e8..c4a71f43 100644 --- a/UI/Pages/Shared/AresFooter.razor +++ b/UI/Components/AresFooter.razor @@ -1,8 +1,8 @@ -
- @ChildContent -
- -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } -} +
+ @ChildContent +
+ +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } +} diff --git a/UI/Pages/Shared/DashboardWidget.razor b/UI/Components/DashboardWidget.razor similarity index 95% rename from UI/Pages/Shared/DashboardWidget.razor rename to UI/Components/DashboardWidget.razor index e48f8f6e..14516e66 100644 --- a/UI/Pages/Shared/DashboardWidget.razor +++ b/UI/Components/DashboardWidget.razor @@ -1,7 +1,7 @@ -
- @ChildContent -
-@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } -} +
+ @ChildContent +
+@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } +} diff --git a/UI/Pages/Shared/DashboardWidget.razor.css b/UI/Components/DashboardWidget.razor.css similarity index 96% rename from UI/Pages/Shared/DashboardWidget.razor.css rename to UI/Components/DashboardWidget.razor.css index 580ceafb..0e237bba 100644 --- a/UI/Pages/Shared/DashboardWidget.razor.css +++ b/UI/Components/DashboardWidget.razor.css @@ -1,7 +1,7 @@ -.ares-dashboard-widget { - background: rgba(224, 255, 255, 0.1); - padding: 10px; - max-height: fit-content; - border-radius: 5px; - box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.7); +.ares-dashboard-widget { + background: rgba(224, 255, 255, 0.1); + padding: 10px; + max-height: fit-content; + border-radius: 5px; + box-shadow: 5px 5px 5px rgba(10, 10, 10, 0.7); } \ No newline at end of file diff --git a/UI/Pages/Shared/ErrorLayout.razor b/UI/Components/Layouts/ErrorLayout.razor similarity index 95% rename from UI/Pages/Shared/ErrorLayout.razor rename to UI/Components/Layouts/ErrorLayout.razor index abdc7df1..e4764d16 100644 --- a/UI/Pages/Shared/ErrorLayout.razor +++ b/UI/Components/Layouts/ErrorLayout.razor @@ -1,18 +1,18 @@ -@inherits LayoutComponentBase - - - - -ARES - -
- - - - - -
- @Body - Go Home -
-
+@inherits LayoutComponentBase + + + + +ARES + +
+ + + + + +
+ @Body + Go Home +
+
diff --git a/UI/Pages/Shared/ErrorLayout.razor.css b/UI/Components/Layouts/ErrorLayout.razor.css similarity index 94% rename from UI/Pages/Shared/ErrorLayout.razor.css rename to UI/Components/Layouts/ErrorLayout.razor.css index 699f17cd..551e4b27 100644 --- a/UI/Pages/Shared/ErrorLayout.razor.css +++ b/UI/Components/Layouts/ErrorLayout.razor.css @@ -1,70 +1,70 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - } - - .top-row a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row:not(.auth) { - display: none; - } - - .top-row.auth { - justify-content: space-between; - } - - .top-row a, .top-row .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + } + + .top-row a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row:not(.auth) { + display: none; + } + + .top-row.auth { + justify-content: space-between; + } + + .top-row a, .top-row .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} diff --git a/UI/Pages/Shared/LoadingSpinner.razor b/UI/Components/LoadingSpinner.razor similarity index 94% rename from UI/Pages/Shared/LoadingSpinner.razor rename to UI/Components/LoadingSpinner.razor index 220641f4..dd32243e 100644 --- a/UI/Pages/Shared/LoadingSpinner.razor +++ b/UI/Components/LoadingSpinner.razor @@ -1,21 +1,21 @@ -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -@code { - - [Parameter] - public SpinnerSize SpinnerSize { get; set; } = SpinnerSize.Large; - +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +@code { + + [Parameter] + public SpinnerSize SpinnerSize { get; set; } = SpinnerSize.Large; + } \ No newline at end of file diff --git a/UI/Pages/Shared/LoadingSpinner.razor.css b/UI/Components/LoadingSpinner.razor.css similarity index 94% rename from UI/Pages/Shared/LoadingSpinner.razor.css rename to UI/Components/LoadingSpinner.razor.css index baeb577c..f7f8c21d 100644 --- a/UI/Pages/Shared/LoadingSpinner.razor.css +++ b/UI/Components/LoadingSpinner.razor.css @@ -1,119 +1,119 @@ -.lds-spinner { - color: grey; - display: inline-block; - position: relative; -} - -.lds-spinner.lds-spinner-sm { - width: 18px; - height: 18px; -} - -.lds-spinner.lds-spinner-lg { - width: 80px; - height: 80px; -} - -.lds-spinner.lds-spinner-sm div { - transform-origin: 10px 10px; -} - -.lds-spinner.lds-spinner-lg div { - transform-origin: 40px 40px; -} - -.lds-spinner div { - animation: lds-spinner 1.2s linear infinite; -} - -.lds-spinner div:after { - content: " "; - display: block; - position: absolute; - border-radius: 20%; - background: #fff; -} - -.lds-spinner.lds-spinner-sm div:after { - top: 1px; - left: 9px; - width: 1px; - height: 5px; -} - - .lds-spinner.lds-spinner-lg div:after { - top: 3px; - left: 37px; - width: 6px; - height: 18px; - } - -.lds-spinner div:nth-child(1) { - transform: rotate(0deg); - animation-delay: -1.1s; -} - -.lds-spinner div:nth-child(2) { - transform: rotate(30deg); - animation-delay: -1s; -} - -.lds-spinner div:nth-child(3) { - transform: rotate(60deg); - animation-delay: -0.9s; -} - -.lds-spinner div:nth-child(4) { - transform: rotate(90deg); - animation-delay: -0.8s; -} - -.lds-spinner div:nth-child(5) { - transform: rotate(120deg); - animation-delay: -0.7s; -} - -.lds-spinner div:nth-child(6) { - transform: rotate(150deg); - animation-delay: -0.6s; -} - -.lds-spinner div:nth-child(7) { - transform: rotate(180deg); - animation-delay: -0.5s; -} - -.lds-spinner div:nth-child(8) { - transform: rotate(210deg); - animation-delay: -0.4s; -} - -.lds-spinner div:nth-child(9) { - transform: rotate(240deg); - animation-delay: -0.3s; -} - -.lds-spinner div:nth-child(10) { - transform: rotate(270deg); - animation-delay: -0.2s; -} - -.lds-spinner div:nth-child(11) { - transform: rotate(300deg); - animation-delay: -0.1s; -} - -.lds-spinner div:nth-child(12) { - transform: rotate(330deg); - animation-delay: 0s; -} - -@keyframes lds-spinner { - 0% { - opacity: 1; - } - - 100% { - opacity: 0; - } +.lds-spinner { + color: grey; + display: inline-block; + position: relative; +} + +.lds-spinner.lds-spinner-sm { + width: 18px; + height: 18px; +} + +.lds-spinner.lds-spinner-lg { + width: 80px; + height: 80px; +} + +.lds-spinner.lds-spinner-sm div { + transform-origin: 10px 10px; +} + +.lds-spinner.lds-spinner-lg div { + transform-origin: 40px 40px; +} + +.lds-spinner div { + animation: lds-spinner 1.2s linear infinite; +} + +.lds-spinner div:after { + content: " "; + display: block; + position: absolute; + border-radius: 20%; + background: #fff; +} + +.lds-spinner.lds-spinner-sm div:after { + top: 1px; + left: 9px; + width: 1px; + height: 5px; +} + + .lds-spinner.lds-spinner-lg div:after { + top: 3px; + left: 37px; + width: 6px; + height: 18px; + } + +.lds-spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; +} + +.lds-spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; +} + +.lds-spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; +} + +.lds-spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; +} + +.lds-spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; +} + +.lds-spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; +} + +.lds-spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; +} + +.lds-spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; +} + +.lds-spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; +} + +.lds-spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; +} + +.lds-spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; +} + +.lds-spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; +} + +@keyframes lds-spinner { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } } \ No newline at end of file diff --git a/UI/Pages/Shared/AresNavFooterItem.razor b/UI/Components/Nav/AresNavFooterItem.razor similarity index 95% rename from UI/Pages/Shared/AresNavFooterItem.razor rename to UI/Components/Nav/AresNavFooterItem.razor index ec484832..b1517885 100644 --- a/UI/Pages/Shared/AresNavFooterItem.razor +++ b/UI/Components/Nav/AresNavFooterItem.razor @@ -1,14 +1,14 @@ - - - - -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } - - [Parameter] - public string? Icon { get; set; } - - [Parameter] - public string? Path { get; set; } -} + + + + +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter] + public string? Icon { get; set; } + + [Parameter] + public string? Path { get; set; } +} diff --git a/UI/Pages/Shared/AresNavItem.razor b/UI/Components/Nav/AresNavItem.razor similarity index 99% rename from UI/Pages/Shared/AresNavItem.razor rename to UI/Components/Nav/AresNavItem.razor index 32883fd8..fabdc0b8 100644 --- a/UI/Pages/Shared/AresNavItem.razor +++ b/UI/Components/Nav/AresNavItem.razor @@ -30,7 +30,7 @@ } -@code { +@code { [Parameter] public string? Path { get; set; } diff --git a/UI/Pages/Shared/AresNavMenu.razor b/UI/Components/Nav/AresNavMenu.razor similarity index 96% rename from UI/Pages/Shared/AresNavMenu.razor rename to UI/Components/Nav/AresNavMenu.razor index 862cc6a9..05ac838c 100644 --- a/UI/Pages/Shared/AresNavMenu.razor +++ b/UI/Components/Nav/AresNavMenu.razor @@ -1,35 +1,35 @@ -
- - Ares Logo - -
- -
-
- @FooterContent -
-
- -
-
- -
-
- -@code { - [Parameter] - public RenderFragment? MainNavContent { get; set; } - - [Parameter] - public RenderFragment? FooterContent { get; set; } - - [Parameter] - public RenderFragment? CollapsedNavContent { get; set; } - - [Parameter] - public bool ExpandedMainNav { get; set; } -} +
+ + Ares Logo + +
+ +
+
+ @FooterContent +
+
+ +
+
+ +
+
+ +@code { + [Parameter] + public RenderFragment? MainNavContent { get; set; } + + [Parameter] + public RenderFragment? FooterContent { get; set; } + + [Parameter] + public RenderFragment? CollapsedNavContent { get; set; } + + [Parameter] + public bool ExpandedMainNav { get; set; } +} diff --git a/UI/Pages/Shared/ScriptEditor.razor b/UI/Components/ScriptEditor.razor similarity index 100% rename from UI/Pages/Shared/ScriptEditor.razor rename to UI/Components/ScriptEditor.razor diff --git a/UI/Pages/Shared/ScriptEditor.razor.css b/UI/Components/ScriptEditor.razor.css similarity index 100% rename from UI/Pages/Shared/ScriptEditor.razor.css rename to UI/Components/ScriptEditor.razor.css diff --git a/UI/Pages/Shared/SpinnerSize.cs b/UI/Components/SpinnerSize.cs similarity index 55% rename from UI/Pages/Shared/SpinnerSize.cs rename to UI/Components/SpinnerSize.cs index c9de554c..01c3c7f5 100644 --- a/UI/Pages/Shared/SpinnerSize.cs +++ b/UI/Components/SpinnerSize.cs @@ -1,7 +1,7 @@ -namespace UI.Pages.Shared; - -public enum SpinnerSize -{ - Small, - Large -} +namespace UI.Components; + +public enum SpinnerSize +{ + Small, + Large +} diff --git a/UI/Features/ScriptPlayground/ScriptPlayground.razor b/UI/Features/ScriptPlayground/ScriptPlayground.razor index 2eef76b6..27ed0005 100644 --- a/UI/Features/ScriptPlayground/ScriptPlayground.razor +++ b/UI/Features/ScriptPlayground/ScriptPlayground.razor @@ -1,6 +1,7 @@ @page "/scripting" @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @using System.Reactive.Linq +@using UI.Components
From 9fe38d4d913029436186218fb47fdbd5d6cad96a Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 15:08:29 -0500 Subject: [PATCH 09/17] Separated the rest of the items into their appropriate directories. --- .editorconfig | 260 ++++++++++++++++ UI/Components/Layouts/MainLayout.razor | 7 +- .../Settings/AnalyzerSettingsListViewModel.cs | 2 +- .../Settings/AnalyzerSettingsViewModel.cs | 2 +- .../Factories/PlanningDesignerFactory.cs | 4 +- .../PlannerAllocationEditorViewModel.cs | 4 +- .../ViewModels/PlanningViewModel.cs | 4 +- .../CampaignEdit/Views/StepViewer.razor | 2 +- .../DeviceStateExporter.razor | 1 + .../Settings/LoggingSettingsList.razor | 1 + .../Settings/LoggingSettingsListViewModel.cs | 2 +- .../CM3Camera/CM3CameraSettingsList.razor | 1 + .../ChemyxPump/ChemyxPumpSettingsList.razor | 1 + .../LaserChillerSettingsList.razor | 1 + .../Devices/Mfc/MfcConfigEditView.razor | 1 + UI/Features/Devices/Mfc/MfcSettingsList.razor | 1 + UI/Features/Devices/Mfc/MfcSettingsView.razor | 1 + UI/Features/Devices/Mfc/MfcUnitControl.razor | 1 + .../Remote/RemoteDeviceSettingsList.razor | 1 + .../RemoteDeviceSettingsListViewModel.cs | 4 +- .../Remote/RemoteDeviceSettingsViewModel.cs | 4 +- .../RestDevice/RestDeviceSettingsList.razor | 1 + .../SerialRestDeviceSettingsList.razor | 1 + .../Devices/Servo/ServoSettingsList.razor | 1 + .../Settings/DeviceSettingsContainer.razor | 1 + .../StepperControllerSettingsListView.razor | 1 + .../StepperControllerSettingsView.razor | 1 + .../SyringePump/SyringePumpSettingsList.razor | 1 + .../Tc0304/DataloggerSettingsList.razor | 1 + .../TubeFurnaceSettingsListView.razor | 1 + .../TubeFurnace/TubeFurnaceSettingsView.razor | 1 + .../ValveControllerSettingsList.razor | 1 + .../VerdiV6Laser/VerdiLaserSettingsList.razor | 1 + UI/Features/Execution/ExecutionViewModel.cs | 2 +- .../Layout/PlannerSettingsContainer.razor | 1 + .../Settings/PlannerSettingsList.razor | 1 + .../Settings/PlannerSettingsListViewModel.cs | 2 +- .../Settings/PlannerSettingsViewModel.cs | 2 +- .../ServerHealth/AresConnectionStatus.cs | 18 +- .../ServerHealthNotificationService.cs | 7 +- .../ServerHealth/ServerHealthService.cs | 291 +++++++++--------- .../Dialog/IUiDialogService.cs | 2 +- .../Dialog/RadzenUiDialogService.cs | 2 +- .../Dialog/UiConfirmOptions.cs | 2 +- .../Grpc/ClientManager.cs | 148 ++++----- .../Grpc/IClientManager.cs | 16 +- .../Hosting}/ILocalService.cs | 12 +- .../Interfaces/IUiNotificationService.cs | 2 +- .../INotificationReceivingService.cs | 4 +- .../NotificationReceivingService.cs | 2 +- .../RadzenUiNotificationService.cs | 2 +- .../Notification/UiNotificationMessage.cs | 2 +- .../UiNotificationServiceExtensions.cs | 2 +- .../Notification/UiNotificationSeverity.cs | 2 +- UI/Program.cs | 4 +- UI/ServiceCollectionExtensions.cs | 9 +- UI/ServiceStarter.cs | 2 +- UI/UI.csproj | 14 +- UI/_Imports.razor | 4 +- 59 files changed, 585 insertions(+), 287 deletions(-) create mode 100644 .editorconfig rename UI/{Services => Features}/ServerHealth/AresConnectionStatus.cs (66%) rename UI/{Services/ServerHealthNotification => Features/ServerHealth}/ServerHealthNotificationService.cs (91%) rename UI/{Services => Features}/ServerHealth/ServerHealthService.cs (95%) rename UI/{Services => Infrastructure}/Dialog/IUiDialogService.cs (79%) rename UI/{Services => Infrastructure}/Dialog/RadzenUiDialogService.cs (95%) rename UI/{Services => Infrastructure}/Dialog/UiConfirmOptions.cs (80%) rename UI/{Services => Infrastructure}/Grpc/ClientManager.cs (95%) rename UI/{Services => Infrastructure}/Grpc/IClientManager.cs (61%) rename UI/{Services => Infrastructure/Hosting}/ILocalService.cs (59%) rename UI/{Services => Infrastructure}/Notification/INotificationReceivingService.cs (69%) rename UI/{Services => Infrastructure}/Notification/NotificationReceivingService.cs (98%) rename UI/{Services => Infrastructure}/Notification/RadzenUiNotificationService.cs (96%) rename UI/{Services => Infrastructure}/Notification/UiNotificationMessage.cs (88%) rename UI/{Services => Infrastructure}/Notification/UiNotificationServiceExtensions.cs (96%) rename UI/{Services => Infrastructure}/Notification/UiNotificationSeverity.cs (65%) diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..10067523 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,260 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 2 +indent_style = space +tab_width = 2 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Code Actions #### + +# Type members +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Symbol search +dotnet_search_reference_assemblies = true + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = file_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true +csharp_style_prefer_top_level_statements = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.style = begins_with__ + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.begins_with__.required_prefix = _ +dotnet_naming_style.begins_with__.required_suffix = +dotnet_naming_style.begins_with__.word_separator = +dotnet_naming_style.begins_with__.capitalization = camel_case diff --git a/UI/Components/Layouts/MainLayout.razor b/UI/Components/Layouts/MainLayout.razor index 6cdff32a..0ba4127e 100644 --- a/UI/Components/Layouts/MainLayout.razor +++ b/UI/Components/Layouts/MainLayout.razor @@ -1,7 +1,8 @@ -@using Ares.Services +@using Ares.Services @using Google.Protobuf.WellKnownTypes -@using UI.Services.Dialog -@using UI.Services.Notification +@using UI.Components.Nav +@using UI.Infrastructure.Dialog +@using UI.Infrastructure.Notification @inherits LayoutComponentBase @inject IUiDialogService dialogService diff --git a/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs b/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs index 8307cfd1..13a30a93 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsListViewModel.cs @@ -3,7 +3,7 @@ using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.Analyzing.Settings; diff --git a/UI/Features/Analyzing/Settings/AnalyzerSettingsViewModel.cs b/UI/Features/Analyzing/Settings/AnalyzerSettingsViewModel.cs index 266ab44f..ec1b5cc7 100644 --- a/UI/Features/Analyzing/Settings/AnalyzerSettingsViewModel.cs +++ b/UI/Features/Analyzing/Settings/AnalyzerSettingsViewModel.cs @@ -3,7 +3,7 @@ using Ares.Services; using Grpc.Core; using ReactiveUI; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.Analyzing.Settings; diff --git a/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs index 068a88be..2dbb366e 100644 --- a/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs +++ b/UI/Features/CampaignEdit/Factories/PlanningDesignerFactory.cs @@ -1,8 +1,8 @@ -using Ares.Datamodel.Templates; +using Ares.Datamodel.Templates; using Ares.Services; using Google.Protobuf.WellKnownTypes; using UI.Features.CampaignEdit.ViewModels; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.CampaignEdit.Factories; diff --git a/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs index 1ba71f84..e7c4c261 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlannerAllocationEditorViewModel.cs @@ -1,9 +1,9 @@ -using Ares.Datamodel.Planning; +using Ares.Datamodel.Planning; using Ares.Datamodel.Templates; using Ares.Services; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Backend.ViewModels.Automation.CampaignEdit; diff --git a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs index 847b5e01..8cae875c 100644 --- a/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs +++ b/UI/Features/CampaignEdit/ViewModels/PlanningViewModel.cs @@ -1,9 +1,9 @@ -using ReactiveUI; +using ReactiveUI; using System.Collections.ObjectModel; using Ares.Datamodel.Templates; using Ares.Services; using Ares.Datamodel.Planning; -using UI.Services.Notification; +using UI.Infrastructure.Notification; using UI.Backend.ViewModels.Automation.CampaignEdit; namespace UI.Features.CampaignEdit.ViewModels; diff --git a/UI/Features/CampaignEdit/Views/StepViewer.razor b/UI/Features/CampaignEdit/Views/StepViewer.razor index 973268c1..8652eca7 100644 --- a/UI/Features/CampaignEdit/Views/StepViewer.razor +++ b/UI/Features/CampaignEdit/Views/StepViewer.razor @@ -16,7 +16,7 @@
@if(HasExperimentOutputs) { - + }
diff --git a/UI/Features/DeviceStateExport/DeviceStateExporter.razor b/UI/Features/DeviceStateExport/DeviceStateExporter.razor index 795f3c85..f03d230b 100644 --- a/UI/Features/DeviceStateExport/DeviceStateExporter.razor +++ b/UI/Features/DeviceStateExport/DeviceStateExporter.razor @@ -1,4 +1,5 @@ @using Ares.Services +@using UI.Components @using UI.Infrastructure.Extensions; @using UI.Backend.ViewModels.DeviceStateLogging; @using UI.Features.DeviceStateLogging diff --git a/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor index c477d0fc..0e570a30 100644 --- a/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor +++ b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsList.razor @@ -1,5 +1,6 @@ @page "/settings/logging" @using ReactiveUI.Blazor +@using UI.Components @using UI.Features.DeviceStateLogging.Settings @using UI.Components.Layouts @layout SettingsLayout diff --git a/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs index c93ff569..955c1514 100644 --- a/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs +++ b/UI/Features/DeviceStateLogging/Settings/LoggingSettingsListViewModel.cs @@ -5,7 +5,7 @@ using ReactiveUI; using ReactiveUI.SourceGenerators; using UI.Features.DeviceStateLogging; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.DeviceStateLogging.Settings; diff --git a/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor b/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor index d143a1b8..ef3e6214 100644 --- a/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor +++ b/UI/Features/Devices/CM3Camera/CM3CameraSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/cm3camera" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.CM3Camera +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor index a8132af2..fcbc4c88 100644 --- a/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor +++ b/UI/Features/Devices/ChemyxPump/ChemyxPumpSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/chemyx" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.ChemyxPump +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor b/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor index c13b5f8b..927c4b1c 100644 --- a/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor +++ b/UI/Features/Devices/LaserChiller/LaserChillerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/laserchiller" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.LaserChiller +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/Mfc/MfcConfigEditView.razor b/UI/Features/Devices/Mfc/MfcConfigEditView.razor index 57df88bf..b1ee1deb 100644 --- a/UI/Features/Devices/Mfc/MfcConfigEditView.razor +++ b/UI/Features/Devices/Mfc/MfcConfigEditView.razor @@ -1,6 +1,7 @@ @inherits ReactiveUI.Blazor.ReactiveComponentBase @using UI.Backend.ViewModels.Settings.Device.Mfc; @using Ares.Alicat.Mfc.Config; +@using UI.Components
diff --git a/UI/Features/Devices/Mfc/MfcSettingsList.razor b/UI/Features/Devices/Mfc/MfcSettingsList.razor index c7f403d8..f0bdfb27 100644 --- a/UI/Features/Devices/Mfc/MfcSettingsList.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/alicat" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Mfc +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/Mfc/MfcSettingsView.razor b/UI/Features/Devices/Mfc/MfcSettingsView.razor index 22141f52..a2e2388f 100644 --- a/UI/Features/Devices/Mfc/MfcSettingsView.razor +++ b/UI/Features/Devices/Mfc/MfcSettingsView.razor @@ -1,6 +1,7 @@ @using Ares.Alicat.Mfc.Config @using Ares.Alicat.Mfc.Messaging @using UI.Backend.ViewModels.Settings.Device.Mfc +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService diff --git a/UI/Features/Devices/Mfc/MfcUnitControl.razor b/UI/Features/Devices/Mfc/MfcUnitControl.razor index 149d1715..c68b086d 100644 --- a/UI/Features/Devices/Mfc/MfcUnitControl.razor +++ b/UI/Features/Devices/Mfc/MfcUnitControl.razor @@ -1,5 +1,6 @@ @using Ares.Alicat.Mfc.Messaging @using UI.Backend.ViewModels.Devices.Mfc +@using UI.Components @inherits ReactiveUI.Blazor.ReactiveComponentBase diff --git a/UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor b/UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor index 900b607d..d7115cea 100644 --- a/UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor +++ b/UI/Features/Devices/Remote/RemoteDeviceSettingsList.razor @@ -3,6 +3,7 @@ @using Ares.Services @using Google.Protobuf.WellKnownTypes @using UI.Backend.ViewModels.Settings.Device.Remote +@using UI.Components @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase @inject DialogService DialogService diff --git a/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs index 1101d351..954ac160 100644 --- a/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceSettingsListViewModel.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using Ares.Datamodel.Device; using Ares.Services; using Ares.Services.Device; @@ -7,7 +7,7 @@ using ReactiveUI; using ReactiveUI.SourceGenerators; using UI.Features.Devices.Remote; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Backend.ViewModels.Settings.Device.Remote; diff --git a/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs b/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs index 893a4fe2..ee657d60 100644 --- a/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs +++ b/UI/Features/Devices/Remote/RemoteDeviceSettingsViewModel.cs @@ -1,4 +1,4 @@ -using System.Reactive; +using System.Reactive; using System.Reactive.Linq; using Ares.Datamodel; using Ares.Datamodel.Device; @@ -10,7 +10,7 @@ using ReactiveUI.SourceGenerators; using UI.Features.Devices.Remote; using UI.Features.Devices.Shared; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Backend.ViewModels.Settings.Device.Remote; diff --git a/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor b/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor index 1106ea9f..7072bfef 100644 --- a/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor +++ b/UI/Features/Devices/RestDevice/RestDeviceSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/restdevice" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.RestDevice +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor index 000fc930..8dd268bb 100644 --- a/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor +++ b/UI/Features/Devices/SerialRestDevice/SerialRestDeviceSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/serialrest" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.SerialRestDevice +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/Servo/ServoSettingsList.razor b/UI/Features/Devices/Servo/ServoSettingsList.razor index 0332e3e6..c1887a75 100644 --- a/UI/Features/Devices/Servo/ServoSettingsList.razor +++ b/UI/Features/Devices/Servo/ServoSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/servo" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Servo +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor b/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor index c2d8c8bf..8c856e74 100644 --- a/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor +++ b/UI/Features/Devices/Shared/Settings/DeviceSettingsContainer.razor @@ -1,4 +1,5 @@ @using Ares.Datamodel.Device +@using UI.Components
@Name
diff --git a/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor b/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor index 92f32da9..606642bd 100644 --- a/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsListView.razor @@ -1,6 +1,7 @@ @page "/settings/device/stepper_controller" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.StepperController +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor index ca6f3835..ceeb1198 100644 --- a/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor +++ b/UI/Features/Devices/StepperController/StepperControllerSettingsView.razor @@ -1,4 +1,5 @@ @using UI.Backend.ViewModels.Settings.Device.StepperController +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService diff --git a/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor b/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor index 417fcb41..925b0a87 100644 --- a/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor +++ b/UI/Features/Devices/SyringePump/SyringePumpSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/syringe-pump" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.SyringePump +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor b/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor index 11ded9e9..d2cc2e54 100644 --- a/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor +++ b/UI/Features/Devices/Tc0304/DataloggerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/tc0304" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.Tc0304 +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor index 6f1fb890..32d0234c 100644 --- a/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsListView.razor @@ -1,6 +1,7 @@ @page "/settings/device/tube_furnace" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.TubeFurnace +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor index 6ce77fc3..bd3427a7 100644 --- a/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor +++ b/UI/Features/Devices/TubeFurnace/TubeFurnaceSettingsView.razor @@ -1,4 +1,5 @@ @using UI.Backend.ViewModels.Settings.Device.TubeFurnace +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveComponentBase @inject DialogService DialogService diff --git a/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor b/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor index 237eb2f2..6aa4a611 100644 --- a/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor +++ b/UI/Features/Devices/ValveController/ValveControllerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/ValveController" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.ValveController +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor index cedfe44e..91dcf282 100644 --- a/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor +++ b/UI/Features/Devices/VerdiV6Laser/VerdiLaserSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/device/verdilaser" @layout DeviceSettingsLayout @using UI.Backend.ViewModels.Settings.Device.VerdiLaser +@using UI.Components @using UI.Infrastructure.Interfaces @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Execution/ExecutionViewModel.cs b/UI/Features/Execution/ExecutionViewModel.cs index 62ecc913..a2ca9a90 100644 --- a/UI/Features/Execution/ExecutionViewModel.cs +++ b/UI/Features/Execution/ExecutionViewModel.cs @@ -8,7 +8,7 @@ using Ares.Datamodel.Templates; using Ares.Services; using UI.Infrastructure.Extensions; -using UI.Services.Notification; +using UI.Infrastructure.Notification; using Ares.Datamodel.Planning; using System.ComponentModel; using System.Reactive.Linq; diff --git a/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor index cefcbbbf..fa9e7e82 100644 --- a/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor +++ b/UI/Features/Planning/Settings/Layout/PlannerSettingsContainer.razor @@ -1,4 +1,5 @@ @using Ares.Datamodel.Connection +@using UI.Components
@Name
diff --git a/UI/Features/Planning/Settings/PlannerSettingsList.razor b/UI/Features/Planning/Settings/PlannerSettingsList.razor index 57a80bdc..6319ad9b 100644 --- a/UI/Features/Planning/Settings/PlannerSettingsList.razor +++ b/UI/Features/Planning/Settings/PlannerSettingsList.razor @@ -1,6 +1,7 @@ @page "/settings/planning" @using Ares.Services @using Google.Protobuf.WellKnownTypes +@using UI.Components @using UI.Features.Planning.Settings @using UI.Features.Planning.Settings.Layout @inherits ReactiveUI.Blazor.ReactiveInjectableComponentBase diff --git a/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs b/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs index d8e7795c..20786cb7 100644 --- a/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs +++ b/UI/Features/Planning/Settings/PlannerSettingsListViewModel.cs @@ -3,7 +3,7 @@ using Google.Protobuf.WellKnownTypes; using ReactiveUI; using ReactiveUI.SourceGenerators; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.Planning.Settings; diff --git a/UI/Features/Planning/Settings/PlannerSettingsViewModel.cs b/UI/Features/Planning/Settings/PlannerSettingsViewModel.cs index 1b6974e9..32020c31 100644 --- a/UI/Features/Planning/Settings/PlannerSettingsViewModel.cs +++ b/UI/Features/Planning/Settings/PlannerSettingsViewModel.cs @@ -3,7 +3,7 @@ using Ares.Services; using Grpc.Core; using ReactiveUI; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Features.Planning.Settings; diff --git a/UI/Services/ServerHealth/AresConnectionStatus.cs b/UI/Features/ServerHealth/AresConnectionStatus.cs similarity index 66% rename from UI/Services/ServerHealth/AresConnectionStatus.cs rename to UI/Features/ServerHealth/AresConnectionStatus.cs index 56ffed8b..03e11e1d 100644 --- a/UI/Services/ServerHealth/AresConnectionStatus.cs +++ b/UI/Features/ServerHealth/AresConnectionStatus.cs @@ -1,9 +1,9 @@ -namespace UI.Services.ServerHealth; - -public enum AresConnectionStatus -{ - Unattempted, - Connected, - Disconnected, - Connecting -} +namespace UI.Features.ServerHealth; + +public enum AresConnectionStatus +{ + Unattempted, + Connected, + Disconnected, + Connecting +} diff --git a/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs b/UI/Features/ServerHealth/ServerHealthNotificationService.cs similarity index 91% rename from UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs rename to UI/Features/ServerHealth/ServerHealthNotificationService.cs index 92a346cc..010fcfc1 100644 --- a/UI/Services/ServerHealthNotification/ServerHealthNotificationService.cs +++ b/UI/Features/ServerHealth/ServerHealthNotificationService.cs @@ -1,9 +1,10 @@ using Ares.Services; +using UI.Infrastructure.Hosting; using UI.Infrastructure.Interfaces; -using UI.Services.Notification; -using UI.Services.ServerHealth; +using UI.Infrastructure.Notification; +using UI.Features.ServerHealth; -namespace UI.Services.ServerHealthNotification; +namespace UI.Features.ServerHealth; /// /// Uses the server health service to grab new state messages and publish them to the notification repo/service diff --git a/UI/Services/ServerHealth/ServerHealthService.cs b/UI/Features/ServerHealth/ServerHealthService.cs similarity index 95% rename from UI/Services/ServerHealth/ServerHealthService.cs rename to UI/Features/ServerHealth/ServerHealthService.cs index b2a67cb7..f9248b1e 100644 --- a/UI/Services/ServerHealth/ServerHealthService.cs +++ b/UI/Features/ServerHealth/ServerHealthService.cs @@ -1,145 +1,146 @@ -using System.Reactive.Linq; -using System.Reactive.Subjects; -using Ares.Services; -using Google.Protobuf.WellKnownTypes; -using Grpc.Core; -using Grpc.Health.V1; - -namespace UI.Services.ServerHealth; - -/// -/// Responsible for using an ares server info client to get state messages from the server and publishing them -/// to an internal observable -/// -internal class ServerHealthService : ILocalService -{ - private readonly AresServerInfo.AresServerInfoClient _aresServerInfo; - private readonly Health.HealthClient _healthClient; - private readonly ILogger _logger; - private readonly ISubject _serverStatusSubject = new BehaviorSubject(new ServerStatusResponse { ServerStatus = Ares.Services.ServerStatus.Idle, StatusMessage = "Not Connected" }); - - public readonly IObservable ServerStatus; - private Task _heartbeatListener = Task.CompletedTask; - private CancellationTokenSource _serviceCancellationTokenSource = new(); - private Task _stateListener = Task.CompletedTask; - - public ServerHealthService(AresServerInfo.AresServerInfoClient aresServerInfo, Health.HealthClient healthClient, ILogger logger) - { - _aresServerInfo = aresServerInfo; - _healthClient = healthClient; - _logger = logger; - ServerStatus = _serverStatusSubject.AsObservable(); - } - - private bool Running => !_heartbeatListener.IsCompleted || !_stateListener.IsCompleted; - - public AresConnectionStatus AresConnectionStatus { get; private set; } - - public string ServerName { get; private set; } = string.Empty; - - public Version ServerVersion { get; private set; } = new(); - - public async Task Start() - { - if (Running) - return; - - _serviceCancellationTokenSource = new CancellationTokenSource(); - AresConnectionStatus = AresConnectionStatus.Connecting; - try - { - var info = await _aresServerInfo.GetServerInfoAsync(new Empty(), null, null, _serviceCancellationTokenSource.Token); - ServerName = info.ServerName; - ServerVersion = Version.Parse(info.Version); - AresConnectionStatus = AresConnectionStatus.Connected; - } - catch (RpcException e) - { - AresConnectionStatus = AresConnectionStatus.Disconnected; - ServerName = string.Empty; - ServerVersion = new Version(); - _logger.LogError("Failed to start {}: {}", GetType().Name, e.Message); - return; - } - - _stateListener = Task.Run(StartListening, _serviceCancellationTokenSource.Token); - _heartbeatListener = Task.Run(EstablishHeartbeat, _serviceCancellationTokenSource.Token); - } - - public async void Stop() - { - _serviceCancellationTokenSource.Cancel(); - - await Task.WhenAll(_heartbeatListener, _stateListener); - } - - private async void EstablishHeartbeat() - { - var healthRequest = new HealthCheckRequest { Service = string.Empty }; - while (!_serviceCancellationTokenSource.Token.IsCancellationRequested) - try - { - var timeout = TimeSpan.FromSeconds(15); - var timeoutTask = Task.Delay(timeout, _serviceCancellationTokenSource.Token); - var healthTask = _healthClient.CheckAsync(healthRequest, null, null, _serviceCancellationTokenSource.Token).ResponseAsync; - var completedTask = await Task.WhenAny(timeoutTask, healthTask); - - if (completedTask == timeoutTask) - { - HandleDisconnect("Lost connection to server. Health check timed out."); - return; - } - - if (completedTask.IsFaulted && completedTask.Exception?.InnerException is RpcException e) - { - HandleDisconnect(e.Message); - return; - } - - await Task.Delay(timeout); - } - catch (RpcException e) - { - HandleDisconnect(e.Message); - } - catch (OperationCanceledException) - { - HandleDisconnect($"{GetType().Name} stopping."); - } - } - - private void HandleDisconnect(string message = "") - { - ServerName = string.Empty; - ServerVersion = new Version(); - AresConnectionStatus = AresConnectionStatus.Disconnected; - _serverStatusSubject.OnNext(new ServerStatusResponse - { - ServerStatus = Ares.Services.ServerStatus.Error, - StatusMessage = $"Server is offline: {message}" - }); - - _logger.LogError("Disconnected from server: {}", message); - - Stop(); - } - - private async Task StartListening() - { - var statusStream = _aresServerInfo.GetServerStatusStream(new Empty(), null, null, _serviceCancellationTokenSource.Token); - try - { - while (await statusStream.ResponseStream.MoveNext(_serviceCancellationTokenSource.Token) && !_serviceCancellationTokenSource.IsCancellationRequested) - { - var response = statusStream.ResponseStream.Current; - _logger.LogInformation("Received state from server: {}", response.StatusMessage); - _serverStatusSubject.OnNext(response); - } - } - catch (RpcException e) - { - _logger.LogError("Failed to get state from server: {}", e.Message); - HandleDisconnect(e.Message); - } - } -} +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Ares.Services; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; +using Grpc.Health.V1; +using UI.Infrastructure.Hosting; + +namespace UI.Features.ServerHealth; + +/// +/// Responsible for using an ares server info client to get state messages from the server and publishing them +/// to an internal observable +/// +internal class ServerHealthService : ILocalService +{ + private readonly AresServerInfo.AresServerInfoClient _aresServerInfo; + private readonly Health.HealthClient _healthClient; + private readonly ILogger _logger; + private readonly ISubject _serverStatusSubject = new BehaviorSubject(new ServerStatusResponse { ServerStatus = Ares.Services.ServerStatus.Idle, StatusMessage = "Not Connected" }); + + public readonly IObservable ServerStatus; + private Task _heartbeatListener = Task.CompletedTask; + private CancellationTokenSource _serviceCancellationTokenSource = new(); + private Task _stateListener = Task.CompletedTask; + + public ServerHealthService(AresServerInfo.AresServerInfoClient aresServerInfo, Health.HealthClient healthClient, ILogger logger) + { + _aresServerInfo = aresServerInfo; + _healthClient = healthClient; + _logger = logger; + ServerStatus = _serverStatusSubject.AsObservable(); + } + + private bool Running => !_heartbeatListener.IsCompleted || !_stateListener.IsCompleted; + + public AresConnectionStatus AresConnectionStatus { get; private set; } + + public string ServerName { get; private set; } = string.Empty; + + public Version ServerVersion { get; private set; } = new(); + + public async Task Start() + { + if (Running) + return; + + _serviceCancellationTokenSource = new CancellationTokenSource(); + AresConnectionStatus = AresConnectionStatus.Connecting; + try + { + var info = await _aresServerInfo.GetServerInfoAsync(new Empty(), null, null, _serviceCancellationTokenSource.Token); + ServerName = info.ServerName; + ServerVersion = Version.Parse(info.Version); + AresConnectionStatus = AresConnectionStatus.Connected; + } + catch (RpcException e) + { + AresConnectionStatus = AresConnectionStatus.Disconnected; + ServerName = string.Empty; + ServerVersion = new Version(); + _logger.LogError("Failed to start {}: {}", GetType().Name, e.Message); + return; + } + + _stateListener = Task.Run(StartListening, _serviceCancellationTokenSource.Token); + _heartbeatListener = Task.Run(EstablishHeartbeat, _serviceCancellationTokenSource.Token); + } + + public async void Stop() + { + _serviceCancellationTokenSource.Cancel(); + + await Task.WhenAll(_heartbeatListener, _stateListener); + } + + private async void EstablishHeartbeat() + { + var healthRequest = new HealthCheckRequest { Service = string.Empty }; + while (!_serviceCancellationTokenSource.Token.IsCancellationRequested) + try + { + var timeout = TimeSpan.FromSeconds(15); + var timeoutTask = Task.Delay(timeout, _serviceCancellationTokenSource.Token); + var healthTask = _healthClient.CheckAsync(healthRequest, null, null, _serviceCancellationTokenSource.Token).ResponseAsync; + var completedTask = await Task.WhenAny(timeoutTask, healthTask); + + if (completedTask == timeoutTask) + { + HandleDisconnect("Lost connection to server. Health check timed out."); + return; + } + + if (completedTask.IsFaulted && completedTask.Exception?.InnerException is RpcException e) + { + HandleDisconnect(e.Message); + return; + } + + await Task.Delay(timeout); + } + catch (RpcException e) + { + HandleDisconnect(e.Message); + } + catch (OperationCanceledException) + { + HandleDisconnect($"{GetType().Name} stopping."); + } + } + + private void HandleDisconnect(string message = "") + { + ServerName = string.Empty; + ServerVersion = new Version(); + AresConnectionStatus = AresConnectionStatus.Disconnected; + _serverStatusSubject.OnNext(new ServerStatusResponse + { + ServerStatus = Ares.Services.ServerStatus.Error, + StatusMessage = $"Server is offline: {message}" + }); + + _logger.LogError("Disconnected from server: {}", message); + + Stop(); + } + + private async Task StartListening() + { + var statusStream = _aresServerInfo.GetServerStatusStream(new Empty(), null, null, _serviceCancellationTokenSource.Token); + try + { + while (await statusStream.ResponseStream.MoveNext(_serviceCancellationTokenSource.Token) && !_serviceCancellationTokenSource.IsCancellationRequested) + { + var response = statusStream.ResponseStream.Current; + _logger.LogInformation("Received state from server: {}", response.StatusMessage); + _serverStatusSubject.OnNext(response); + } + } + catch (RpcException e) + { + _logger.LogError("Failed to get state from server: {}", e.Message); + HandleDisconnect(e.Message); + } + } +} diff --git a/UI/Services/Dialog/IUiDialogService.cs b/UI/Infrastructure/Dialog/IUiDialogService.cs similarity index 79% rename from UI/Services/Dialog/IUiDialogService.cs rename to UI/Infrastructure/Dialog/IUiDialogService.cs index 00837c1f..b5ae3eba 100644 --- a/UI/Services/Dialog/IUiDialogService.cs +++ b/UI/Infrastructure/Dialog/IUiDialogService.cs @@ -1,4 +1,4 @@ -namespace UI.Services.Dialog; +namespace UI.Infrastructure.Dialog; public interface IUiDialogService { diff --git a/UI/Services/Dialog/RadzenUiDialogService.cs b/UI/Infrastructure/Dialog/RadzenUiDialogService.cs similarity index 95% rename from UI/Services/Dialog/RadzenUiDialogService.cs rename to UI/Infrastructure/Dialog/RadzenUiDialogService.cs index 3558f030..1e09b14a 100644 --- a/UI/Services/Dialog/RadzenUiDialogService.cs +++ b/UI/Infrastructure/Dialog/RadzenUiDialogService.cs @@ -1,6 +1,6 @@ using Radzen; -namespace UI.Services.Dialog; +namespace UI.Infrastructure.Dialog; internal sealed class RadzenUiDialogService : IUiDialogService { diff --git a/UI/Services/Dialog/UiConfirmOptions.cs b/UI/Infrastructure/Dialog/UiConfirmOptions.cs similarity index 80% rename from UI/Services/Dialog/UiConfirmOptions.cs rename to UI/Infrastructure/Dialog/UiConfirmOptions.cs index 7abe55dd..0429f313 100644 --- a/UI/Services/Dialog/UiConfirmOptions.cs +++ b/UI/Infrastructure/Dialog/UiConfirmOptions.cs @@ -1,4 +1,4 @@ -namespace UI.Services.Dialog; +namespace UI.Infrastructure.Dialog; public sealed class UiConfirmOptions { diff --git a/UI/Services/Grpc/ClientManager.cs b/UI/Infrastructure/Grpc/ClientManager.cs similarity index 95% rename from UI/Services/Grpc/ClientManager.cs rename to UI/Infrastructure/Grpc/ClientManager.cs index 42d50bcb..060f9034 100644 --- a/UI/Services/Grpc/ClientManager.cs +++ b/UI/Infrastructure/Grpc/ClientManager.cs @@ -1,74 +1,74 @@ -using Grpc.Core; -using Grpc.Net.Client; -using Microsoft.Extensions.Options; -using System.Security.Cryptography.X509Certificates; -using UI.Authentication; -using UI.Settings; - -namespace UI.Services.Grpc; - -internal class ClientManager : IClientManager -{ - private readonly AresAuthenticationState _authenticationState; - private readonly CertificateSettings _certificateOptions; - private readonly ILogger _logger; - private readonly RemoteServiceSettings _remoteServiceSettings; - private GrpcChannel? _channel; - - public ClientManager(IOptions remoteOptions, - IOptions certificateOptions, - AresAuthenticationState authenticationState, - ILogger logger) - { - _certificateOptions = certificateOptions.Value; - _authenticationState = authenticationState; - _logger = logger; - _remoteServiceSettings = remoteOptions.Value; - CreateChannel().GetAwaiter().GetResult(); - } - - private async Task CreateChannel() - { - if (_channel is not null) - { - await _channel.ShutdownAsync(); - _channel.Dispose(); - } - - if (_remoteServiceSettings.ServerPort == 0 || string.IsNullOrEmpty(_remoteServiceSettings.ServerHost)) - return; - - var handler = new HttpClientHandler(); - try - { - var cert = new X509Certificate2(_certificateOptions.Path ?? "", _certificateOptions.Password); - handler.ClientCertificates.Add(cert); - handler.CheckCertificateRevocationList = false; - } - catch (Exception) - { - _logger.LogWarning("Unable to create a secure gRPC channel as the specified certificate was not found. Ensure that appsettings.json has the correct certificate path."); - // throw new InvalidOperationException("Unable to create a secure gRPC channel as the specified certificate was not found. Ensure that appsettings.json has the correct certificate path."); - } - - var serverUri = new UriBuilder("https", _remoteServiceSettings.ServerHost, _remoteServiceSettings.ServerPort ?? 443).Uri; - var opts = new GrpcChannelOptions - { - HttpHandler = handler - }; - - _channel = GrpcChannel.ForAddress(serverUri, opts); - } - - public T GetClient() where T : ClientBase - { - if (_channel is null) - throw new NullReferenceException($"Couldn't create a client as {nameof(GrpcChannel)} was not created"); - - var client = (T?)Activator.CreateInstance(typeof(T), _channel); - if (client is null) - throw new InvalidOperationException($"Unable to create client of type {nameof(T)}"); - - return client; - } -} +using Grpc.Core; +using Grpc.Net.Client; +using Microsoft.Extensions.Options; +using System.Security.Cryptography.X509Certificates; +using UI.Authentication; +using UI.Settings; + +namespace UI.Infrastructure.Grpc; + +internal class ClientManager : IClientManager +{ + private readonly AresAuthenticationState _authenticationState; + private readonly CertificateSettings _certificateOptions; + private readonly ILogger _logger; + private readonly RemoteServiceSettings _remoteServiceSettings; + private GrpcChannel? _channel; + + public ClientManager(IOptions remoteOptions, + IOptions certificateOptions, + AresAuthenticationState authenticationState, + ILogger logger) + { + _certificateOptions = certificateOptions.Value; + _authenticationState = authenticationState; + _logger = logger; + _remoteServiceSettings = remoteOptions.Value; + CreateChannel().GetAwaiter().GetResult(); + } + + private async Task CreateChannel() + { + if (_channel is not null) + { + await _channel.ShutdownAsync(); + _channel.Dispose(); + } + + if (_remoteServiceSettings.ServerPort == 0 || string.IsNullOrEmpty(_remoteServiceSettings.ServerHost)) + return; + + var handler = new HttpClientHandler(); + try + { + var cert = new X509Certificate2(_certificateOptions.Path ?? "", _certificateOptions.Password); + handler.ClientCertificates.Add(cert); + handler.CheckCertificateRevocationList = false; + } + catch (Exception) + { + _logger.LogWarning("Unable to create a secure gRPC channel as the specified certificate was not found. Ensure that appsettings.json has the correct certificate path."); + // throw new InvalidOperationException("Unable to create a secure gRPC channel as the specified certificate was not found. Ensure that appsettings.json has the correct certificate path."); + } + + var serverUri = new UriBuilder("https", _remoteServiceSettings.ServerHost, _remoteServiceSettings.ServerPort ?? 443).Uri; + var opts = new GrpcChannelOptions + { + HttpHandler = handler + }; + + _channel = GrpcChannel.ForAddress(serverUri, opts); + } + + public T GetClient() where T : ClientBase + { + if (_channel is null) + throw new NullReferenceException($"Couldn't create a client as {nameof(GrpcChannel)} was not created"); + + var client = (T?)Activator.CreateInstance(typeof(T), _channel); + if (client is null) + throw new InvalidOperationException($"Unable to create client of type {nameof(T)}"); + + return client; + } +} diff --git a/UI/Services/Grpc/IClientManager.cs b/UI/Infrastructure/Grpc/IClientManager.cs similarity index 61% rename from UI/Services/Grpc/IClientManager.cs rename to UI/Infrastructure/Grpc/IClientManager.cs index 834e8a4d..2b364d46 100644 --- a/UI/Services/Grpc/IClientManager.cs +++ b/UI/Infrastructure/Grpc/IClientManager.cs @@ -1,8 +1,8 @@ -using Grpc.Core; - -namespace UI.Services.Grpc; - -public interface IClientManager -{ - public T GetClient() where T : ClientBase; -} +using Grpc.Core; + +namespace UI.Infrastructure.Grpc; + +public interface IClientManager +{ + public T GetClient() where T : ClientBase; +} diff --git a/UI/Services/ILocalService.cs b/UI/Infrastructure/Hosting/ILocalService.cs similarity index 59% rename from UI/Services/ILocalService.cs rename to UI/Infrastructure/Hosting/ILocalService.cs index 6809ee4e..06856a03 100644 --- a/UI/Services/ILocalService.cs +++ b/UI/Infrastructure/Hosting/ILocalService.cs @@ -1,6 +1,6 @@ -namespace UI.Services; - -internal interface ILocalService -{ - Task Start(); -} +namespace UI.Infrastructure.Hosting; + +internal interface ILocalService +{ + Task Start(); +} diff --git a/UI/Infrastructure/Interfaces/IUiNotificationService.cs b/UI/Infrastructure/Interfaces/IUiNotificationService.cs index 5895569b..2667dfc4 100644 --- a/UI/Infrastructure/Interfaces/IUiNotificationService.cs +++ b/UI/Infrastructure/Interfaces/IUiNotificationService.cs @@ -1,4 +1,4 @@ -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI.Infrastructure.Interfaces; diff --git a/UI/Services/Notification/INotificationReceivingService.cs b/UI/Infrastructure/Notification/INotificationReceivingService.cs similarity index 69% rename from UI/Services/Notification/INotificationReceivingService.cs rename to UI/Infrastructure/Notification/INotificationReceivingService.cs index b4e733d1..f8c17290 100644 --- a/UI/Services/Notification/INotificationReceivingService.cs +++ b/UI/Infrastructure/Notification/INotificationReceivingService.cs @@ -1,6 +1,6 @@ -using Ares.Services; +using Ares.Services; -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; public interface INotificationReceivingService { diff --git a/UI/Services/Notification/NotificationReceivingService.cs b/UI/Infrastructure/Notification/NotificationReceivingService.cs similarity index 98% rename from UI/Services/Notification/NotificationReceivingService.cs rename to UI/Infrastructure/Notification/NotificationReceivingService.cs index bf8ddb35..df43850b 100644 --- a/UI/Services/Notification/NotificationReceivingService.cs +++ b/UI/Infrastructure/Notification/NotificationReceivingService.cs @@ -5,7 +5,7 @@ using UI.Infrastructure.Notifications; using UI.Infrastructure.Interfaces; -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; public class NotificationReceivingService : INotificationReceivingService { diff --git a/UI/Services/Notification/RadzenUiNotificationService.cs b/UI/Infrastructure/Notification/RadzenUiNotificationService.cs similarity index 96% rename from UI/Services/Notification/RadzenUiNotificationService.cs rename to UI/Infrastructure/Notification/RadzenUiNotificationService.cs index 30e222d8..cb6ffcd0 100644 --- a/UI/Services/Notification/RadzenUiNotificationService.cs +++ b/UI/Infrastructure/Notification/RadzenUiNotificationService.cs @@ -1,7 +1,7 @@ using Radzen; using UI.Infrastructure.Interfaces; -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; internal sealed class RadzenUiNotificationService : IUiNotificationService { diff --git a/UI/Services/Notification/UiNotificationMessage.cs b/UI/Infrastructure/Notification/UiNotificationMessage.cs similarity index 88% rename from UI/Services/Notification/UiNotificationMessage.cs rename to UI/Infrastructure/Notification/UiNotificationMessage.cs index 83076b2a..cbc96f43 100644 --- a/UI/Services/Notification/UiNotificationMessage.cs +++ b/UI/Infrastructure/Notification/UiNotificationMessage.cs @@ -1,4 +1,4 @@ -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; public sealed record UiNotificationMessage { diff --git a/UI/Services/Notification/UiNotificationServiceExtensions.cs b/UI/Infrastructure/Notification/UiNotificationServiceExtensions.cs similarity index 96% rename from UI/Services/Notification/UiNotificationServiceExtensions.cs rename to UI/Infrastructure/Notification/UiNotificationServiceExtensions.cs index 340d01df..fe786b1f 100644 --- a/UI/Services/Notification/UiNotificationServiceExtensions.cs +++ b/UI/Infrastructure/Notification/UiNotificationServiceExtensions.cs @@ -1,6 +1,6 @@ using UI.Infrastructure.Interfaces; -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; public static class UiNotificationServiceExtensions { diff --git a/UI/Services/Notification/UiNotificationSeverity.cs b/UI/Infrastructure/Notification/UiNotificationSeverity.cs similarity index 65% rename from UI/Services/Notification/UiNotificationSeverity.cs rename to UI/Infrastructure/Notification/UiNotificationSeverity.cs index 01d97443..004f13b5 100644 --- a/UI/Services/Notification/UiNotificationSeverity.cs +++ b/UI/Infrastructure/Notification/UiNotificationSeverity.cs @@ -1,4 +1,4 @@ -namespace UI.Services.Notification; +namespace UI.Infrastructure.Notification; public enum UiNotificationSeverity { diff --git a/UI/Program.cs b/UI/Program.cs index 01aeff6c..b34607f0 100644 --- a/UI/Program.cs +++ b/UI/Program.cs @@ -5,8 +5,8 @@ using UI; using UI.Infrastructure.Helpers; using UI.Domain; -using UI.Services.Grpc; -using UI.Services.Notification; +using UI.Infrastructure.Grpc; +using UI.Infrastructure.Notification; using UI.Settings; var builder = WebApplication.CreateBuilder(args); diff --git a/UI/ServiceCollectionExtensions.cs b/UI/ServiceCollectionExtensions.cs index 9637655c..13b67518 100644 --- a/UI/ServiceCollectionExtensions.cs +++ b/UI/ServiceCollectionExtensions.cs @@ -65,11 +65,10 @@ using UI.Features.DeviceStateLogging; using UI.Infrastructure.Interfaces; using UI.Infrastructure.Monaco.Interops; -using UI.Services.Dialog; -using UI.Services.Grpc; -using UI.Services.Notification; -using UI.Services.ServerHealth; -using UI.Services.ServerHealthNotification; +using UI.Infrastructure.Dialog; +using UI.Infrastructure.Grpc; +using UI.Infrastructure.Notification; +using UI.Features.ServerHealth; using ValveController.Services; using VerdiV6.Services; diff --git a/UI/ServiceStarter.cs b/UI/ServiceStarter.cs index 4bf9dbeb..3952a4ac 100644 --- a/UI/ServiceStarter.cs +++ b/UI/ServiceStarter.cs @@ -10,7 +10,7 @@ using UI.Features.Devices.Tc0304; using UI.Features.Devices.TubeFurnace; using UI.Features.Devices.ValveController; -using UI.Services.Notification; +using UI.Infrastructure.Notification; namespace UI; diff --git a/UI/UI.csproj b/UI/UI.csproj index e550705a..9f4ce9e9 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -7,12 +7,25 @@ aspnet-UI-87962D8C-B573-4F3B-852F-E1DA10F52308 + + RZ10012 + + + + RZ10012 + + + + + + + @@ -46,7 +59,6 @@ - diff --git a/UI/_Imports.razor b/UI/_Imports.razor index 1b2ebf59..776a5d11 100644 --- a/UI/_Imports.razor +++ b/UI/_Imports.razor @@ -1,4 +1,4 @@ -@using System.Net.Http +@using System.Net.Http @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Forms @@ -10,7 +10,7 @@ @using UI.Features.Devices.Shared @using UI.Features.Devices.Shared.Settings @using UI.Pages.Shared -@using UI.Services.Notification +@using UI.Infrastructure.Notification @using Radzen @using Radzen.Blazor @using BlazorMonaco From 9ab771a89e3f7e20dc7d09d7e722a36d72022177 Mon Sep 17 00:00:00 2001 From: "Babeckis, Arnas" Date: Mon, 9 Feb 2026 16:27:59 -0500 Subject: [PATCH 10/17] Moved the experiment viewer components out into a shared components directory. --- .../Views => Components/Experiments}/CommandViewer.razor | 0 .../Experiments}/ExperimentOutputIcon.razor | 0 .../Views => Components/Experiments}/ExperimentViewer.razor | 0 .../Views => Components/Experiments}/StepViewer.razor | 0 UI/Features/CampaignEdit/Views/StepDesigner.razor | 2 +- UI/_Imports.razor | 4 ++++ 6 files changed, 5 insertions(+), 1 deletion(-) rename UI/{Features/CampaignEdit/Views => Components/Experiments}/CommandViewer.razor (100%) rename UI/{Features/CampaignEdit/Views => Components/Experiments}/ExperimentOutputIcon.razor (100%) rename UI/{Features/CampaignEdit/Views => Components/Experiments}/ExperimentViewer.razor (100%) rename UI/{Features/CampaignEdit/Views => Components/Experiments}/StepViewer.razor (100%) diff --git a/UI/Features/CampaignEdit/Views/CommandViewer.razor b/UI/Components/Experiments/CommandViewer.razor similarity index 100% rename from UI/Features/CampaignEdit/Views/CommandViewer.razor rename to UI/Components/Experiments/CommandViewer.razor diff --git a/UI/Features/CampaignEdit/Views/ExperimentOutputIcon.razor b/UI/Components/Experiments/ExperimentOutputIcon.razor similarity index 100% rename from UI/Features/CampaignEdit/Views/ExperimentOutputIcon.razor rename to UI/Components/Experiments/ExperimentOutputIcon.razor diff --git a/UI/Features/CampaignEdit/Views/ExperimentViewer.razor b/UI/Components/Experiments/ExperimentViewer.razor similarity index 100% rename from UI/Features/CampaignEdit/Views/ExperimentViewer.razor rename to UI/Components/Experiments/ExperimentViewer.razor diff --git a/UI/Features/CampaignEdit/Views/StepViewer.razor b/UI/Components/Experiments/StepViewer.razor similarity index 100% rename from UI/Features/CampaignEdit/Views/StepViewer.razor rename to UI/Components/Experiments/StepViewer.razor diff --git a/UI/Features/CampaignEdit/Views/StepDesigner.razor b/UI/Features/CampaignEdit/Views/StepDesigner.razor index efac9fe7..80f3de9e 100644 --- a/UI/Features/CampaignEdit/Views/StepDesigner.razor +++ b/UI/Features/CampaignEdit/Views/StepDesigner.razor @@ -13,7 +13,7 @@ @ViewModel!.Name @ViewModel!.CommandDesigners.Count