init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
using System.ComponentModel;
|
||||
using DTS.Common.Converters;
|
||||
|
||||
namespace DTS.Viewer.TestModification
|
||||
{
|
||||
public enum Keys
|
||||
{
|
||||
ApplyShiftT0ModsTestOnly //System Setting for whether to restrict "Shift T0" test modifications to "Test" only.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,655 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DTS.Common;
|
||||
using DTS.Common.Classes.Sensors;
|
||||
using DTS.Common.Events;
|
||||
using DTS.Common.Interface;
|
||||
using DTS.Common.Settings;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using DTS.Serialization;
|
||||
using Prism.Ioc;
|
||||
using Prism.Events;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace DTS.Viewer.TestModification.Model
|
||||
{
|
||||
public class TestModelManipulation
|
||||
{
|
||||
#region constants
|
||||
|
||||
/// <summary>
|
||||
/// parameters in test serialization
|
||||
/// </summary>
|
||||
public const double UNUSED_START_TIME = 0;
|
||||
|
||||
public const int UNUSED_DATA_COLLECTION_LENGTH = 0;
|
||||
|
||||
/// <summary>
|
||||
/// .chn file can back up just the header or the entire file
|
||||
/// if backed up as a header the extension will be .header.bak
|
||||
/// </summary>
|
||||
private const string BackupHeaderExtension = ".header.bak";
|
||||
|
||||
/// <summary>
|
||||
/// the extension for full backup files (.chn.bak, .dts.bak)
|
||||
/// </summary>
|
||||
private const string BackupFileExtension = ".bak";
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// revert changes since original file, this includes the .DTS file and all binary files
|
||||
/// </summary>
|
||||
public static void UndoAllModification(ITestModificationModel model)
|
||||
{
|
||||
RestoreDTSFileIfModified(model);
|
||||
|
||||
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
|
||||
f.Importer.Read(model.SelectedChannel.BinaryFilePath, out var target);
|
||||
|
||||
foreach (var m in target.Modules)
|
||||
{
|
||||
foreach (var c in m.Channels) { RestoreChannelIfModified(c); }
|
||||
foreach (var cc in m.CalculatedChannels) { RestoreChannelIfModified(cc); }
|
||||
}
|
||||
|
||||
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
|
||||
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
//if DeriveROI from all is true, wait till the derive function is complete otherwise you could be
|
||||
//reading and writing from the same files at the same time
|
||||
if (!SettingsDB.GetGlobalValueBool("DeriveROIFromAll", false))
|
||||
{
|
||||
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { /*model.SelectedChannel*/ });
|
||||
eventAggregator?.GetEvent<RefreshTestRequestEvent>().Publish(dtsFileName);
|
||||
}
|
||||
|
||||
eventAggregator?.GetEvent<TestModificationEvent>()
|
||||
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns whether a backup file exists for the given model
|
||||
/// </summary>
|
||||
public static bool BackupExists(ITestModificationModel model)
|
||||
{
|
||||
if (model.SelectedChannel != null)
|
||||
{
|
||||
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
|
||||
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
|
||||
var dtsBackupFileName = dtsFileName + BackupFileExtension;
|
||||
var chnBackupFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, model.SelectedChannel.BinaryFileName + BackupFileExtension);
|
||||
return System.IO.File.Exists(dtsBackupFileName) || System.IO.File.Exists(chnBackupFileName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the test id from a binary filename
|
||||
/// it didn't seem the test id was available from any of the structures I had access to
|
||||
/// but it's the portion of the file name without all the decoration
|
||||
/// the filename is in the form of TestIdChxxx.yyy.chn
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
private static string GetTestIdFromBinaryFileName(string filename)
|
||||
{
|
||||
var index = filename.IndexOf('.');
|
||||
filename = filename.Substring(0, index);
|
||||
index = filename.LastIndexOf("Ch", StringComparison.Ordinal);
|
||||
//we expect the Ch, but if we don't find it, use the what portion we have
|
||||
return index >= 0 ? filename.Substring(0, index) : filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// restores DTS file (if a backup file exists)
|
||||
/// </summary>
|
||||
private static void RestoreDTSFileIfModified(ITestModificationModel model)
|
||||
{
|
||||
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
|
||||
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
|
||||
var dtsBackupFileName = dtsFileName + BackupFileExtension;
|
||||
if (!System.IO.File.Exists(dtsBackupFileName)) return;
|
||||
//Delete the current .dts file
|
||||
Common.Utils.FileUtils.DeleteFileOrMove(dtsFileName, APILogger.Log);
|
||||
System.IO.File.Move(dtsBackupFileName, dtsFileName);
|
||||
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
eventAggregator?.GetEvent<TestModificationEvent>()
|
||||
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// restores an individual channel binary file [assuming a backup exists]
|
||||
/// note this does not restore any xml entries in the .dts file for the channel
|
||||
/// </summary>
|
||||
/// <param name="channelToRestore"></param>
|
||||
private static void RestoreChannelIfModified(Test.Module.Channel channelToRestore)
|
||||
{
|
||||
var chnFileName = ((Test.Module.AnalogInputChannel)channelToRestore).PersistentChannelInfo.Filename;
|
||||
var chnHeaderBackupFileName = chnFileName + BackupHeaderExtension;
|
||||
var chnBackupFileName = chnFileName + BackupFileExtension;
|
||||
|
||||
//binary files can be backed up two different ways, a full backup or just the header
|
||||
//if we find the header, we can restore be just overwriting the bytes from the backup header
|
||||
if (System.IO.File.Exists(chnHeaderBackupFileName))
|
||||
{
|
||||
var originalFile = System.IO.File.ReadAllBytes(chnHeaderBackupFileName);
|
||||
var headerLength = channelToRestore.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw
|
||||
.File.PersistentChannel.Field.BeginningOfData);
|
||||
using (var fs = System.IO.File.OpenWrite(chnFileName))
|
||||
{
|
||||
fs.Write(originalFile, 0, headerLength); //146
|
||||
}
|
||||
|
||||
//Delete the copy of the original header
|
||||
Common.Utils.FileUtils.DeleteFileOrMove(chnHeaderBackupFileName, APILogger.Log);
|
||||
}
|
||||
|
||||
//if there's a full backup, then just replace the entire file with the backup, if there's no full backup
|
||||
//we are already done
|
||||
if (!System.IO.File.Exists(chnBackupFileName)) return;
|
||||
|
||||
//Delete Current File
|
||||
Common.Utils.FileUtils.DeleteFileOrMove(chnFileName, APILogger.Log);
|
||||
|
||||
//Restore original file
|
||||
System.IO.File.Copy(chnBackupFileName, chnFileName);
|
||||
|
||||
//Delete the copy of the original header
|
||||
Common.Utils.FileUtils.DeleteFileOrMove(chnBackupFileName, APILogger.Log);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// backs up a single binary channel
|
||||
/// binary channels can be backed up two ways, either the entire binary file or just the header
|
||||
/// we backup the entire binary file if we are modifying data
|
||||
/// if we are just modifying the binary header, then we just back up the header (since it's significantly smaller usually)
|
||||
/// </summary>
|
||||
/// <param name="testModuleChannel"></param>
|
||||
/// <param name="headerOnly"></param>
|
||||
public static void BackupChannelIfNeeded(Test.Module.Channel testModuleChannel, bool headerOnly)
|
||||
{
|
||||
//Back up .chn file if necessary
|
||||
var chnFilePath = testModuleChannel.PersistentChannelInfo.Filename;
|
||||
var chnBackupFilePath = chnFilePath + BackupFileExtension;
|
||||
|
||||
// If we have a full backup of the CHN, quit
|
||||
if (System.IO.File.Exists(chnBackupFilePath)) return;
|
||||
|
||||
|
||||
var chnHeaderBackupFilePath = chnFilePath + BackupHeaderExtension;
|
||||
byte[] originalFileHeader = null;
|
||||
|
||||
// If we are backing up the header and have a header backup, quit
|
||||
// Else if we have to backup the whole file, get the header backup bytes then delete the header backup
|
||||
if (System.IO.File.Exists(chnHeaderBackupFilePath))
|
||||
{
|
||||
if (headerOnly) { return; }
|
||||
//Read contents of header backup
|
||||
originalFileHeader = System.IO.File.ReadAllBytes(chnHeaderBackupFilePath);
|
||||
//Delete the copy of the original header
|
||||
Common.Utils.FileUtils.DeleteFileOrMove(chnHeaderBackupFilePath, APILogger.Log);
|
||||
}
|
||||
|
||||
// Get the header bytes from the original chn and write the header backup
|
||||
if (headerOnly)
|
||||
{
|
||||
var originalFile = System.IO.File.ReadAllBytes(chnFilePath);
|
||||
|
||||
// Get the end of the header byte offset index
|
||||
var writeEndIndex = testModuleChannel.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw.File.PersistentChannel.Field.BeginningOfData);
|
||||
// open the header backup to write
|
||||
using (var fs = System.IO.File.OpenWrite(chnHeaderBackupFilePath))
|
||||
{
|
||||
fs.Write(originalFile, 0, writeEndIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the whole chn to the backup location
|
||||
System.IO.File.Copy(chnFilePath, chnBackupFilePath);
|
||||
|
||||
// If we dont have a header backup, we are done
|
||||
if (originalFileHeader == null) return;
|
||||
|
||||
// Get the end of the header byte offset index
|
||||
var writeEndIndex = testModuleChannel.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw.File.PersistentChannel.Field.BeginningOfData);
|
||||
|
||||
// open the full backup file to write the original header on top of
|
||||
using (var fs = System.IO.File.OpenWrite(chnBackupFilePath))
|
||||
{
|
||||
fs.Write(originalFileHeader, 0, writeEndIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// backs up the DTS file
|
||||
/// if the backup file already exists we don't need to do any work, the backup file will be
|
||||
/// the original version of the file
|
||||
/// </summary>
|
||||
/// <param name="directory"></param>
|
||||
/// <param name="testId"></param>
|
||||
private static void BackupDTSFile(string directory, string testId)
|
||||
{
|
||||
var dtsFilePath = Path.Combine(directory, testId + ".dts");
|
||||
var dtsBackupFilePath = dtsFilePath + ".bak";
|
||||
|
||||
//Back up dts file only if a backup file does not exist. This should guarantee that the
|
||||
//original file is the only one that is preserved.
|
||||
if (false == System.IO.File.Exists(dtsBackupFilePath))
|
||||
{
|
||||
System.IO.File.Copy(dtsFilePath, dtsBackupFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// writes all modifications to binary files/dts file
|
||||
/// </summary>
|
||||
/// <param name="useISOCodeFilterMapping">
|
||||
/// controls whether to change isocode field for software filter when the software filter is modified.
|
||||
/// </param>
|
||||
/// <param name="bUseZeroForUnfiltered"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SaveModification(ITestModificationModel model, bool useISOCodeFilterMapping, bool bUseZeroForUnfiltered)
|
||||
{
|
||||
var bWriteDTSFile = model.IsModifiedDataFlag
|
||||
|| model.IsModifiedFilter
|
||||
|| model.IsModifiedDescription
|
||||
|| model.IsModifiedT0
|
||||
|| model.IsModifiedEuMultiplier
|
||||
|| model.IsModifiedEuOffset
|
||||
|| model.IsModifiedSensitivity;
|
||||
|
||||
var bWriteChnBackup = model.IsModifiedT0 || model.IsModifiedSensitivity || model.IsModifiedLineFit;
|
||||
|
||||
var bWriteHeaderOnly = model.IsModifiedSensitivity || model.IsModifiedT0 || (model.IsModifiedFilter && useISOCodeFilterMapping);
|
||||
|
||||
if (!bWriteDTSFile && !bWriteChnBackup)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
|
||||
if (bWriteDTSFile)
|
||||
{
|
||||
BackupDTSFile(model.SelectedChannel.BinaryFilePath, testId);
|
||||
}
|
||||
|
||||
var dtsFile = Path.Combine(model.SelectedChannel.BinaryFilePath, $"{testId}.dts");
|
||||
Serialization.SliceRaw.File.ReadTestSetup(dtsFile, out var target, out var testSetup, false);
|
||||
|
||||
foreach (var module in target.Modules)
|
||||
{
|
||||
if (model.IsModifiedT0 && model.T0Mode == T0Mode.Test)
|
||||
{
|
||||
//if we are applying T0 channes to the entire test, then this module will be updated regardless of what channel is selected
|
||||
ApplyT0Change(model, module);
|
||||
}
|
||||
if (model.IsModifiedT0 && model.T0Mode == T0Mode.DAS &&
|
||||
module.Channels.TrueForAll(ch => ch.ChannelId != model.SelectedChannel.ChannelId) &&
|
||||
module.BaseSerialNumber == target.Modules.First(m => m.Channels.Exists(ch => ch.ChannelId == model.SelectedChannel.ChannelId))?.BaseSerialNumber)
|
||||
{
|
||||
//the model has a modified T0, we're in DAS mode, the module does *not* have the selected channel, but is on the same DAS as the one that does
|
||||
ApplyT0Change(model, module);
|
||||
}
|
||||
foreach (var channel in module.Channels)
|
||||
{
|
||||
//find the specific channel in the dts file/binaries that matches the selected channel in the UI
|
||||
if ((channel.ChannelId != model.SelectedChannel.ChannelId || !channel.PersistentChannelInfo.Filename.EndsWith(model.SelectedChannel.BinaryFileName)) && !model.IsModifiedT0) continue;
|
||||
|
||||
if (model.IsModifiedFilter && channel.ChannelId == model.SelectedChannel.ChannelId && useISOCodeFilterMapping)
|
||||
{
|
||||
bWriteChnBackup = true;
|
||||
}
|
||||
|
||||
if (bWriteChnBackup)
|
||||
{
|
||||
BackupChannelIfNeeded(channel, bWriteHeaderOnly);
|
||||
}
|
||||
|
||||
var aic = (Test.Module.AnalogInputChannel)channel;
|
||||
|
||||
if (model.IsModifiedDescription && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
channel.ChannelDescriptionString = model.Description;
|
||||
model.SelectedChannel.SetChannelDescriptionAndDisplayName(model.Description);
|
||||
}
|
||||
if (model.IsModifiedDataFlag && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
channel.DataFlag = (int)model.SelectedDataFlag;
|
||||
model.SelectedChannel.DataFlag = channel.DataFlag;
|
||||
}
|
||||
if (model.IsModifiedFilter && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
//FB 13120
|
||||
aic.SoftwareFilter = CFCFilterDTSFileStringConverter.FilterClassToString(model.SelectedFilter);
|
||||
model.SelectedChannel.SoftwareFilter = aic.SoftwareFilter;
|
||||
if (useISOCodeFilterMapping)
|
||||
{
|
||||
var iso = new Common.ISO.IsoCode(aic.IsoCode);
|
||||
//FB 13120
|
||||
iso.FilterClass = CFCFilterDTSFileStringConverter.FilterClassToCFC(model.SelectedFilter, bUseZeroForUnfiltered);
|
||||
aic.IsoCode = iso.StringRepresentation;
|
||||
var notUsed = aic.PersistentChannelInfo.NumberOfTriggers;
|
||||
aic.PersistentChannelInfo.IsoCode = iso.StringRepresentation.ToArray();
|
||||
model.SelectedChannel.IsoCode = iso.StringRepresentation;
|
||||
}
|
||||
}
|
||||
if (model.IsModifiedEuMultiplier && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
aic.Multiplier = model.EuMultiplier;
|
||||
model.SelectedChannel.Multiplier = model.EuMultiplier;
|
||||
}
|
||||
if (model.IsModifiedEuOffset && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
aic.UserOffsetEU = model.EuOffset;
|
||||
model.SelectedChannel.UserOffsetEu = model.EuOffset;
|
||||
}
|
||||
|
||||
if (model.IsModifiedSensitivity && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
//modifying sensitivity involves both changing the dts file and the .chn binary
|
||||
var mvPerEU = Math.Abs(model.Sensitivity);
|
||||
aic.Sensitivity = model.Sensitivity;
|
||||
model.SelectedChannel.Sensitivity = model.Sensitivity;
|
||||
var p = aic.PersistentChannelInfo;
|
||||
//this is to force the values to be initialized before setting
|
||||
// ReSharper disable once UnusedVariable
|
||||
var notUsed = p.MvPerEu;
|
||||
p.MvPerEu = mvPerEU;
|
||||
}
|
||||
if (model.IsModifiedT0 && model.T0Mode == T0Mode.DAS && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
ApplyT0Change(model, module);
|
||||
}
|
||||
if (model.IsModifiedLineFit && channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
ApplyLineFit(model, channel);
|
||||
}
|
||||
if (bWriteChnBackup && channel.ChannelId == model.SelectedChannel.ChannelId || model.IsModifiedT0)
|
||||
{
|
||||
var p = aic.PersistentChannelInfo;
|
||||
p.StampCrc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bWriteDTSFile)
|
||||
{
|
||||
WriteDTSFileChanges(model, dtsFile, target, testSetup, testId);
|
||||
}
|
||||
//this is to publish that the selected channel has changed
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
|
||||
eventAggregator?.GetEvent<TestModificationEvent>()
|
||||
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
|
||||
return true;
|
||||
}
|
||||
// added TestId parameter to correct incorrect target.TestId values, function now uses passed in test id rather than target.testid
|
||||
//14897 modifying T0 in view data creates a second dts file holding the name of the test setup and not the test ID
|
||||
private static void WriteDTSFileChanges(ITestModificationModel model, string dtsFile, Test target, TestSetup testSetup, string testId)
|
||||
{
|
||||
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
|
||||
f.Exporter.Write(model.SelectedChannel.BinaryFilePath, testId, target, false, false, UNUSED_START_TIME, UNUSED_DATA_COLLECTION_LENGTH);
|
||||
//Append the <TestSetup> DataPRO info to the end of the existing .dts file which already contains SLICEWare-compatible info
|
||||
using (var writer = new StringWriter())
|
||||
{
|
||||
new System.Xml.Serialization.XmlSerializer(typeof(TestSetup)).Serialize(writer, testSetup);
|
||||
Encoding encoder;
|
||||
try
|
||||
{
|
||||
//force UTF-16 for the dts file, it contains "UTF-16" in the xml by default and isn't consumed by anything that requires
|
||||
//codepage exports (CSV/excel)
|
||||
encoder = Encoding.Unicode; //UTF-16
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log("Problem getting encoder", ex);
|
||||
encoder = Encoding.Default;
|
||||
}
|
||||
|
||||
using (var fileWriter = new StreamWriter(dtsFile, true, encoder))
|
||||
{
|
||||
fileWriter.Write(fileWriter.NewLine + writer);
|
||||
}
|
||||
System.Threading.Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SaveModificationDataFlag(ITestModificationModel model)
|
||||
{
|
||||
if (!model.IsModifiedDataFlag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
|
||||
BackupDTSFile(model.SelectedChannel.BinaryFilePath, testId);
|
||||
|
||||
var dtsFile = Path.Combine(model.SelectedChannel.BinaryFilePath, $"{testId}.dts");
|
||||
Serialization.SliceRaw.File.ReadTestSetup(dtsFile, out var target, out var testSetup, false);
|
||||
|
||||
foreach (var module in target.Modules)
|
||||
{
|
||||
foreach (var channel in module.Channels)
|
||||
{
|
||||
//find the specific channel in the dts file/binaries that matches the selected channel in the UI
|
||||
if (channel.ChannelId != model.SelectedChannel.ChannelId) continue;
|
||||
|
||||
if (!model.IsModifiedDataFlag) continue;
|
||||
channel.DataFlag = (int)model.SelectedDataFlag;
|
||||
model.SelectedChannel.DataFlag = channel.DataFlag;
|
||||
}
|
||||
}
|
||||
WriteDTSFileChanges(model, dtsFile, target, testSetup, testId);
|
||||
//this is to publish that the selected channel has changed
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { model.SelectedChannel });
|
||||
PopulateFromChannel(model);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// this applies line fit on the selected channel
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="channel"></param>
|
||||
public static void ApplyLineFit(ITestModificationModel model, Test.Module.Channel channel)
|
||||
{
|
||||
BackupChannelIfNeeded(channel, false);
|
||||
using (var p = channel.PersistentChannelInfo)
|
||||
{
|
||||
p.StampCrc(); //Force init of all fields
|
||||
|
||||
var adcData = p.Data.ToList();
|
||||
|
||||
var startIndex = GetSampleIndexFromMilliseconds(channel.ParentModule, model.T1);
|
||||
var endIndex = GetSampleIndexFromMilliseconds(channel.ParentModule, model.T2);
|
||||
|
||||
//swap start and end if they are offset
|
||||
if ( startIndex > endIndex)
|
||||
{
|
||||
var temp = endIndex;
|
||||
endIndex = startIndex;
|
||||
startIndex = temp;
|
||||
}
|
||||
|
||||
var start = startIndex + 1;
|
||||
|
||||
var dataLength = (ulong)adcData.LongCount();
|
||||
|
||||
if ( start >= dataLength)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (start > int.MaxValue) { start = int.MaxValue; }
|
||||
if (endIndex >= dataLength) { endIndex = dataLength - 1; }
|
||||
if (startIndex > endIndex)
|
||||
{
|
||||
var temp = endIndex;
|
||||
endIndex = startIndex;
|
||||
startIndex = temp;
|
||||
}
|
||||
var end = endIndex - 1;
|
||||
var deltaIndex = endIndex - startIndex;
|
||||
var startADC = adcData[(int)startIndex];
|
||||
var deltaADC = adcData[(int)endIndex] - startADC;
|
||||
//http://manuscript.dts.local/f/cases/36697/Line-fit-feature-not-working-properly-after-modifying-data
|
||||
//modified this to just do a straight line fit between two points - DTM 2024-02-05
|
||||
for (var i = start; i <= end; i++)
|
||||
{
|
||||
adcData[(int)i] = (short)(startADC + (deltaADC * ((double)(i - start) / deltaIndex)));
|
||||
}
|
||||
|
||||
p.ReplaceData(adcData.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// preview line fit will update the data in the UI for the selected channel, but not on disk
|
||||
/// </summary>
|
||||
public static void PreviewLineFit(ITestModificationModel model)
|
||||
{
|
||||
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
|
||||
f.Importer.Read(model.SelectedChannel.BinaryFilePath, out var target);
|
||||
|
||||
Test.Module.Channel ch = null;
|
||||
|
||||
foreach (var module in target.Modules)
|
||||
{
|
||||
if (null != ch)
|
||||
{
|
||||
break;
|
||||
}
|
||||
foreach (var channel in module.Channels)
|
||||
{
|
||||
if (channel.ChannelId == model.SelectedChannel.ChannelId)
|
||||
{
|
||||
ch = channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null == ch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var startIndex = GetSampleIndexFromMilliseconds(ch.ParentModule, model.T1);
|
||||
var endIndex = GetSampleIndexFromMilliseconds(ch.ParentModule, model.T2);
|
||||
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
eventAggregator?.GetEvent<ChannelsModificationLineFitNotification>().Publish(new LineFitArgs(model.SelectedChannel, startIndex, endIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sample index from the file based on the given milliseconds
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <param name="milliSeconds"></param>
|
||||
/// <returns></returns>
|
||||
private static ulong GetSampleIndexFromMilliseconds(Test.Module m, double milliSeconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
var seconds = milliSeconds / 1000.0;
|
||||
var triggerSampleNumber = Convert.ToDouble(m.TriggerSampleNumbers[0]);
|
||||
var offset = m.SampleRateHz * seconds;
|
||||
if (triggerSampleNumber + offset > m.StartRecordSampleNumber)
|
||||
{
|
||||
return Convert.ToUInt64(triggerSampleNumber + offset - m.StartRecordSampleNumber);
|
||||
}
|
||||
}
|
||||
catch( OverflowException ex)
|
||||
{
|
||||
if (milliSeconds > 0) { return ulong.MaxValue; }
|
||||
return 0UL;
|
||||
}
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// applies an adjustment to T0 for the module provided
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="module"></param>
|
||||
private static void ApplyT0Change(ITestModificationModel model, Test.Module module)
|
||||
{
|
||||
//apply t0 changes
|
||||
var sampleNumberChange = Convert.ToInt64(-1 * Math.Truncate(model.T0 * module.SampleRateHz / 1000D));
|
||||
var timeChange = 0 != module.SampleRateHz
|
||||
? -1D * sampleNumberChange / module.SampleRateHz
|
||||
: model.T0 / 1000D;
|
||||
if (module.TriggerSampleNumbers.Any())
|
||||
{
|
||||
var oldTrigger = module.TriggerSampleNumbers[0];
|
||||
var newTrigger = 0UL;
|
||||
|
||||
if ((long)oldTrigger > sampleNumberChange)
|
||||
{
|
||||
newTrigger = Convert.ToUInt64((long)oldTrigger - sampleNumberChange);
|
||||
module.RequestedPreTriggerSeconds =
|
||||
module.RequestedPreTriggerSeconds - timeChange;
|
||||
module.RequestedPostTriggerSeconds =
|
||||
module.RequestedPostTriggerSeconds - timeChange;
|
||||
}
|
||||
module.TriggerSampleNumbers = new List<ulong> { newTrigger };
|
||||
foreach (var channel in module.Channels)
|
||||
{
|
||||
channel.TimeOfFirstSampleSec -= timeChange;
|
||||
BackupModifyT0AndStampChannel(channel, newTrigger);
|
||||
}
|
||||
foreach (var channel in module.CalculatedChannels)
|
||||
{
|
||||
channel.TimeOfFirstSampleSec -= timeChange;
|
||||
BackupModifyT0AndStampChannel(channel, newTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void BackupModifyT0AndStampChannel(Test.Module.Channel channel, ulong newTrigger)
|
||||
{
|
||||
BackupChannelIfNeeded(channel, true);
|
||||
var aic = (Test.Module.AnalogInputChannel)channel;
|
||||
var p = aic.PersistentChannelInfo;
|
||||
//this is done just to assure the property is initialized
|
||||
// ReSharper disable once UnusedVariable
|
||||
var notUsed = p.TriggerSampleNumbers;
|
||||
p.TriggerSampleNumbers = new[] { newTrigger };
|
||||
|
||||
p.StampCrc();
|
||||
}
|
||||
/// <summary>
|
||||
/// revert changes since last save
|
||||
/// </summary>
|
||||
public static void UndoModification(ITestModificationModel model)
|
||||
{
|
||||
PopulateFromChannel(model);
|
||||
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { model.SelectedChannel });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates/Initializes UI from selected channel
|
||||
/// </summary>
|
||||
public static void PopulateFromChannel(ITestModificationModel model)
|
||||
{
|
||||
model.Description = model.SelectedChannel?.ChannelDescriptionString;
|
||||
model.EuMultiplier = model.SelectedChannel?.Multiplier ?? 1D;
|
||||
model.T0 = 0D;
|
||||
model.EuOffset = model.SelectedChannel?.UserOffsetEu ?? 0D;
|
||||
//FB 13120
|
||||
model.SelectedFilter = FilterClass.GetFilterClassFromString(model.SelectedChannel?.SoftwareFilter);
|
||||
model.T1 = 0D;
|
||||
model.T2 = 0D;
|
||||
model.Sensitivity = model.SelectedChannel?.Sensitivity ?? 1D;
|
||||
model.SelectedDataFlag = null == model.SelectedChannel ? DataFlag.None : (DataFlag)model.SelectedChannel.DataFlag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
using DTS.Common;
|
||||
using DTS.Common.Classes.Sensors;
|
||||
using DTS.Common.Enums.Sensors;
|
||||
using DTS.Common.Interface;
|
||||
using DTS.Common.Interface.Sensors;
|
||||
using DTS.Common.Interface.Sensors.SoftwareFilters;
|
||||
using DTS.SensorDB;
|
||||
using Prism.Commands;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
// ReSharper disable RedundantDefaultMemberInitializer
|
||||
// ReSharper disable CheckNamespace
|
||||
// ReSharper disable UnassignedGetOnlyAutoProperty
|
||||
|
||||
namespace DTS.Viewer.ChartOptions.Model
|
||||
{
|
||||
public class TestModificationModel : ITestModificationModel
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// the calibration date for cal on channel (or empty if not applicable)
|
||||
/// </summary>
|
||||
public string CalDate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == Cal) { return string.Empty; }
|
||||
return Cal.CalibrationDate.ToShortDateString();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// the modify date for cal on channel (or empty if not applicable)
|
||||
/// </summary>
|
||||
public string ModifyDate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == Cal) { return string.Empty; }
|
||||
return $"{Cal.ModifyDate.ToShortDateString()} {Cal.ModifyDate.ToShortTimeString()}";
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// sensitivity in calibration record for latest cal for sensor on channel
|
||||
/// </summary>
|
||||
public double CalSensitivity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == Cal) { return double.NaN; }
|
||||
if (null == Cal.Records || null == Cal.Records.Records || 0 == Cal.Records.Records.Length)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
return Cal.Records.Records[0].Sensitivity;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (null == Cal) { return; }
|
||||
if (null == Cal.Records || null == Cal.Records.Records || 0 == Cal.Records.Records.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Cal.Records.Records[0].Sensitivity = value;
|
||||
}
|
||||
}
|
||||
private void RebindCalProperties()
|
||||
{
|
||||
OnPropertyChanged("CalSensitivity");
|
||||
OnPropertyChanged("NonLinear");
|
||||
OnPropertyChanged("ProportionalToExcitation");
|
||||
OnPropertyChanged("ShowSensorCal");
|
||||
OnPropertyChanged("CalDate");
|
||||
OnPropertyChanged("ModifyDate");
|
||||
}
|
||||
/// <summary>
|
||||
/// whether latest calibration for channel is proportional to excitation or not
|
||||
/// </summary>
|
||||
public bool ProportionalToExcitation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == Cal) { return false; }
|
||||
return Cal.IsProportional;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// whether you should show sensor cal or not
|
||||
/// </summary>
|
||||
public bool ShowSensorCal
|
||||
{
|
||||
get
|
||||
{
|
||||
return null != Cal && null != Sensor && !NonLinear;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// whether calibration is non linear or not
|
||||
/// </summary>
|
||||
public bool NonLinear
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == Cal) { return false; }
|
||||
return Cal.NonLinear;
|
||||
}
|
||||
}
|
||||
|
||||
private ISensorCalDbRecord _cal;
|
||||
/// <summary>
|
||||
/// the latest calibration for channel
|
||||
/// </summary>
|
||||
public ISensorCalDbRecord Cal
|
||||
{
|
||||
get => _cal;
|
||||
set
|
||||
{
|
||||
_cal = value;
|
||||
RebindCalProperties();
|
||||
}
|
||||
}
|
||||
private ISensorDbRecord _sensor;
|
||||
/// <summary>
|
||||
/// sensor corresponding to channel
|
||||
/// </summary>
|
||||
public ISensorDbRecord Sensor
|
||||
{
|
||||
get => _sensor;
|
||||
set
|
||||
{
|
||||
_sensor = value;
|
||||
RebindCalProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private ITestChannel _selectedChannel;
|
||||
|
||||
public ITestChannel SelectedChannel
|
||||
{
|
||||
get => _selectedChannel;
|
||||
set
|
||||
{
|
||||
if (value != _selectedChannel)
|
||||
{
|
||||
_selectedChannel = value;
|
||||
OnPropertyChanged("SelectedChannel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedDescription => !Description.Equals(SelectedChannel?.ChannelDescriptionString);
|
||||
|
||||
private string _description = "";
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => _description;
|
||||
set
|
||||
{
|
||||
if (value != _description)
|
||||
{
|
||||
_description = value;
|
||||
OnPropertyChanged("Description");
|
||||
OnPropertyChanged("IsModifiedDescription");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedEuMultiplier => !EuMultiplier.Equals(SelectedChannel?.Multiplier);
|
||||
|
||||
private double _euMultiplier = 0D;
|
||||
|
||||
public double EuMultiplier
|
||||
{
|
||||
get => _euMultiplier;
|
||||
set
|
||||
{
|
||||
if (value != _euMultiplier)
|
||||
{
|
||||
_euMultiplier = value;
|
||||
OnPropertyChanged("EuMultiplier");
|
||||
OnPropertyChanged("IsModifiedEuMultiplier");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedEuOffset => !EuOffset.Equals(SelectedChannel?.UserOffsetEu);
|
||||
|
||||
private double _euOffset = 0D;
|
||||
|
||||
public double EuOffset
|
||||
{
|
||||
get => _euOffset;
|
||||
set
|
||||
{
|
||||
if (value != _euOffset)
|
||||
{
|
||||
_euOffset = value;
|
||||
OnPropertyChanged("EuOffset");
|
||||
OnPropertyChanged("IsModifiedEuOffset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedT0 => !T0.Equals(0D);
|
||||
|
||||
private double _t0 = 0D;
|
||||
|
||||
public double T0
|
||||
{
|
||||
get => _t0;
|
||||
set
|
||||
{
|
||||
if (value != _t0)
|
||||
{
|
||||
_t0 = value;
|
||||
OnPropertyChanged("T0");
|
||||
OnPropertyChanged("IsModifiedT0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isModified;
|
||||
/// <summary>
|
||||
/// indicates whether any values have changed from default for the selected channel
|
||||
/// </summary>
|
||||
public bool IsModified
|
||||
{
|
||||
|
||||
get => _isModified;
|
||||
|
||||
private set
|
||||
{
|
||||
if (value != _isModified)
|
||||
{
|
||||
_isModified = value;
|
||||
OnPropertyChanged("IsModified");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isModifiedLineFit;
|
||||
public bool IsModifiedLineFit
|
||||
{
|
||||
get
|
||||
{
|
||||
_isModifiedLineFit = !T1.Equals(0D) || !T2.Equals(0D);
|
||||
return _isModifiedLineFit;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _isModifiedLineFit)
|
||||
{
|
||||
_isModifiedLineFit = value;
|
||||
OnPropertyChanged("IsModifiedLineFit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line fit start point (ms)
|
||||
/// </summary>
|
||||
private double _t1 = 0D;
|
||||
|
||||
public double T1
|
||||
{
|
||||
get => _t1;
|
||||
set
|
||||
{
|
||||
if (!_t1.Equals(value))
|
||||
{
|
||||
_t1 = value;
|
||||
OnPropertyChanged("T1");
|
||||
OnPropertyChanged("IsModifiedLineFit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line fit end point (ms)
|
||||
/// </summary>
|
||||
private double _t2 = 0D;
|
||||
|
||||
public double T2
|
||||
{
|
||||
get => _t2;
|
||||
set
|
||||
{
|
||||
if (!_t2.Equals(value))
|
||||
{
|
||||
_t2 = value;
|
||||
OnPropertyChanged("T2");
|
||||
OnPropertyChanged("IsModifiedLineFit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedSensitivity => !Sensitivity.Equals(SelectedChannel?.Sensitivity);
|
||||
|
||||
private double _sensitivity = 0D;
|
||||
|
||||
public double Sensitivity
|
||||
{
|
||||
get => _sensitivity;
|
||||
set
|
||||
{
|
||||
if (!_sensitivity.Equals(value))
|
||||
{
|
||||
_sensitivity = value;
|
||||
OnPropertyChanged("Sensitivity");
|
||||
OnPropertyChanged("IsModifiedSensitivity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FB 13120 updated the IsModifiedFilterto use filter class
|
||||
public bool IsModifiedFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
var sf = FilterClass.GetFilterClassFromString(SelectedChannel?.SoftwareFilter);
|
||||
return !SelectedFilter.Equals(sf);
|
||||
}
|
||||
}
|
||||
|
||||
//FB 13120 selected filter
|
||||
private IFilterClass _selectedFilter = new FilterClass(FilterClassType.Unfiltered);
|
||||
public IFilterClass SelectedFilter
|
||||
{
|
||||
get => _selectedFilter;
|
||||
set
|
||||
{
|
||||
if (!_selectedFilter.Equals(value))
|
||||
{
|
||||
_selectedFilter = value;
|
||||
OnPropertyChanged("SelectedFilter");
|
||||
OnPropertyChanged("IsModifiedFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// T0 Mode indicates whether T0 changes will be applied to DAS or test
|
||||
/// </summary>
|
||||
private T0Mode _t0Mode = T0Mode.Test;
|
||||
|
||||
public T0Mode T0Mode
|
||||
{
|
||||
get => _t0Mode;
|
||||
set
|
||||
{
|
||||
var newValue = !IsT0ModeTestOnly ? value : T0Mode.Test;
|
||||
if (newValue != _t0Mode)
|
||||
{
|
||||
_t0Mode = newValue;
|
||||
OnPropertyChanged("T0Mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isT0ModeTestOnly = false;
|
||||
public bool IsT0ModeTestOnly
|
||||
{
|
||||
get => _isT0ModeTestOnly;
|
||||
internal set
|
||||
{
|
||||
if (value != _isT0ModeTestOnly)
|
||||
{
|
||||
_isT0ModeTestOnly = value;
|
||||
OnPropertyChanged("IsT0ModeTestOnly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableSensitivityControl = true;
|
||||
public bool EnableSensitivityControl
|
||||
{
|
||||
get => _enableSensitivityControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableSensitivityControl)
|
||||
{
|
||||
_enableSensitivityControl = value;
|
||||
OnPropertyChanged("EnableSensitivityControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableLineFitControl = true;
|
||||
public bool EnableLineFitControl
|
||||
{
|
||||
get => _enableLineFitControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableLineFitControl)
|
||||
{
|
||||
_enableLineFitControl = value;
|
||||
OnPropertyChanged("EnableLineFitControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableEUOffsetControl = true;
|
||||
public bool EnableEUOffsetControl
|
||||
{
|
||||
get => _enableEUOffsetControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableEUOffsetControl)
|
||||
{
|
||||
_enableEUOffsetControl = value;
|
||||
OnPropertyChanged("EnableEUOffsetControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableEUMultiplierControl = true;
|
||||
public bool EnableEUMultiplierControl
|
||||
{
|
||||
get => _enableEUMultiplierControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableEUMultiplierControl)
|
||||
{
|
||||
_enableEUMultiplierControl = value;
|
||||
OnPropertyChanged("EnableEUMultiplierControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableFilterControl = true;
|
||||
public bool EnableFilterControl
|
||||
{
|
||||
get => _enableFilterControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableFilterControl)
|
||||
{
|
||||
_enableFilterControl = value;
|
||||
OnPropertyChanged("EnableFilterControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableDescriptionControl = true;
|
||||
public bool EnableDescriptionControl
|
||||
{
|
||||
get => _enableDescriptionControl;
|
||||
internal set
|
||||
{
|
||||
if (value != _enableDescriptionControl)
|
||||
{
|
||||
_enableDescriptionControl = value;
|
||||
OnPropertyChanged("EnableDescriptionControl");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _IsDataFlagEnabled = true;
|
||||
public bool IsDataFlagEnabled
|
||||
{
|
||||
get => _IsDataFlagEnabled;
|
||||
internal set
|
||||
{
|
||||
if (value != _IsDataFlagEnabled)
|
||||
{
|
||||
_IsDataFlagEnabled = value;
|
||||
OnPropertyChanged("IsDataFlagEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _IsT0Enabled = true;
|
||||
public bool IsT0Enabled
|
||||
{
|
||||
get => _IsT0Enabled;
|
||||
internal set
|
||||
{
|
||||
if (value != _IsT0Enabled)
|
||||
{
|
||||
_IsT0Enabled = value;
|
||||
OnPropertyChanged("IsT0Enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModifiedDataFlag => null != SelectedChannel && !SelectedDataFlag.Equals((DataFlag)SelectedChannel.DataFlag);
|
||||
|
||||
private DataFlag _dataFlag = DataFlag.None;
|
||||
|
||||
public DataFlag SelectedDataFlag
|
||||
{
|
||||
get => _dataFlag;
|
||||
set
|
||||
{
|
||||
if (value != _dataFlag)
|
||||
{
|
||||
_dataFlag = value;
|
||||
OnPropertyChanged("SelectedDataFlag");
|
||||
OnPropertyChanged("IsModifiedDataFlag");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ITestModificationViewModel _parent;
|
||||
|
||||
public ITestModificationViewModel Parent
|
||||
{
|
||||
get => _parent;
|
||||
set
|
||||
{
|
||||
if (value != _parent)
|
||||
{
|
||||
_parent = value;
|
||||
OnPropertyChanged("Parent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Occurs when a property value changes.
|
||||
///</summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
if (propertyName != "IsModified" && propertyName != "LineFitModified" && propertyName != "SelectedDataFlag")
|
||||
{
|
||||
IsModified = (IsModifiedDescription || IsModifiedEuMultiplier
|
||||
|| IsModifiedEuOffset || IsModifiedFilter
|
||||
|| IsModifiedLineFit || IsModifiedSensitivity || IsModifiedT0)
|
||||
&& SelectedChannel != null; //can't modify nothing
|
||||
}
|
||||
|
||||
if (propertyName == "IsT0ModeTestOnly")
|
||||
{
|
||||
T0Mode = _t0Mode; //if we've changed the global setting, run a check of the current mode
|
||||
}
|
||||
|
||||
Parent?.PublishChanges();
|
||||
}
|
||||
|
||||
public bool IsSaved { get; }
|
||||
|
||||
#endregion Properties
|
||||
private DelegateCommand _UpdateDatabaseCommand;
|
||||
public DelegateCommand UpdateDatabaseCommand => _UpdateDatabaseCommand ?? (_UpdateDatabaseCommand = new DelegateCommand(UpdateDatabaseMethod));
|
||||
|
||||
private void UpdateDatabaseMethod()
|
||||
{
|
||||
Parent.UpdateDatabaseMethod();
|
||||
}
|
||||
#region Functions
|
||||
/// <summary>
|
||||
/// returns true if T0 is valid
|
||||
/// right now this just means T0 falls between the start and end of the current channel's dataset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool ValidateT0()
|
||||
{
|
||||
var valid = true;
|
||||
|
||||
if (null == SelectedChannel) { return true; }
|
||||
if (null == SelectedChannel.ParentModule) { return true; }
|
||||
|
||||
var startRecordSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.StartRecordSampleNumber);
|
||||
var triggerSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.TriggerSampleNumbers[0]);
|
||||
var totalSamples = Convert.ToDecimal(SelectedChannel.ParentModule.NumberOfSamples);
|
||||
var sampleRate = Convert.ToDecimal(SelectedChannel.ParentModule.SampleRateHz);
|
||||
|
||||
//note that T0 is always in ms, so we just deal in ms here (sampleRate is hz[sps])
|
||||
var startTime = -1000 * (triggerSampleNumber - startRecordSampleNumber) / sampleRate;
|
||||
var endTime = 1000 * (totalSamples - triggerSampleNumber + startRecordSampleNumber) / sampleRate;
|
||||
|
||||
var t0 = Convert.ToDecimal(T0);
|
||||
|
||||
if (t0 < startTime || t0 > endTime) { return false; }
|
||||
|
||||
return valid;
|
||||
}
|
||||
#endregion Functions
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user