diff --git a/TremAn3.Core/Coherence.cs b/TremAn3.Core/Coherence.cs index 7c3c83b..e18bf98 100644 --- a/TremAn3.Core/Coherence.cs +++ b/TremAn3.Core/Coherence.cs @@ -22,8 +22,8 @@ public Coherence(double frameRate, List> comXAllRois, List> ComXAllRois { get; set; } = new List>(); - private List> ComYAllRois { get; set; } = new List>(); + private List> ComXAllRois { get; set; } + private List> ComYAllRois { get; set; } public DataResult Compute() { diff --git a/TremAn3.Core/MeasurementModel.cs b/TremAn3.Core/MeasurementModel.cs index 0747033..74be282 100644 --- a/TremAn3.Core/MeasurementModel.cs +++ b/TremAn3.Core/MeasurementModel.cs @@ -30,6 +30,8 @@ public MeasurementModel(IEnumerable comAlgs) public VectorsDataModel VectorsDataModel { get; set; } + [JsonIgnore] + public AdditionalResultsModel AdditionalResultsModel { get; set; } = new AdditionalResultsModel(); public double FrameRate { get; set; } public double Maxrange { get; set; } @@ -38,11 +40,15 @@ public MeasurementModel(IEnumerable comAlgs) public int FreqProgressSegmnetSize { get; set; } public int FreqProgressStep { get; set; } - public void GetResults(IEnumerable algs, Dictionary dataResultsDict) + public void GetResults(IEnumerable algs, Dictionary dataResultsDict, AdditionalResultsModel additionalResultsModel) { VectorsDataModel = new VectorsDataModel(algs,dataResultsDict); + AdditionalResultsModel = AdditionalResultsModel; } + + + //public int FftSegmentSize { get; set; } } @@ -131,4 +137,22 @@ public class ResultsModel public List ComY { get; private set; } = new List(); } + + + public class AdditionalResultsModel + { + + public List CoherenceAverageResults { get; set; } = new List(); + + } + + public class CoherenceAverageResultModel + { + public double MinHz { get; set; } + public double MaxHz { get; set; } + + public double Average { get; set; } + + + } } diff --git a/TremAn3/Helpers/StaticConverters.cs b/TremAn3/Helpers/StaticConverters.cs index ead41ba..03e303a 100644 --- a/TremAn3/Helpers/StaticConverters.cs +++ b/TremAn3/Helpers/StaticConverters.cs @@ -66,6 +66,10 @@ public static double SpectralAnalysisBiggerToggleToSizeOfPlot(bool? IsChecked) public static string DoubleFormatter(double val) => $"{val:0.00}"; + public static string DoubleToStringHz(double val) => $"{val:00.000} Hz "; + public static string DoubleToString3Decimals(double val) => $"{val:00.000}"; + + diff --git a/TremAn3/Services/DataService.cs b/TremAn3/Services/DataService.cs index 0babdf5..2be655d 100644 --- a/TremAn3/Services/DataService.cs +++ b/TremAn3/Services/DataService.cs @@ -90,22 +90,22 @@ internal async Task GetFileByFalToken(string falToken) return (mruToken, falToken); } - + internal async Task SaveMeasurementResults(MeasurementModel measurementModel, VideoFileModel vfm) { //resultsViewModel.Id = resultsViewModel.Id == Guid.Empty ? Guid.NewGuid(): resultsViewModel.Id; var allMeasurementsFolder = await GetFolder_AllMeasurements(); StorageFolder measurementsFolderForVideo = await GetFolderForVideo(allMeasurementsFolder, vfm); - string measurementFolderAndFIleName = $"m_{DateTime.Now:yyyy-MM-dd_HH-mm-ss.ff}_{measurementModel.Id.ToString().Substring(0,8)}"; + string measurementFolderAndFIleName = $"m_{DateTime.Now:yyyy-MM-dd_HH-mm-ss.ff}_{measurementModel.Id.ToString().Substring(0, 8)}"; StorageFolder folderForMeasurement = await measurementsFolderForVideo.CreateFolderAsync(measurementFolderAndFIleName, CreationCollisionOption.OpenIfExists); await SaveMeasurementResults(measurementModel, folderForMeasurement);//csvs will have similar structure of filename - return folderForMeasurement; + return folderForMeasurement; } - + /// @@ -114,21 +114,31 @@ internal async Task SaveMeasurementResults(MeasurementModel measu /// /// /// - internal static async Task SaveMeasurementResults(MeasurementModel measurementModel, StorageFolder folderForMeasurement,bool saveOnlyTopLevelData = false) + internal static async Task SaveMeasurementResults(MeasurementModel measurementModel, StorageFolder folderForMeasurement, bool saveOnlyTopLevelData = false) { await JsonServices.WriteToJsonFile(folderForMeasurement, GetMeasurementFileName(folderForMeasurement), measurementModel);//saves toplevel //saves vector data to special folder - if(!saveOnlyTopLevelData) - await JsonServices.WriteToJsonFile(folderForMeasurement, GetMeasurementVectorDataFileName(folderForMeasurement), measurementModel.VectorsDataModel); + if (!saveOnlyTopLevelData) + { + await JsonServices.WriteToJsonFile(folderForMeasurement, GetMeasurementVectorDataFileName(folderForMeasurement), measurementModel.VectorsDataModel); + await SaveAdditionalResults( measurementModel.AdditionalResultsModel, folderForMeasurement); + } } - private static string GetMeasurementFileName (StorageFolder continerFolder) => $"{continerFolder.Name}.json"; - private static string GetVideoModelFileName (StorageFolder videoFolder) => $"{videoFolder.Name}.json"; - private static string GetMeasurementVectorDataFileName (StorageFolder continerFolder) => $"{continerFolder.Name}_vectorData.json"; + public static async Task SaveAdditionalResults( AdditionalResultsModel additionalResultsModel,StorageFolder folderForMeasurement) + { + await JsonServices.WriteToJsonFile(folderForMeasurement, GetMeasurementAdditionalResultsFileName(folderForMeasurement), additionalResultsModel); + } + + private static string GetMeasurementFileName(StorageFolder continerFolder) => $"{continerFolder.Name}.json"; + private static string GetVideoModelFileName(StorageFolder videoFolder) => $"{videoFolder.Name}.json"; + private static string GetMeasurementVectorDataFileName(StorageFolder continerFolder) => $"{continerFolder.Name}_vectorData.json"; + private static string GetMeasurementAdditionalResultsFileName(StorageFolder continerFolder) => $"{continerFolder.Name}_additionalResults.json"; + internal async Task DeleteAllMeasurements() { - await (await GetFolder_AllMeasurements()).DeleteAsync(); + await (await GetFolder_AllMeasurements()).DeleteAsync(); } @@ -149,6 +159,17 @@ internal async Task LoadVectorDataToModel(MeasurementModel model, StorageFolder model.VectorsDataModel = vectorsDataModels; } + + internal async Task LoadAdditionalResultsToModel(MeasurementModel model, StorageFolder folderForMeasurement) + { + StorageFile jsonFile; + jsonFile = await folderForMeasurement.GetFileAsync(GetMeasurementAdditionalResultsFileName(folderForMeasurement)); + if (jsonFile is null) throw new FileNotFoundException($"No vectorData file ({jsonFile}) in {folderForMeasurement.Name} folder"); + var vectorsDataModels = await JsonServices.ReadFromJsonFile(jsonFile); + model.AdditionalResultsModel = vectorsDataModels?? new(); + } + + private StorageFolder _measurementsFolderForVideo; //public async Task> GetPastMeasurementsForVideo(StorageFile currentStorageFile, VideoFileModel videoFileModel) //{ @@ -173,7 +194,7 @@ public async Task> GetAllPastMeasurements() { var measurementsViewModels = new List(); var allMeasurementsFolder = await GetFolder_AllMeasurements(); - var videoFolders =await allMeasurementsFolder.GetFoldersAsync(); + var videoFolders = await allMeasurementsFolder.GetFoldersAsync(); foreach (var videoFolder in videoFolders) { @@ -187,7 +208,7 @@ public async Task> GetAllPastMeasurements() var jsonFile = await mesFolder.GetFileAsync(GetMeasurementFileName(mesFolder)); if (jsonFile is null) continue; MeasurementModel measModel = await JsonServices.ReadFromJsonFile(jsonFile); - MeasurementViewModel vm = new MeasurementViewModel(measModel,vfm); + MeasurementViewModel vm = new MeasurementViewModel(measModel, vfm); measurementsViewModels.Add(vm); vm.FolderForMeasurement = mesFolder; } @@ -206,7 +227,7 @@ public async Task> GetAllPastMeasurements() /// private async Task GetFolderForVideo(StorageFolder measurementsFolder, VideoFileModel videoFileModel) { - string videoFolderName = $"v_{GetPathToSourceForFileName(videoFileModel.Name)}_{videoFileModel.FalToken.ToString().Trim(new char[] {'{','}' })}"; + string videoFolderName = $"v_{GetPathToSourceForFileName(videoFileModel.Name)}_{videoFileModel.FalToken.ToString().Trim(new char[] { '{', '}' })}"; StorageFolder videoFolder = await measurementsFolder.CreateFolderAsync(videoFolderName, CreationCollisionOption.OpenIfExists); //when getting folder, also check for video file. This file is used for loading basic video properties @@ -214,7 +235,7 @@ private async Task GetFolderForVideo(StorageFolder measurementsFo var videoFile = await videoFolder.TryGetItemAsync(GetVideoModelFileName(videoFolder)); if (videoFile is null)//do nothing when already created { - var vf = await videoFolder.CreateFileAsync(GetVideoModelFileName(videoFolder), CreationCollisionOption.FailIfExists); + var vf = await videoFolder.CreateFileAsync(GetVideoModelFileName(videoFolder), CreationCollisionOption.FailIfExists); await JsonServices.WriteToJsonFile(vf, videoFileModel); } @@ -249,7 +270,7 @@ public async Task DeleteStoredViewModel(MeasurementViewModel vm) await vm.FolderForMeasurement.DeleteAsync(); } - + } diff --git a/TremAn3/Services/ExportService.cs b/TremAn3/Services/ExportService.cs index 7aeb9b4..4f302e8 100644 --- a/TremAn3/Services/ExportService.cs +++ b/TremAn3/Services/ExportService.cs @@ -75,15 +75,28 @@ public async Task ExportFreqAnalysisToCsvAsync() headers.Add($"{dataSeriesType}__{roi}"); } } - var roi1Roi2Coherence = ViewModelLocator.Current.MainViewModel.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.DataResultsDict[DataSeriesType.Coherence]; + var currentGlobal = ViewModelLocator.Current.MainViewModel.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel; + var roi1Roi2Coherence = currentGlobal.DataResultsDict[DataSeriesType.Coherence]; if (roi1Roi2Coherence.IsOk) { values.Add(roi1Roi2Coherence.X); headers.Add("freq[Hz]_coherence_roi1_roi2"); + values.Add(roi1Roi2Coherence.Y); headers.Add("coherence_roi1_roi2"); - values.Add([ViewModelLocator.Current.MainViewModel.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.CoherenceAverage]); - headers.Add("coherence_roi1_roi2_average"); + + + values.Add(currentGlobal.CoherenceMeasurementResults.Select(x => x.MinHz).ToList()); + headers.Add("coherence_minFreq[Hz]"); + + values.Add(currentGlobal.CoherenceMeasurementResults.Select(x => x.MaxHz).ToList()); + headers.Add("coherence_maxFreq[Hz]"); + + values.Add(currentGlobal.CoherenceMeasurementResults.Select(x => x.Average).ToList()); + headers.Add("coherence_average"); + + // values.Add([ViewModelLocator.Current.MainViewModel.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.CoherenceAverage]); + // headers.Add("coherence_roi1_roi2_average"); } diff --git a/TremAn3/Services/StoringMeasurementsService.cs b/TremAn3/Services/StoringMeasurementsService.cs index 558b41f..6481d4f 100644 --- a/TremAn3/Services/StoringMeasurementsService.cs +++ b/TremAn3/Services/StoringMeasurementsService.cs @@ -19,6 +19,7 @@ public MeasurementsService(DataService ds) DataService _DataService; /// /// this is called when model is loaded from file and we want display rresults from model. + /// here the data gets from model to vm /// /// /// @@ -44,6 +45,7 @@ public async Task DisplayMeasurementByModelAsync(MeasurementModel measurementMod selvm.ComputationViewModel.Algorithm = new CenterOfMotionAlgorithm(roiRes, measurementModel.FrameRate); } ResultsViewModel rvm = new ResultsViewModel(); + rvm.AddedToCollection = mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.AddedToCollection; foreach (var gsdm in measurementModel.VectorsDataModel.GlobalScopedDataResultsModels) { @@ -53,11 +55,29 @@ public async Task DisplayMeasurementByModelAsync(MeasurementModel measurementMod X = gsdm.X, ErrorMessage = gsdm.ErrorMessage }; - rvm.DataResultsDict.Add(gsdm.DataSeriesType, dr); + rvm.DataResultsDict.Add(gsdm.DataSeriesType, dr); } + rvm.CoherenceMeasurementResults.Clear(); + foreach (var item in measurementModel.AdditionalResultsModel.CoherenceAverageResults) + rvm.CoherenceMeasurementResults.Add(new CoherenceMeasurementResults() { Average = item.Average, MaxHz = item.MaxHz, MinHz = item.MinHz }); + + + if (rvm.CoherenceMeasurementResults.Any()) + { + rvm.CoherenceMinHz = rvm.CoherenceMeasurementResults.Last().MinHz; + rvm.CoherenceMaxHz = rvm.CoherenceMeasurementResults.Last().MaxHz; + } + else + { + rvm.CoherenceMinHz = 0; + rvm.CoherenceMaxHz = mainVm.MediaPlayerViewModel.VideoPropsViewModel.FrameRate / 2; + } + mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel = rvm; - await mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.ComputeAdditionalResults(); + rvm.SetIsCoherenceOk(); + rvm.IsComputationPaused = false; + //await mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.ComputeAdditionalResults(); await mainVm.FreqCounterViewModel.DisplayPlots(false); @@ -69,17 +89,20 @@ public async Task DisplayMeasurementByModelAsync(MeasurementModel measurementMod /// /// /// - public async Task GetModelFromVmAndSaveItToFile(MeasurementViewModel vm) + public async Task GetModelFromVmAndSaveItToFile(MeasurementViewModel vm) { var mainVm = ViewModelLocator.Current.MainViewModel; var algs = ViewModelLocator.Current.DrawingRectanglesViewModel.SelectionRectanglesViewModels.Select(x => x.ComputationViewModel.Algorithm); - vm.Model.GetResults(algs,mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.DataResultsDict); + + + var currentGlobalResults = mainVm.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel; + vm.Model.GetResults(algs, currentGlobalResults.DataResultsDict, currentGlobalResults.GetAdditionalResultsModel()); vm.Model.FreqProgressSegmnetSize = mainVm.FreqCounterViewModel.FreqProgressViewModel.SegmnetSize; vm.Model.FreqProgressStep = mainVm.FreqCounterViewModel.FreqProgressViewModel.Step; if (vm.FolderForMeasurement == null) - vm.FolderForMeasurement = await _DataService.SaveMeasurementResults(vm.Model, mainVm.MediaPlayerViewModel.VideoFileModel); + vm.FolderForMeasurement = await _DataService.SaveMeasurementResults(vm.Model, mainVm.MediaPlayerViewModel.VideoFileModel); else - await DataService.SaveMeasurementResults(vm.Model, vm.FolderForMeasurement ); + await DataService.SaveMeasurementResults(vm.Model, vm.FolderForMeasurement); } diff --git a/TremAn3/ViewModels/FreqCounterViewModel.cs b/TremAn3/ViewModels/FreqCounterViewModel.cs index c0003b5..ab904ba 100644 --- a/TremAn3/ViewModels/FreqCounterViewModel.cs +++ b/TremAn3/ViewModels/FreqCounterViewModel.cs @@ -107,16 +107,16 @@ public async Task DisplayPlots(bool doComputation, DataSeriesType? dataSeriesTyp //this basically compute the psd, amps spec, etc... IEnumerable tasks = null; - if (dataSeriesType == null) //do it for all + if (dataSeriesType == null)//do it for all tasks = comps.Select(x => x.PrepareForDisplay(FreqProgressViewModel.Step, FreqProgressViewModel.SegmnetSize, doComputation)); else tasks = comps.Select(x => x.PrepareForDisplay((DataSeriesType)dataSeriesType, FreqProgressViewModel.Step, FreqProgressViewModel.SegmnetSize)); - if(doComputation) + if (doComputation) ViewModelLocator.Current.LoadingContentViewModel.Type = LoadingContentType.ComputingVectorData; await Task.WhenAll(tasks); timeAnotations.Clear();//discard current anotations (new will be added) var plotModels = dataSeriesType == null ? PlotModelsContainerViewModel.PlotModels : - PlotModelsContainerViewModel.PlotModels.Where(x => x.DataSeriesType == (DataSeriesType)dataSeriesType); + PlotModelsContainerViewModel.PlotModels.Where(x => x.DataSeriesType == (DataSeriesType)dataSeriesType); foreach (var pmwt in plotModels) { @@ -144,7 +144,7 @@ public async Task DisplayPlots(bool doComputation, DataSeriesType? dataSeriesTyp else pmwt.PlotModel = new PlotModel { Title = firstDatares.ErrorMessage }; //for com x and com y add anotation - if (pmwt.DataSeriesType == DataSeriesType.ComX || pmwt.DataSeriesType == DataSeriesType.ComY) + if (pmwt.DataSeriesType is DataSeriesType.ComX or DataSeriesType.ComY) { var newAno = RecreateAnnotation(); timeAnotations.Add(newAno); @@ -155,7 +155,7 @@ public async Task DisplayPlots(bool doComputation, DataSeriesType? dataSeriesTyp if (dataSeriesType == null)//we currently use dataseries type only for freq progress In case of using single dataseries type for scoped - //it has to be changed.. + //it has to be changed.. { //global scoped results if (doComputation) @@ -163,8 +163,8 @@ public async Task DisplayPlots(bool doComputation, DataSeriesType? dataSeriesTyp ViewModelLocator.Current.LoadingContentViewModel.Type = LoadingContentType.ComputingGlobalVectorData; await CurrentGlobalScopedResultsViewModel.ComputeAllResults(ParentVm.MediaPlayerViewModel.VideoPropsViewModel.FrameRate, - comps.Select(x => x.Algorithm.Results.DataResultsDict[DataSeriesType.ComX].Y).ToList(), - comps.Select(x => x.Algorithm.Results.DataResultsDict[DataSeriesType.ComY].Y).ToList()); + comps.Select(x => x.Algorithm.Results.DataResultsDict[DataSeriesType.ComX].Y).ToList(), + comps.Select(x => x.Algorithm.Results.DataResultsDict[DataSeriesType.ComY].Y).ToList()); } foreach (var pwmt in PlotModelsContainerViewModel.PlotModelsGlobalScope) @@ -184,6 +184,16 @@ await CurrentGlobalScopedResultsViewModel.ComputeAllResults(ParentVm.MediaPlayer { pwmt.PlotModel = new PlotModel { Title = res.ErrorMessage }; } + + if (pwmt.DataSeriesType != DataSeriesType.Coherence) + continue; + var xAxis = new LinearAxis + { + Position = AxisPosition.Bottom, + Minimum = CurrentGlobalScopedResultsViewModel.CoherenceMinHz, + Maximum = CurrentGlobalScopedResultsViewModel.CoherenceMaxHz, + }; + pwmt.PlotModel.Axes.Add(xAxis); } } @@ -194,54 +204,7 @@ await CurrentGlobalScopedResultsViewModel.ComputeAllResults(ParentVm.MediaPlayer } - /// - /// Freq progress has its own logic how to rewrite plots. It is mainly bcs of different segements sizes for fft. Some of - /// them does not produce result. And this method is called when segment size is changed - /// solo caller -> if only freq progress redraw is call, raise prop change, otherwise do it somewhere else - /// - //public void ReDrawFreqProgress(bool isSoloCaller = false) - //{ - // var freqProgressPlotModel = new PlotModel(); - // var comps = DrawingRectanglesViewModel.SelectionRectanglesViewModels.Select(x => x.ComputationViewModel).ToList(); - // if (comps.Count < 1) - // return; - // //double maxYOfFreqProgress = 0; - // foreach (var comp in comps) - // { - // try - // { - // //comp.PrepareForDisplayFreqProgress(FreqProgressViewModel.Step, FreqProgressViewModel.SegmnetSize); - // } - // catch (FftSettingsException e) - // {//when there is an error (i.e. window is longer than signal) - // FreqProgressViewModel.StatusMessage = e.Message; - // FreqProgressViewModel.IsFreqProgressParametersOk = false; - // PlotModelsContainerViewModel.SetFreqProgressToNoData();//display No data (also exception and err message is handled in prepare) - // if (isSoloCaller) - // RaisePropertyChanged(nameof(PlotModelsContainerViewModel)); - // return; - // } - - // freqProgressPlotModel.Series.Add(comp.LineSeriesDict[DataSeriesType.FreqProgress]); - // //get maximum to better view - // //maxYOfFreqProgress = maxYOfFreqProgress < comp.Algorithm.Results.FreqProgress.Max() ? comp.Algorithm.Results.FreqProgress.Max() : maxYOfFreqProgress; - // } - - - // FreqProgressViewModel.IsFreqProgressParametersOk = true; - // var freqProgressAnnotation = RecreateAnnotation(); - // timeAnotations.Add(freqProgressAnnotation); - // freqProgressPlotModel.Annotations.Add(freqProgressAnnotation); - // //10 is just subtitution for max - // var maxYOfFreqProgress = 10;// comps.Max(comp => comp.Algorithm.Results.FreqProgress.Max()); - - // freqProgressPlotModel.Axes.Add(new LinearAxis() { Maximum = maxYOfFreqProgress * 1.1, Minimum = 0, MajorTickSize = 2, MinorTickSize = 0.5, Position = AxisPosition.Left, Key = "Vertical" }); - // PlotModelsContainerViewModel.PlotModels.First(x => x.DataSeriesType == DataSeriesType.FreqProgress).PlotModel = freqProgressPlotModel; - // //PlotModelsContainerViewModel.PlotModels.First(x => x.DataSeriesType == DataSeriesType.FreqProgress).PlotModel.InvalidatePlot(true); - // if (isSoloCaller) - // RaisePropertyChanged(nameof(PlotModelsContainerViewModel)); - - //} + // List timeAnotations = new List(); @@ -253,8 +216,7 @@ await CurrentGlobalScopedResultsViewModel.ComputeAllResults(ParentVm.MediaPlayer public double Maximum { get => _maximum; - set - { + set { if (_maximum == value) return; _maximum = value; RaisePropertyChanged(); @@ -278,8 +240,7 @@ internal void ResetResultDisplay() public double Minrange { get => _minrange; - set - { + set { if (_minrange == value) return; if (Maxrange - value >= 1) @@ -299,8 +260,7 @@ public double Minrange public double Maxrange { get => _maxrange; - set - { + set { if (_maxrange == value) return; if (value - Minrange >= 1) @@ -322,8 +282,7 @@ internal void IsRoiSameAsResultSomeChange(bool iscallingResultNotObsolete) ResetResultDisplay(); } } - else - if (0 == DrawingRectanglesViewModel.SelectionRectanglesViewModels.Select(x => x.ComputationViewModel).ToList().Where(x => x.IsRoiSameAsResult == false).Count()) + else if (0 == DrawingRectanglesViewModel.SelectionRectanglesViewModels.Select(x => x.ComputationViewModel).ToList().Where(x => x.IsRoiSameAsResult == false).Count()) IsAllResultsNotObsolete = true;//all results are same as roi } @@ -348,8 +307,7 @@ public bool IsAllResultsNotObsolete public bool IsComputationInProgress { get => _IsComputationInProgress; - set - { + set { if (Set(ref _IsComputationInProgress, value)) if (value) ViewModelLocator.Current.LoadingContentViewModel.Type = LoadingContentType.ComputationInProgress; @@ -372,8 +330,7 @@ public double PercentageOfResolution public double SliderPlotValue { get => _SliderPlotValue; - set - { + set { if (Set(ref _SliderPlotValue, value) && !MediaControllingViewModel.IsPositionChangeFromMethod && !isRangeInSettingProcess) @@ -383,6 +340,11 @@ public double SliderPlotValue private MediaControllingViewModel MediaControllingViewModel { get => ViewModelLocator.Current.MediaControllingViewModel; } + + + + + public async Task DisplaySpectralAnalysisInfo() { diff --git a/TremAn3/ViewModels/MainViewModel.cs b/TremAn3/ViewModels/MainViewModel.cs index f75f5a2..813f481 100644 --- a/TremAn3/ViewModels/MainViewModel.cs +++ b/TremAn3/ViewModels/MainViewModel.cs @@ -90,6 +90,7 @@ private async Task OpenStorageFile(StorageFile file, bool isMeasurementAlreadyDi { try { + await ViewModelLocator.Current.MainViewModel.PastMeasurementsViewModel.SelectedMeasurementVmSet(null); if (file == null) return; await MediaPlayerViewModel.ChangeSourceAsync(file); if (!isMeasurementAlreadyDisplayed) diff --git a/TremAn3/ViewModels/PastMeasurementsViewModel.cs b/TremAn3/ViewModels/PastMeasurementsViewModel.cs index 8d83807..a604485 100644 --- a/TremAn3/ViewModels/PastMeasurementsViewModel.cs +++ b/TremAn3/ViewModels/PastMeasurementsViewModel.cs @@ -91,13 +91,19 @@ public async Task SelectedMeasurementVmSet(MeasurementViewModel value, bool isBa - if (value == null) return; + if (value == null) + { + ViewModelLocator.Current.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.ResetResults(); + return; + } ViewModelLocator.Current.LoadingContentViewModel.Type = LoadingContentType.Generic; + - await Task.Delay(1); + // await Task.Delay(1); if (!value.IsVectorDataLoaded) { await _DataService.LoadVectorDataToModel(value.Model, value.FolderForMeasurement); + await _DataService.LoadAdditionalResultsToModel( value.Model, value.FolderForMeasurement ); value.IsVectorDataLoaded = true; } if (isBasedOnViewModel) @@ -266,5 +272,14 @@ public async Task EditName() } } + //this saves the result outsite of the new measurement.. Other results, like comx comy, coherence, etc are computed and then saved... + //this is different, because all the values are saved already and we only add new additional results. + public async Task SaveAdditionalResults() + { + Model.AdditionalResultsModel = ViewModelLocator.Current.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.GetAdditionalResultsModel(); + if(FolderForMeasurement!=null) + await DataService.SaveAdditionalResults(Model.AdditionalResultsModel, FolderForMeasurement); + } + } diff --git a/TremAn3/ViewModels/PlotModelsContainerViewModel.cs b/TremAn3/ViewModels/PlotModelsContainerViewModel.cs index a7c86b0..a3927c7 100644 --- a/TremAn3/ViewModels/PlotModelsContainerViewModel.cs +++ b/TremAn3/ViewModels/PlotModelsContainerViewModel.cs @@ -27,9 +27,7 @@ public void SetFreqProgressToNoData() public void InvalidateTimePlots(bool updateData) { PlotModels.Where(x => - x.DataSeriesType == DataSeriesType.FreqProgress || - x.DataSeriesType == DataSeriesType.ComX || - x.DataSeriesType == DataSeriesType.ComY) + x.DataSeriesType is DataSeriesType.FreqProgress or DataSeriesType.ComX or DataSeriesType.ComY) .ToList().ForEach(x=>x.PlotModel.InvalidatePlot(updateData)); } public void InvalidateAllPlots(bool updateData) diff --git a/TremAn3/ViewModels/ResultsViewModel.cs b/TremAn3/ViewModels/ResultsViewModel.cs index c65adb7..895cd76 100644 --- a/TremAn3/ViewModels/ResultsViewModel.cs +++ b/TremAn3/ViewModels/ResultsViewModel.cs @@ -10,10 +10,36 @@ namespace TremAn3.ViewModels; using Services; +using System.Collections.ObjectModel; using Windows.ApplicationModel.Core; +using Windows.UI.Xaml; public class ResultsViewModel : ViewModelBase { + + + public ResultsViewModel() + { + CoherenceMeasurementResults.CollectionChanged += async (_, e) => + { + + if (AddedToCollection == null) + return; + if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) + { + AddedToCollection.Invoke(); + } + + }; + } + + + public Action AddedToCollection { get; set; } + + + //setups the result from model, then allow computation. Wout this, computation run on data loading (and rewrite files) + public bool IsComputationPaused { get; set; } = true; + /// /// computes all the results and place it into the DataResutlsDict /// @@ -22,47 +48,174 @@ public class ResultsViewModel : ViewModelBase /// public async Task ComputeAllResults(double frameRate, List> comXAllRois, List> comYAllRois) { - await Task.Run(async () => { - Coherence coherence = new Coherence((int)frameRate, comXAllRois, comYAllRois); + await Task.Run(async () => + { + Coherence coherence = new((int)frameRate, comXAllRois, comYAllRois); + DataResultsDict.Clear(); var coh = coherence.Compute(); DataResultsDict.Add(DataSeriesType.Coherence, coh); - await WindowManagerService.Current.MainDispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { + await WindowManagerService.Current.MainDispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + { + IsCoherenceOk = coh.IsOk; + IsComputationPaused = true; + CoherenceMinHz = 0; + CoherenceMaxHz = frameRate / 2; await ComputeAdditionalResults(); + IsComputationPaused = false; }); }); } - public async Task ComputeAllResults(double frameRate, List> comXAllRois, List> comYAllRois, List freqProgress) + private bool _isCoherenceOk; + public bool IsCoherenceOk { - await Task.Run(async () => { - Coherence coherence = new((int)frameRate, comXAllRois, comYAllRois); - DataResultsDict.Clear(); - var coh = coherence.Compute(); - DataResultsDict.Add(DataSeriesType.Coherence, coh); - await ComputeAdditionalResults(); - }); + get => _isCoherenceOk; + set => Set(ref _isCoherenceOk, value); } + + public async Task ComputeAdditionalResults() { - await WindowManagerService.Current.MainDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - if (DataResultsDict.ContainsKey(DataSeriesType.Coherence) && DataResultsDict[DataSeriesType.Coherence].Y != null) - CoherenceAverage = DataResultsDict[DataSeriesType.Coherence].Y.Average(); + await WindowManagerService.Current.MainDispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + { + if (ViewModelLocator.Current.PastMeasurementsViewModel.SelectedMeasurementVm == null) + return; + if (!DataResultsDict.ContainsKey(DataSeriesType.Coherence) || DataResultsDict[DataSeriesType.Coherence].Y == null) + return; + var r = DataResultsDict[DataSeriesType.Coherence]; + //find indexes of r.X where r.X is between CoherenceMinHz and CoherenceMaxHz + var minIndex = r.X.IndexOf(r.X.FirstOrDefault(x => x >= CoherenceMinHz)); + var maxIndex = r.X.IndexOf(r.X.LastOrDefault(x => x <= CoherenceMaxHz)); + //get r.Y values between minIndex and maxIndex + + if (minIndex == -1 || maxIndex == -1) + return; + if (minIndex >= maxIndex) + return; + var chAverage = r.Y.GetRange(minIndex, maxIndex - minIndex).Average(); + CoherenceMeasurementResults.Add(new() { Average = chAverage, MaxHz = CoherenceMaxHz, MinHz = CoherenceMinHz }); + await ViewModelLocator.Current.PastMeasurementsViewModel.SelectedMeasurementVm.SaveAdditionalResults(); + }); } - private double coherenceAverage; - public double CoherenceAverage + private ObservableCollection _coherenceMeasurementResults = []; + public ObservableCollection CoherenceMeasurementResults + { + get => _coherenceMeasurementResults; + //set => Set(ref _coherenceMeasurementResults, value); + } + + public async Task RemoveOne(CoherenceMeasurementResults res) + { + CoherenceMeasurementResults.Remove(res); + await ViewModelLocator.Current.PastMeasurementsViewModel.SelectedMeasurementVm.SaveAdditionalResults(); + } + + + public AdditionalResultsModel GetAdditionalResultsModel() + { + AdditionalResultsModel additionalResultsModel = new(); + + additionalResultsModel.CoherenceAverageResults = + CoherenceMeasurementResults.Select(x => new CoherenceAverageResultModel() { Average = x.Average, MaxHz = x.MaxHz, MinHz = x.MinHz }) + .ToList(); + return additionalResultsModel; + + } + + public bool isSettingRangeDontCompute; + + private double _CoherenceMinHz; + public double CoherenceMinHz { - get => coherenceAverage; - set => Set(ref coherenceAverage, value); + get => _CoherenceMinHz; + set =>Set(ref _CoherenceMinHz, value); + } + private double _CoherenceMaxHz; + public double CoherenceMaxHz + { + get => _CoherenceMaxHz; + set => Set(ref _CoherenceMaxHz, value); + } + + + + public async void DeleteAllMeasurements() + { + CoherenceMeasurementResults.Clear(); + await ViewModelLocator.Current.PastMeasurementsViewModel.SelectedMeasurementVm.SaveAdditionalResults(); + + } + - public Dictionary DataResultsDict { get; set; } = new(); + public async Task CoherenceRangeChanged() + { + if (!IsComputationPaused) + { + await ComputeAdditionalResults();//coherence average + } + await ViewModelLocator.Current.FreqCounterViewModel.DisplayPlots(false); + // await FreqCounterViewModel.ParentVm.PastMeasurementsViewModel.SaveSelectedMeasurement(); + } + + internal void ResetResults() + { + isSettingRangeDontCompute = true; + CoherenceMeasurementResults.Clear(); + isSettingRangeDontCompute = false; + } + + internal void SetIsCoherenceOk() + { + if (DataResultsDict.ContainsKey(DataSeriesType.Coherence)) + IsCoherenceOk = DataResultsDict[DataSeriesType.Coherence].IsOk; + else + IsCoherenceOk = false; + } + + public Dictionary DataResultsDict + { + get; + set; + } = []; //public Guid Id { get; set; } = Guid.Empty; } + +public class CoherenceMeasurementResults : ViewModelBase +{ + + + private double average; + public double Average + { + get => average; + set => Set(ref average, value); + } + + private double _minHz; + public double MinHz + { + get => _minHz; + set => Set(ref _minHz, value); + } + + private double _maxHz; + public double MaxHz + { + get => _maxHz; + set => Set(ref _maxHz, value); + } + public async void DeleteMe(object sender, RoutedEventArgs e) + { + await ViewModelLocator.Current.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.RemoveOne(this); + } +} + diff --git a/TremAn3/Views/FreqCounterUc.xaml b/TremAn3/Views/FreqCounterUc.xaml index df970bc..7a3ef6c 100644 --- a/TremAn3/Views/FreqCounterUc.xaml +++ b/TremAn3/Views/FreqCounterUc.xaml @@ -11,8 +11,8 @@ xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" xmlns:mux="using:Microsoft.UI.Xaml.Controls" xmlns:vm="using:TremAn3.ViewModels" - xmlns:core="using:TremAn3.Core" - + xmlns:core="using:TremAn3.Core" xmlns:numberformatting="using:Windows.Globalization.NumberFormatting" + Loaded="UserControl_Loaded" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> @@ -44,7 +44,7 @@ + Position="{Binding Position}" LineExtents="{Binding PlotModel.PlotArea}"> @@ -161,7 +161,7 @@ IsEnabled="False" /> - Video file is no longer available. It has probably been deleted or moved. You can still review the measurement. @@ -195,8 +195,9 @@ - + @@ -212,7 +213,8 @@ - @@ -224,7 +226,9 @@ - @@ -257,7 +261,8 @@ - @@ -269,21 +274,99 @@ - - + - - - - - + + + + + Current view: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -338,7 +421,8 @@ x:Name="FreqProgressStatusMessageTxbl" Text="{x:Bind ViewModel.FreqProgressViewModel.StatusMessage, Mode=OneWay}"> - diff --git a/TremAn3/Views/FreqCounterUc.xaml.cs b/TremAn3/Views/FreqCounterUc.xaml.cs index de7a166..675f9bb 100644 --- a/TremAn3/Views/FreqCounterUc.xaml.cs +++ b/TremAn3/Views/FreqCounterUc.xaml.cs @@ -18,20 +18,55 @@ namespace TremAn3.Views { + using Microsoft.UI.Xaml.Controls; using OxyPlot.Windows; + using System.Collections.Specialized; + using Windows.Globalization.NumberFormatting; using Windows.System; public sealed partial class FreqCounterUc : UserControl { - private TeachingTipsViewModel TeachingTipsViewModel => ViewModelLocator.Current.TeachingTipsViewModel; - public FreqCounterViewModel ViewModel{ get; set; } + public FreqCounterViewModel ViewModel { get; set; } public SettingsViewModel SettingsViewModel => ViewModelLocator.Current.SettingsViewModel; + + + public FreqCounterUc() { this.InitializeComponent(); + + + } + + //public DecimalFormatter DecimalFormatter = new DecimalFormatter() { FractionDigits = 3}; + + + private async void NumberBoxMaxHz_OnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) + { + ViewModel.CurrentGlobalScopedResultsViewModel.CoherenceMaxHz = args.NewValue; + await ViewModel.CurrentGlobalScopedResultsViewModel.CoherenceRangeChanged(); + } + + private async void NumberBoxMinHz_OnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) + { + ViewModel.CurrentGlobalScopedResultsViewModel.CoherenceMinHz = args.NewValue; + await ViewModel.CurrentGlobalScopedResultsViewModel.CoherenceRangeChanged(); + } + + private void ScrollToEndOfCoherenceResults() + { + if(ListViewCoherenceResults.Items.Count>0) + ListViewCoherenceResults.ScrollIntoView(ListViewCoherenceResults.Items[ListViewCoherenceResults.Items.Count - 1]); + } + + private void UserControl_Loaded(object sender, RoutedEventArgs e) + { + + + ViewModelLocator.Current.FreqCounterViewModel.CurrentGlobalScopedResultsViewModel.AddedToCollection = ScrollToEndOfCoherenceResults; } } }