Files
2026-04-17 14:55:32 -04:00

656 lines
31 KiB
C#

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;
}
}
}