aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels
diff options
context:
space:
mode:
authorMirta <mirta@twine-s.com>2020-11-23 16:13:53 +0200
committerMirta <mirta@twine-s.com>2020-11-23 16:13:53 +0200
commit91c007adced573e09b77ab4be4a5aba623a816cc (patch)
tree250221fc2def7d59f1393be8394f766faf576656 /Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels
parent4e9af2b852eb3b9eecfa09e9bc76869558e183cb (diff)
parent50a3c0b857b4aa88a9e3970d69256f12b5b24eb8 (diff)
downloadTango-91c007adced573e09b77ab4be4a5aba623a816cc.tar.gz
Tango-91c007adced573e09b77ab4be4a5aba623a816cc.zip
Merge branch 'master' of https://twinetfs.visualstudio.com/Tango/_git/Tango
Diffstat (limited to 'Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/DataStoreViewVM.cs517
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MachineViewVM.cs74
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MainViewVM.cs63
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SelectionViewVM.cs80
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SettingsViewVM.cs144
5 files changed, 878 insertions, 0 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/DataStoreViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/DataStoreViewVM.cs
new file mode 100644
index 000000000..46f388461
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/DataStoreViewVM.cs
@@ -0,0 +1,517 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using Tango.Core.Commands;
+using Tango.Core.DI;
+using Tango.CSV;
+using Tango.DataStore;
+using Tango.DataStore.Editing;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.DataStore;
+using Tango.FSE.Common.Notifications;
+using Tango.FSE.MachineConfiguration.Dialogs;
+using Tango.FSE.MachineConfiguration.Messages;
+using Tango.FSE.MachineConfiguration.Models;
+using static Tango.FSE.BL.Services.MachineConfigurationService;
+using static Tango.SharedUI.Controls.NavigationControl;
+
+namespace Tango.FSE.MachineConfiguration.ViewModels
+{
+ public class DataStoreViewVM : FSEViewModel, INavigationViewModel
+ {
+ private String _machineGuid;
+ private MachineEditingComposition _editingComposition;
+ private ICollectionView _selectedCollectionView;
+ private ICollectionView _collectionsView;
+
+ [TangoInject]
+ public IDataStoreProvider DataStoreProvider { get; set; }
+
+ private DataStoreModel _dataStore;
+ public DataStoreModel DataStore
+ {
+ get { return _dataStore; }
+ set { _dataStore = value; RaisePropertyChangedAuto(); }
+ }
+
+ private DataStoreCollectionModel _selectedCollection;
+ public DataStoreCollectionModel SelectedCollection
+ {
+ get { return _selectedCollection; }
+ set { _selectedCollection = value; RaisePropertyChangedAuto(); OnSelectedCollectionChanged(); }
+ }
+
+ private DataStoreItemModel _selectedItem;
+ public DataStoreItemModel SelectedItem
+ {
+ get { return _selectedItem; }
+ set { _selectedItem = value; RaisePropertyChangedAuto(); }
+ }
+
+ private String _filter;
+ public String Filter
+ {
+ get { return _filter; }
+ set { _filter = value; RaisePropertyChangedAuto(); _selectedCollectionView?.Refresh(); }
+ }
+
+ public bool CanSync
+ {
+ get { return MachineProvider.IsPPCAvailable && MachineProvider.Machine != null && _machineGuid == MachineProvider.Machine.Guid; }
+ }
+
+ public RelayCommand<DataStoreItemModel> RemoveItemCommand { get; set; }
+ public RelayCommand<DataStoreCollectionModel> RemoveCollectionCommand { get; set; }
+ public RelayCommand AddItemCommand { get; set; }
+ public RelayCommand AddCollectionCommand { get; set; }
+ public RelayCommand<DataStoreItemModel> EditItemCommand { get; set; }
+ public RelayCommand SaveCommand { get; set; }
+ public RelayCommand ReloadCommand { get; set; }
+ public RelayCommand ImportCommand { get; set; }
+ public RelayCommand ExportCommand { get; set; }
+
+ public DataStoreViewVM()
+ {
+ RegisterForMessage<EditingCompositionLoadedMessage>(OnEditingCompositionLoaded);
+ RemoveItemCommand = new RelayCommand<DataStoreItemModel>(RemoveItem);
+ RemoveCollectionCommand = new RelayCommand<DataStoreCollectionModel>(RemoveCollection);
+ AddItemCommand = new RelayCommand(AddItem);
+ AddCollectionCommand = new RelayCommand(AddCollection);
+ EditItemCommand = new RelayCommand<DataStoreItemModel>(EditItem);
+ SaveCommand = new RelayCommand(SaveModel);
+ ReloadCommand = new RelayCommand(ReloadDataStore);
+ ExportCommand = new RelayCommand(ExportDataStore);
+ ImportCommand = new RelayCommand(ImportDataStore);
+ }
+
+ [TangoInject]
+ public DataStoreViewVM(IMachineProvider machineProvider) : this()
+ {
+ machineProvider.MachineConnected += (_, __) => RaisePropertyChanged(nameof(CanSync));
+ machineProvider.MachineDisconnected += (_, __) => RaisePropertyChanged(nameof(CanSync));
+ }
+
+ private async void ReloadDataStore()
+ {
+ if (DataStore != null)
+ {
+ if (DataStore.Collections.SelectMany(x => x.Items).Any(x => x.HasDifference))
+ {
+ if (!await NotificationProvider.ShowWarningQuestion("Reloading the data store will discard all the current changes. Are you sure?", "RELOAD"))
+ {
+ return;
+ }
+ }
+ }
+
+ await LoadDataStore();
+ }
+
+ private async void EditItem(DataStoreItemModel item)
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreWrite))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow editing of the data store.\nPlease contact your administrator.");
+ return;
+ }
+
+ var vm = await NotificationProvider.ShowDialog<DataStoreItemEditDialogViewVM>(new DataStoreItemEditDialogViewVM() { Item = item, EnableTypeChange = !item.IsGlobal && item.GlobalItem == null });
+
+ if (vm.DialogResult)
+ {
+ item.IsDeleted = false;
+ item.IsGlobal = false;
+ item.Type = vm.Type;
+ item.Value = vm.Value;
+ }
+ }
+
+ private async void AddCollection()
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreCreate))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow creating new data store collections.\nPlease contact your administrator.");
+ return;
+ }
+
+ var result = await NotificationProvider.ShowInputBox("Add Collection", "Please enter the collection name", MaterialDesignThemes.Wpf.PackIconKind.Collection, null, null, 20, null, null, (input) =>
+ {
+ if (DataStore.Collections.ToList().Exists(x => x.Name.ToLower() == input.ToLower()))
+ {
+ return "The specified collection already exists.";
+ }
+
+ if (!DataStoreHelper.ValidateCollectionOrKeyName(input))
+ {
+ return "Collection name contains invalid characters.";
+ }
+
+ return null;
+ });
+
+ if (result.Confirmed)
+ {
+ if (DataStore.Collections.ToList().Exists(x => x.Name.ToLower() == result.Input.ToLower()))
+ {
+ await NotificationProvider.ShowError("The specified collection already exists.");
+ AddCollection();
+ return;
+ }
+
+ DataStore.Collections.Add(new DataStoreCollectionModel()
+ {
+ Name = result.Input
+ });
+
+ SelectedCollection = DataStore.Collections.Last();
+ }
+ }
+
+ private async void AddItem()
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreCreate))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow creating new data store items.\nPlease contact your administrator.");
+ return;
+ }
+
+ var result = await NotificationProvider.ShowInputBox("Add Item", "Please enter the item key", MaterialDesignThemes.Wpf.PackIconKind.KeyAdd, null, null, 20, null, null, (input) =>
+ {
+ if (SelectedCollection.Items.ToList().Exists(x => x.Key.ToLower() == input.ToLower()))
+ {
+ return "The specified key already exists in the collection.";
+ }
+
+ if (!DataStoreHelper.ValidateCollectionOrKeyName(input))
+ {
+ return "Key name contains invalid characters.";
+ }
+
+ return null;
+ });
+
+ if (result.Confirmed)
+ {
+ if (SelectedCollection.Items.ToList().Exists(x => x.Key == result.Input))
+ {
+ await NotificationProvider.ShowError("The specified key already exists in the collection.");
+ AddItem();
+ return;
+ }
+
+ SelectedCollection.Items.Add(new DataStoreItemModel()
+ {
+ Date = DateTime.UtcNow,
+ Guid = Guid.NewGuid().ToString(),
+ Key = result.Input,
+ Type = DataType.Int32,
+ Value = 10
+ });
+ }
+ }
+
+ private async void RemoveCollection(DataStoreCollectionModel collection)
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreCreate))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow deleting data store collections.\nPlease contact your administrator.");
+ return;
+ }
+
+ collection.IsDeleted = true;
+ _collectionsView?.Refresh();
+ }
+
+ private async void RemoveItem(DataStoreItemModel item)
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreCreate))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow deleting data store items.\nPlease contact your administrator.");
+ return;
+ }
+
+ item.IsDeleted = true;
+ _selectedCollectionView.Refresh();
+ }
+
+ private void OnEditingCompositionLoaded(EditingCompositionLoadedMessage obj)
+ {
+ DataStore = null;
+ _editingComposition = obj.EditingComposition;
+ _machineGuid = obj.EditingComposition.Machine.Guid;
+ }
+
+ private void OnSelectedCollectionChanged()
+ {
+ if (SelectedCollection != null)
+ {
+ _selectedCollectionView = CollectionViewSource.GetDefaultView(SelectedCollection.Items);
+ _selectedCollectionView.Filter = (x) => FilterItems(x as DataStoreItemModel);
+ _selectedCollectionView.Refresh();
+ }
+ }
+
+ private bool FilterItems(DataStoreItemModel itemModel)
+ {
+ return (!itemModel.IsDeleted || itemModel.GlobalItem != null) && (String.IsNullOrWhiteSpace(Filter) || itemModel.Key.ToLower().Contains(Filter.ToLower()));
+ }
+
+ private async void SaveModel()
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreWrite))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow editing of the data store.\nPlease contact your administrator.");
+ return;
+ }
+
+ try
+ {
+ using (NotificationProvider.PushTaskItem("Updating data store..."))
+ {
+ await DataStoreProvider.UpdateDataStoreModel(DataStore, _machineGuid);
+ }
+
+ if (MachineProvider.IsPPCAvailable)
+ {
+ await NotificationProvider.ShowSuccess("The connected machine is now synchronized with the data store.");
+ }
+ else
+ {
+ await NotificationProvider.ShowSuccess("Data store saved successfully.\nThe machine will be updated on the next synchronization pass.");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error saving data store.");
+ await NotificationProvider.ShowError($"Error occurred while trying to save the data store.\n{ex.FlattenMessage()}");
+ return;
+ }
+
+ await LoadDataStore();
+ }
+
+ private async Task LoadDataStore()
+ {
+ try
+ {
+ using (NotificationProvider.PushTaskItem("Loading data store..."))
+ {
+ IsFree = false;
+ RaisePropertyChanged(nameof(CanSync));
+
+ var dataStore = await DataStoreProvider.GetDataStoreModel(_machineGuid);
+
+ _collectionsView = CollectionViewSource.GetDefaultView(dataStore.Collections);
+ _collectionsView.Filter = (x) => { return !(x as DataStoreCollectionModel).IsDeleted; };
+ _collectionsView.Refresh();
+
+ DataStore = dataStore;
+ SelectedCollection = DataStore.Collections.FirstOrDefault();
+
+ IsFree = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ DataStore = null;
+ LogManager.Log(ex, "Error loading data store.");
+ if (await NotificationProvider.ShowWarningQuestion($"Error occurred while trying to load the data store.\n{ex.FlattenMessage()}", "RETRY", "CANCEL"))
+ {
+ await LoadDataStore();
+ return;
+ }
+ }
+ }
+
+ public async override void OnNavigatedTo()
+ {
+ base.OnNavigatedTo();
+
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreRead))
+ {
+ return;
+ }
+
+ if (DataStore == null)
+ {
+ await LoadDataStore();
+ }
+ }
+
+ private async void ImportDataStore()
+ {
+ var result = await StorageProvider.OpenFile("Import data store", "CSV Files|*.csv");
+
+ if (result)
+ {
+ try
+ {
+ DataStoreModel model = new DataStoreModel();
+
+ using (NotificationProvider.PushTaskItem("Analyzing file..."))
+ {
+ await Task.Delay(1000);
+
+ await Task.Factory.StartNew(() =>
+ {
+ var items = CsvFile.Read<CsvModel>(new CsvSource(result.SelectedItem)).ToList();
+
+ foreach (var collection in items.GroupBy(x => x.Collection))
+ {
+ DataStoreCollectionModel collectionModel = new DataStoreCollectionModel();
+
+ collectionModel = new DataStoreCollectionModel();
+ collectionModel.Name = collection.First().Collection;
+ collectionModel.IsSelected = true;
+ model.Collections.Add(collectionModel);
+
+ foreach (var item in collection)
+ {
+ DataStoreItemModel itemModel = new DataStoreItemModel();
+ itemModel.Guid = Guid.NewGuid().ToString();
+ itemModel.Key = item.Key;
+ itemModel.Date = DateTime.Parse(item.Date);
+ itemModel.IsSelected = true;
+
+ DataType type = (DataType)Enum.Parse(typeof(DataType), item.Type);
+ itemModel.OriginalType = type;
+ itemModel.Type = type;
+
+ PMR.Common.MessageType? messageType = null;
+ if (type == DataType.Proto && item.MessageType.IsNotNullOrEmpty())
+ {
+ messageType = (PMR.Common.MessageType)Enum.Parse(typeof(PMR.Common.MessageType), item.MessageType);
+ }
+
+ itemModel.OriginalValue = DataStoreHelper.ParseDataStoreValue(type, item.Value, messageType);
+ itemModel.Value = itemModel.OriginalValue;
+
+ collectionModel.Items.Add(itemModel);
+ }
+ }
+ });
+ }
+
+ var vm = await NotificationProvider.ShowDialog<ImportDialogViewVM>(new ImportDialogViewVM() { DataStore = model });
+
+ if (vm.DialogResult)
+ {
+ if (!vm.DataStore.Collections.SelectMany(x => x.Items).Any(x => x.IsSelected))
+ {
+ await NotificationProvider.ShowInfo("No items selected to import.");
+ return;
+ }
+
+ foreach (var collection in vm.DataStore.Collections.Where(x => x.Items.Any(y => y.IsSelected)))
+ {
+ var collectionModel = DataStore.Collections.FirstOrDefault(x => x.Name == collection.Name);
+
+ if (collectionModel == null)
+ {
+ DataStore.Collections.Add(collection);
+ }
+ else
+ {
+ foreach (var item in collection.Items.Where(x => x.IsSelected))
+ {
+ var itemModel = collectionModel.Items.FirstOrDefault(x => x.Key == item.Key);
+
+ if (itemModel == null)
+ {
+ itemModel = new DataStoreItemModel();
+ itemModel.Guid = item.Guid;
+ itemModel.Key = item.Key;
+ collectionModel.Items.Add(itemModel);
+ }
+
+ itemModel.Date = DateTime.UtcNow;
+ itemModel.IsDeleted = false;
+ itemModel.IsGlobal = false;
+ itemModel.Type = item.Type;
+ itemModel.Value = item.Value;
+ }
+ }
+ }
+
+ await NotificationProvider.ShowSuccess("Data store imported successfully.");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error importing data store.");
+ await NotificationProvider.ShowError($"Error importing the data store.\n{ex.FlattenMessage()}");
+ }
+ }
+ }
+
+ private async void ExportDataStore()
+ {
+ var vm = await NotificationProvider.ShowDialog<ExportDialogViewVM>(new ExportDialogViewVM() { DataStore = DataStore });
+
+ if (vm.DialogResult)
+ {
+ if (!vm.DataStore.Collections.SelectMany(x => x.Items).Any(x => x.IsSelected))
+ {
+ await NotificationProvider.ShowInfo("No items selected to export.");
+ return;
+ }
+
+ var result = await StorageProvider.SaveFile("Export data store", "CSV Files|*.csv", $"{_editingComposition.Machine.SerialNumber}_datastore_{DateTime.Now.ToFileName()}.csv", ".csv");
+
+ if (result)
+ {
+ try
+ {
+ using (NotificationProvider.PushTaskItem("Exporting data store..."))
+ {
+ await Task.Delay(1000);
+
+ await Task.Factory.StartNew(() =>
+ {
+ using (DynamicCsvFile csvFile = new DynamicCsvFile(result.SelectedItem))
+ {
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "Date" });
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "Collection" });
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "Key" });
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "Type" });
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "MessageType" });
+ csvFile.Columns.Add(new DynamicCsvFileColumn() { Name = "Value" });
+
+ foreach (var collection in DataStore.Collections)
+ {
+ foreach (var item in collection.Items.Where(x => x.IsSelected && !x.IsGlobal && x.Value != null))
+ {
+ PMR.Common.MessageType? messageType = null;
+
+ if (item.Type == DataType.Proto)
+ {
+ messageType = (item.Value as DataStoreProtoObject).MessageType;
+ }
+
+ csvFile.Append(item.Date, $"\"{collection.Name}\"", $"\"{item.Key}\"", item.Type, messageType.ToStringSafe(), $"\"{item.FormattedValue.Replace("\"", "\"\"")}\"");
+ }
+ }
+ }
+ });
+ }
+
+ NotificationProvider.PushSnackbarItem(MessageType.Success, "Export Data Store", true, "Data store exported successfully.", TimeSpan.FromSeconds(8), null, () =>
+ {
+ StorageProvider.ShowInExplorer(result.SelectedItem);
+ });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error exporting data store.");
+ await NotificationProvider.ShowError($"Error exporting the data store.\n{ex.FlattenMessage()}");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MachineViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MachineViewVM.cs
new file mode 100644
index 000000000..fd953a0b8
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MachineViewVM.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.Entities;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Navigation;
+using Tango.FSE.MachineConfiguration.Messages;
+using static Tango.FSE.BL.Services.MachineConfigurationService;
+using static Tango.FSE.MachineConfiguration.ViewModels.MachineViewVM;
+
+namespace Tango.FSE.MachineConfiguration.ViewModels
+{
+ public class MachineViewVM : ConfigurationViewModel, INavigationObjectReceiver<NavigationObject>
+ {
+ public class NavigationObject
+ {
+ public String MachineGuid { get; set; }
+ }
+
+ public enum NavigationView
+ {
+ SettingsView,
+ DataStoreView,
+ }
+
+ private NavigationView _selectedView;
+ public NavigationView SelectedView
+ {
+ get { return _selectedView; }
+ set
+ {
+ if (value == NavigationView.DataStoreView)
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.DataStoreRead))
+ {
+ Task.Delay(500).ContinueWith((x) =>
+ {
+ SelectedView = _selectedView;
+ NotificationProvider.ShowError("The current user profile does not allow accessing the data store.\nPlease contact your administrator.");
+ });
+ return;
+ }
+ }
+
+ _selectedView = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private MachineEditingComposition _editingComposition;
+ public MachineEditingComposition EditingComposition
+ {
+ get { return _editingComposition; }
+ set { _editingComposition = value; RaisePropertyChangedAuto(); }
+ }
+
+ public void OnNavigatedToWithObject(NavigationObject obj)
+ {
+ SelectedView = NavigationView.SettingsView;
+ LoadMachine(obj.MachineGuid);
+ }
+
+ private async void LoadMachine(String machineGuid)
+ {
+ using (NotificationProvider.PushTaskItem("Loading machine configuration..."))
+ {
+ EditingComposition = await Services.MachineConfigurationService.GetMachineEditingComposition(machineGuid);
+ RaiseMessage(new EditingCompositionLoadedMessage() { EditingComposition = EditingComposition });
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MainViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MainViewVM.cs
new file mode 100644
index 000000000..250dc1046
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/MainViewVM.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Navigation;
+using Tango.FSE.MachineConfiguration.Navigation;
+using Tango.SharedUI.Helpers;
+
+namespace Tango.FSE.MachineConfiguration.ViewModels
+{
+ [ModularNavigationContainer]
+ public class MainViewVM : ConfigurationViewModel
+ {
+ public override void OnApplicationReady()
+ {
+ if (CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.FSE_RunConfigurationModule))
+ {
+ InvokeUI(() =>
+ {
+ NavigationManager.MenuItems.Add(new NavigationMenuItem(() =>
+ {
+ NavigationManager.NavigateTo<ConfigurationModule>();
+ })
+ {
+ Name = "Configuration",
+ Index = 8,
+ Description = "Access to the organization machines configuration and settings",
+ Image = ResourceHelper.GetImageFromResources("Images/configuration.png"),
+ });
+ });
+ }
+ }
+
+ public async override Task<bool> OnApplicationLogout()
+ {
+ while (ModularNavigationManager.CurrentView != ConfigurationView.SelectionView)
+ {
+ if (!await ModularNavigationManager.NavigateBack())
+ {
+ return false;
+ }
+ }
+
+ return await base.OnApplicationLogout();
+ }
+
+ public async override Task<bool> OnNavigateBackRequest()
+ {
+ if (ModularNavigationManager.CurrentView == ConfigurationView.SelectionView)
+ {
+ return await base.OnNavigateBackRequest();
+ }
+ else
+ {
+ await ModularNavigationManager.NavigateBack();
+ return false;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SelectionViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SelectionViewVM.cs
new file mode 100644
index 000000000..39772a4cf
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SelectionViewVM.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.Entities;
+using Tango.Core.Commands;
+using Tango.FSE.Common;
+using Tango.FSE.MachineConfiguration.Navigation;
+
+namespace Tango.FSE.MachineConfiguration.ViewModels
+{
+ public class SelectionViewVM : ConfigurationViewModel
+ {
+ private Machine _selectedMachine;
+ /// <summary>
+ /// Gets or sets the selected machine.
+ /// </summary>
+ public Machine SelectedMachine
+ {
+ get { return _selectedMachine; }
+ set
+ {
+ _selectedMachine = value;
+ RaisePropertyChangedAuto();
+ InvalidateRelayCommands();
+ OnSelectedMachineChanged();
+ }
+ }
+
+ public RelayCommand ManageMachineCommand { get; set; }
+
+ public SelectionViewVM()
+ {
+ ManageMachineCommand = new RelayCommand(ManageSelectedMachine, () => SelectedMachine != null);
+ }
+
+ private async void OnSelectedMachineChanged()
+ {
+ if (SelectedMachine != null)
+ {
+ await Task.Delay(100);
+ this.SetFocus(() => ManageMachineCommand);
+ }
+ }
+
+ private void ManageSelectedMachine()
+ {
+ if (SelectedMachine == null)
+ {
+ NotificationProvider.ShowError("No machine selected.");
+ return;
+ }
+
+ ModularNavigationManager.NavigateTo(ConfigurationView.MachineView, new MachineViewVM.NavigationObject()
+ {
+ MachineGuid = SelectedMachine.Guid
+ });
+ }
+
+ public override void OnNavigatedTo()
+ {
+ base.OnNavigatedTo();
+
+ if (SelectedMachine == null && MachineProvider.Machine != null)
+ {
+ SelectedMachine = MachineProvider.Machine;
+ }
+ }
+
+ public override Task<bool> OnApplicationLogout()
+ {
+ ModularNavigationManager.NavigateTo(ConfigurationView.SelectionView);
+ SelectedMachine = null;
+ return base.OnApplicationLogout();
+ }
+ }
+}
+
+
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SettingsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SettingsViewVM.cs
new file mode 100644
index 000000000..ed2cbd714
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/ViewModels/SettingsViewVM.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.Entities;
+using Tango.Core.Commands;
+using Tango.Core.DI;
+using Tango.FSE.BL;
+using Tango.FSE.Common;
+using Tango.FSE.Common.AutoComplete;
+using Tango.FSE.Common.SQL;
+using Tango.FSE.MachineConfiguration.Messages;
+using static Tango.FSE.BL.Services.MachineConfigurationService;
+
+namespace Tango.FSE.MachineConfiguration.ViewModels
+{
+ public class SettingsViewVM : FSEViewModel
+ {
+ [TangoInject]
+ private IRemoteSqlProvider RemoteSqlProvider { get; set; }
+
+ private MachineEditingComposition _editingComposition;
+ public MachineEditingComposition EditingComposition
+ {
+ get { return _editingComposition; }
+ set { _editingComposition = value; RaisePropertyChangedAuto(); }
+ }
+
+ public RelayCommand SaveCommand { get; set; }
+ public RelayCommand ResetCountersCommand { get; set; }
+ public RelayCommand ResetDeviceRegistrationCommand { get; set; }
+
+ private List<Site> _sites;
+ public List<Site> Sites
+ {
+ get { return _sites; }
+ set { _sites = value; RaisePropertyChangedAuto(); }
+ }
+
+ private Site _selectedSite;
+ public Site SelectedSite
+ {
+ get { return _selectedSite; }
+ set { _selectedSite = value; RaisePropertyChangedAuto(); }
+ }
+
+ public SettingsViewVM()
+ {
+ RegisterForMessage<EditingCompositionLoadedMessage>(OnEditingCompositionLoaded);
+ SaveCommand = new RelayCommand(SaveConfiguration);
+ ResetCountersCommand = new RelayCommand(ResetCounters);
+ ResetDeviceRegistrationCommand = new RelayCommand(ResetDeviceRegistration);
+ }
+
+ private void OnEditingCompositionLoaded(EditingCompositionLoadedMessage msg)
+ {
+ using (NotificationProvider.PushTaskItem("Loading machine configuration..."))
+ {
+ EditingComposition = msg.EditingComposition;
+ EditingComposition.Machine.ForceVersionUpdateChanged += (x, value) => { if (value) EditingComposition.Machine.SuspendVersionUpdate = false; };
+ EditingComposition.Machine.SuspendVersionUpdateChanged += (x, value) => { if (value) EditingComposition.Machine.ForceVersionUpdate = false; };
+ EditingComposition.Machine.OrganizationChanged -= OnMachine_OrganizationChanged;
+ EditingComposition.Machine.OrganizationChanged += OnMachine_OrganizationChanged;
+ OnMachine_OrganizationChanged(EditingComposition.Machine, EditingComposition.Machine.Organization);
+ }
+ }
+
+ private void OnMachine_OrganizationChanged(object sender, Organization organization)
+ {
+ var sites = organization.Sites.ToList();
+ sites.Insert(0, new Site() { Guid = null, Name = "NONE", ID = -1 });
+ Sites = sites;
+ SelectedSite = Sites.FirstOrDefault(x => x.Guid == EditingComposition.Machine.SiteGuid);
+
+ if (SelectedSite == null)
+ {
+ SelectedSite = Sites.First();
+ }
+ }
+
+ private async void SaveConfiguration()
+ {
+ using (NotificationProvider.PushTaskItem("Saving machine configuration..."))
+ {
+ EditingComposition.Machine.SiteGuid = SelectedSite.Guid;
+ EditingComposition = await Services.MachineConfigurationService.SaveMachineEditingComposition(EditingComposition);
+ await NotificationProvider.ShowSuccess("Machine configuration saved successfully.");
+ }
+ }
+
+ private async void ResetCounters()
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.FSE_ResetMachineCounters))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow resetting the machine counters.\nPlease contact your administrator.");
+ return;
+ }
+
+ if (!MachineProvider.IsPPCAvailable)
+ {
+ await NotificationProvider.ShowError("Resetting the machine counters requires an active connection to this machine.");
+ return;
+ }
+
+ if (!await NotificationProvider.ShowWarningQuestion("Resetting the machine counters will delete the entire job runs history. Are you sure?", "RESET COUNTERS", "CANCEL")) return;
+
+ using (var task = NotificationProvider.PushTaskItem("Resetting machine counters..."))
+ {
+ task.UpdateProgress("Resetting local machine counters...");
+
+ var result = await RemoteSqlProvider.ExecuteSqlCommandAsync(new RemoteSqlCommand()
+ {
+ Mode = RemoteSqlCommandMode.Local,
+ Timeout = 30,
+ SQL = "DELETE FROM JOB_RUNS"
+ });
+
+ int localJobRuns = result.LocalAffectedRecords;
+
+ task.UpdateProgress("Resetting global machine counters...");
+
+ int remoteJobRuns = await Services.MachineConfigurationService.ResetCounters(EditingComposition.Machine.Guid);
+
+ await NotificationProvider.ShowSuccess("Machine counters deleted successfully.");
+ }
+ }
+
+ private async void ResetDeviceRegistration()
+ {
+ if (!CurrentUser.HasPermission(Tango.BL.Enumerations.Permissions.FSE_ResetMachineDeviceRegistration))
+ {
+ await NotificationProvider.ShowError("The current user profile does not allow resetting the device registration.\nPlease contact your administrator.");
+ return;
+ }
+
+ if (!await NotificationProvider.ShowWarningQuestion("Resetting the machine's device registration will allow other panel PCs to perform a machine setup for this machine. Are you sure?", "RESET", "CANCEL")) return;
+
+ EditingComposition.Machine.IsDeviceRegistered = false;
+ EditingComposition.Machine.DeviceId = null;
+ EditingComposition.Machine.DeviceName = null;
+ }
+ }
+}