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 Tango.PMR.DataStore; 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 RemoveItemCommand { get; set; } public RelayCommand RemoveCollectionCommand { get; set; } public RelayCommand AddItemCommand { get; set; } public RelayCommand AddCollectionCommand { get; set; } public RelayCommand 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(OnEditingCompositionLoaded); RemoveItemCommand = new RelayCommand(RemoveItem); RemoveCollectionCommand = new RelayCommand(RemoveCollection); AddItemCommand = new RelayCommand(AddItem); AddCollectionCommand = new RelayCommand(AddCollection); EditItemCommand = new RelayCommand(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(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 = Tango.DataStore.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.SortDescriptions.Add(new SortDescription(nameof(DataStoreItemModel.Key), ListSortDirection.Ascending)); _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; } var selectedCollection = SelectedCollection; var selectedItem = SelectedItem; await LoadDataStore(); if (selectedCollection != null) { SelectedCollection = DataStore.Collections.FirstOrDefault(x => x.Name == selectedCollection.Name); } if (SelectedCollection != null && selectedItem != null) { SelectedItem = SelectedCollection.Items.FirstOrDefault(x => x.Key == selectedItem.Key); } } 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) { IsFree = true; 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(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; DataStore.DataType type = (DataStore.DataType)Enum.Parse(typeof(DataStore.DataType), item.Type); itemModel.OriginalType = type; itemModel.Type = type; DataStoreMessageType? messageType = null; if (type == Tango.DataStore.DataType.Proto && item.MessageType.IsNotNullOrEmpty()) { messageType = (DataStoreMessageType)Enum.Parse(typeof(DataStoreMessageType), item.MessageType); } itemModel.OriginalValue = DataStoreHelper.ParseDataStoreValue(type, item.Value, messageType); itemModel.Value = itemModel.OriginalValue; collectionModel.Items.Add(itemModel); } } }); } var vm = await NotificationProvider.ShowDialog(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(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)) { DataStoreMessageType? messageType = null; if (item.Type == Tango.DataStore.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()}"); } } } } } }