Files
DP44/Common/DTS.Common.Serialization/FIAT_ASC/FIAT_Asc.File.Writer.cs
2026-04-17 14:55:32 -04:00

811 lines
38 KiB
C#

using DTS.Common.DAS.Concepts;
using DTS.Common.DAS.Concepts.DAS.Channel;
using DTS.Common.Enums.Sensors;
using DTS.Common.Utilities;
using DTS.Common.Utilities.DotNetProgrammingConstructs;
using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static DTS.Serialization.Test.Module;
namespace DTS.Serialization.FIAT_ASC
{
// *** see FIAT_Asc.File.cs ***
public partial class File
{
///
/// <summary>
/// Utility object for serializing <see cref="DTS.Serialization.Test"/>s to disk
/// in the FTSS CSV format.
/// </summary>
///
public partial class Writer : Writer<File>, IWriter<Test>
{
/// <summary>
/// Initialize an instance of the FIAT_Asc.File.Writer class.
/// </summary>
///
/// <param name="fileType">
/// The associated <see cref="DTS.SErialization.FIAT_Asc.File"/> object.
/// </param>
///
internal Writer(File fileType, int encoding)
: base(fileType, encoding)
{
WriterParent = fileType;
}
/// <summary>
/// the owning file that controls this writer
/// </summary>
internal File WriterParent { get; }
private const string NUMBER_FORMAT = "F8";
/// <summary>
/// Notify <see cref="BeginEventHandler"/> subscribers that the write
/// is starting.
/// </summary>
public event BeginEventHandler OnBegin;
/// <summary>
/// Notify <see cref="EndEventHandler"/> subscribers that the write
/// is finished.
/// </summary>
public event EndEventHandler OnEnd;
/// <summary>
/// Notify <see cref="TickEventHandler"/> subscribers that we are one
/// tick closer to write completion.
/// </summary>
public event TickEventHandler OnTick;
/// <summary>
/// notify subscribers that the write was cancelled
/// </summary>
public event CancelEventHandler OnCancel;
/// <summary>
/// notify subscribers that the writer encountered fatal error
/// </summary>
public event ErrorEventHandler OnError;
/// <summary>
/// The number of data samples that need to be written for a "tick" to be dispatched.
/// </summary>
private int DataSamplesPerTick => 1000;
/// <summary>
/// Return the number of data to be written per "tick".
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
private uint GetChannelTicks(Test.Module.Channel channel)
{
try
{
//
// Most of our wait time will be spent writing data, so we need to give
// the process a little finer granularity.
//
return (uint)(channel.PersistentChannelInfo.Length / DataSamplesPerTick);
}
catch (System.Exception ex)
{
throw new Exception(
"encountered problem determining number of status ticks for channel " +
(null != channel ? "\"" + channel.Number + "\"" : "<NULL>"), ex);
}
}
/// <summary>
/// Get/set the filtered channel data. If this list is supplied, the corresponding test
/// channel data values will be supplied from this list.
/// </summary>
public Dictionary<string, FilteredData> FilteredChannelData
{
get => _FilteredChannelData.Value;
set => _FilteredChannelData.Value = value;
}
private readonly Property<Dictionary<string, FilteredData>> _FilteredChannelData
= new Property<Dictionary<string, FilteredData>>(
"FilteredChannelData",
new Dictionary<string, FilteredData>(),
true
);
/// <summary>
/// Write the specified test to the specified pathname.
/// </summary>
///
/// <param name="pathname">
/// The <see cref="string"/> pathname to which the specified test should be serialized.
/// </param>
///
/// <param name="test">
/// The <see cref="DTS.Serialization.Test"/> to be written out.
/// </param>
///
public void Write(string pathname, string id, Test test, bool bFiltering, bool includeGroupNameInISOExport,
double minStartTime, int dataCollectionLength)
{
try
{
Write(pathname, id, null, test, bFiltering, includeGroupNameInISOExport, null, null, 0, null, null,
null, null, null, null, minStartTime, dataCollectionLength);
}
catch (System.Exception ex)
{
throw new Exception("encountered problem non-event notified writing test", ex);
}
}
public bool UseFlatExportFolder { get; set; } = false;
/// <summary>
/// Write the representation file/files of the specified DTS.Serialization.Test
/// at the given pathname.
/// </summary>
///
/// <param name="targetPathname">
/// The <see cref="string"/> pathname of the specified object's resulting file
/// representation.
/// </param>
///
public void Write(string pathname,
string id,
string dataFolder,
Test test,
bool bFiltering,
bool includeGroupNameInISOExport,
FilteredData fd,
Test.Module.Channel tmChannel,
int channelNumber,
BeginEventHandler beginEventHandler,
CancelEventHandler cancelEventHandler,
EndEventHandler endEventHandler,
TickEventHandler tickEventHandler,
ErrorEventHandler errorEventHandler,
CancelRequested cancelRequested,
double minStartTime,
int dataCollectionLength)
{
System.Exception exception = null;
try
{
OnBegin += beginEventHandler;
OnEnd += endEventHandler;
OnTick += tickEventHandler;
OnCancel += cancelEventHandler;
OnError += errorEventHandler;
// Compute the total number of write ticks that will be dispatched during this
// write, and let the caller know that we're underway.
uint totalWriteTicksNeeded = 0;
uint totalWriteTicksDispatched = 0;
if (test.Channels.Count > 0)
totalWriteTicksNeeded = GetChannelTicks(test.Channels[0]);
OnBegin?.Invoke(this, totalWriteTicksNeeded);
var folderPath = Path.GetDirectoryName(pathname);
var fileName = Path.GetFileNameWithoutExtension(pathname);
var destFolder = folderPath;
foreach (var channel in test.Channels)
{
if (!Directory.Exists(destFolder))
{
Directory.CreateDirectory(destFolder);
}
var channelName = channel.ChannelName2;
if (channel is AnalogInputChannel AIC)
{
channelName = AIC.Description;
}
var channelFileName = Path.Combine(destFolder, $"{channelName.Replace("\\", "_").Replace("/", "_")}{Extension}");
APILogger.Log("opening ", channelFileName);
using (var fs = new FileStream(channelFileName, FileMode.Create))
{
Encoding encoder;
try
{
encoder = Common.Utils.FileUtils.GetEncoding(DefaultEncoding);
}
catch (System.Exception ex)
{
APILogger.Log("Problem getting encoder", ex);
encoder = Encoding.Default;
}
//DateTime start = DateTime.Now;
using (var fileWriter = new StreamWriter(fs, encoder, 1024 * 1000))
{
//List<Test.Module.Channel> exportChannels = test.Channels;
WriteChannelInfo(fileWriter,
id,
test,
FilteredChannelData,
channelFileName,
tickEventHandler,
totalWriteTicksNeeded,
ref totalWriteTicksDispatched,
cancelRequested,
channel);
}
fs.Close();
}
}
}
catch (System.Exception ex)
{
APILogger.Log("encountered problem writing CSV test files", ex);
exception = new Exception("encountered problem writing CSV test files", ex);
}
finally
{
OnEnd?.Invoke(this);
if (null != cancelRequested && cancelRequested())
{
OnCancel?.Invoke(this);
}
if (null != exception && null != errorEventHandler)
{
errorEventHandler(this, exception);
}
else if (null != exception)
{
throw exception;
}
}
}
public void Initialize(string pathname,
string id,
string dataFolder,
Test test,
bool bFiltering,
bool includeGroupNameInISOExport,
FilteredData fd,
Test.Module.Channel tmChannel,
int channelNumber,
BeginEventHandler beginEventHandler,
CancelEventHandler cancelEventHandler,
EndEventHandler endEventHandler,
TickEventHandler tickEventHandler,
ErrorEventHandler errorEventHandler,
CancelRequested cancelRequested)
{
}
/// <summary>
/// Generate a best-estimate export size for the specified dataset, and check it against the
/// current disk availability stats.
/// </summary>
///
/// <param name="testname">
/// The <see cref="string"/> name of the test to be verified.
/// </param>
///
/// <param name="saveLocation">
/// The <see cref="string"/> name of the target location into which the test must fit.
/// </param>
///
/// <param name="headerLines">
/// An <see cref="System.Collections.Generic.IDictionary<TKey,TValue>"/> containing pairs
/// of header entry name <see cref="string"/>s and corresponding value <see cref="string"/>s.
/// </param>
///
/// <param name="coder">
/// A <see cref="DescriptionAttributeCoder<TargetType>"/> for en/coding
/// <see cref="FtssHeaderLine"/> header enumerations.
/// </param>
///
/// <param name="channelsWithMeta">
/// A populated <see cref="System.Collections.Generic.List<T>"/> of
/// <see cref="ChannelWithMeta"/>s generated from
/// the test to be exported.
/// </param>
///
/// <param name="dataCollectionLength">
/// The <see cref="int"/> length of the data collection within this test that is to be
/// exported.
/// </param>
///
/// <param name="minStartTime">
/// The earliest <see cref="double"/> T0-relative start time on any channel within the
/// test to be exported.
/// </param>
///
/// <param name="sampleRate">
/// The <see cref="double"/> sample rate of the test to be exported.
/// </param>
///
private void VerifyExportedFileWillFitOnDisk(
string testname,
string saveLocation,
List<ChannelWithMeta> channelsWithMeta,
int dataCollectionLength,
double minStartTime,
double sampleRate,
CancelRequested cancelRequested
)
{
try
{
//
// Compute the size of the header information.
//
ulong predictedExportSize = 0;
//
// Compute the size of the data portion.
// this computation is expensive for large data collections, so lets just subsample them for now
// for every 2 million samples collected we'll increase the size of subsamples by 1.
var segmentSize = dataCollectionLength / 2000000;
if (segmentSize < 1)
{
segmentSize = 1;
}
for (var i = 0; i < dataCollectionLength; i += segmentSize)
{
if (null != cancelRequested && cancelRequested())
{
break;
}
var thisSegmentSize =
(ulong)((minStartTime + i * 1.0 / sampleRate).ToString(NUMBER_FORMAT) +
System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator).Length;
foreach (var channelWithMeta in channelsWithMeta)
{
if (null != cancelRequested && cancelRequested())
{
break;
}
var thisChannelsIndexAtCurrentTime =
i - (int)((channelWithMeta.StartTime - minStartTime) * channelWithMeta.SampleRate);
thisSegmentSize += thisChannelsIndexAtCurrentTime >= 0 && thisChannelsIndexAtCurrentTime <
channelWithMeta.Channel.PersistentChannelInfo.Length
? 9U
: 1U;
}
var segmentJump = i + segmentSize > dataCollectionLength
? dataCollectionLength - i
: segmentSize;
predictedExportSize += thisSegmentSize * (ulong)segmentJump;
}
predictedExportSize *= sizeof(char);
// Get the stats on available disk space.
var errorcode = DiskUtility.GetDiskFreeSpaceEx(saveLocation, out ulong freeBytesAvailable,
out ulong totalNumberOfBytes, out ulong totalNumberOfFreeBytes);
if (0 != errorcode)
{
// Do the comparison.
if (freeBytesAvailable < predictedExportSize)
{
var bytesNeeded = DiskUtility.GetHumanReadableBytes(predictedExportSize);
var bytesAvailable = DiskUtility.GetHumanReadableBytes(freeBytesAvailable);
throw new UserException("Export requires " + bytesNeeded + " but there are only " +
bytesAvailable + " bytes available on \"" + saveLocation + "\"");
}
}
else
{
APILogger.Log("Failed to get free disk space, windows error: ", errorcode);
}
}
catch (System.Exception ex)
{
throw new Exception(
"encountered problem trying to determing if CSV export of test " +
(testname ?? "<NULL>") + " will fit at location " +
(saveLocation ?? "<NULL>"), ex);
}
}
/// <summary>
/// Write the specified test to the specified stream.
/// </summary>
///
/// <param name="fileWriter">
/// The <see cref="StreamWriter"/> to which the specified test should be serialized.
/// </param>
///
/// <param name="channel">
/// The <see cref="Test"/> to be serialized.
/// </param>
///
/// <param name="writeEvent">
/// The <see cref="Writer.EventHandler"/> that should handle write-progress
/// reporting; null if none is to be used.
/// <param name="targetPath">
/// The <see cref="string"/> path that the file writer will be writing to.
/// </param>
///
/// <param name="tickEventHandler">
/// The <see cref="TickEventHandler"/> that will process "tick" events
/// indicating progress during the export procedure.
/// </param>
///
/// <param name="totalWriteTicksNeeded">
/// The total <see cref="uint"/> number of progress ticks that will be issued during the
/// export procedure.
/// </param>
///
/// <param name="totalWriteTicksDispatched">
/// Returns the total <see cref="uint"/> number of tick events that have been dispatched.
/// </param>
///
protected void WriteChannelInfo
(
StreamWriter fileWriter,
string id,
Test test,
Dictionary<string, FilteredData> filteredData,
string targetPath,
TickEventHandler tickEventHandler,
uint totalWriteTicksNeeded,
ref uint totalWriteTicksDispatched,
CancelRequested cancelRequested,
Test.Module.Channel channel)
{
try
{
double sampleRate = channel.ParentModule.SampleRateHz;
if (1 < SubSampleInterval)
{
if (true == Filtered)
{
var SubSample = new NHTSASubSample<double>();
SubSample.data = filteredData[channel.ChannelId].Data;
SubSample.preTriggerSamples =
channel.ParentModule.TriggerSampleNumbers[0] - channel.ParentModule.StartRecordSampleNumber;
SubSample.sampleRate = sampleRate;
SubSample.subSampleInterval = SubSampleInterval;
SubSample.SubSample();
filteredData[channel.ChannelId].Data = SubSample.data;
channel.IsSubsampled = true; // I don't think anyone will consume this, but for completeness.
}
else
{
var SubSample = new NHTSASubSample<short>();
SubSample.data = channel.PersistentChannelInfo.Data;
SubSample.preTriggerSamples =
channel.ParentModule.TriggerSampleNumbers[0] - channel.ParentModule.StartRecordSampleNumber;
SubSample.sampleRate = sampleRate;
SubSample.subSampleInterval = SubSampleInterval;
SubSample.SubSample();
channel.PersistentChannelInfo.Data = SubSample.data;
channel.IsSubsampled =
true; // I don't think anyone will consume this, but for completeness.
}
}
var channelsWithMeta = new List<ChannelWithMeta>();
var filteredChannelData = filteredData.ContainsKey(channel.ChannelId) ? filteredData[channel.ChannelId] : null;
bool isDigitalInput = false;
bool isSquib = false;
if (channel is Test.Module.AnalogInputChannel)
{
isDigitalInput = (channel as Test.Module.AnalogInputChannel).Bridge == SensorConstants.BridgeType.DigitalInput;
isSquib = (channel as Test.Module.AnalogInputChannel).Bridge == SensorConstants.BridgeType.SQUIB;
}
var rate = Convert.ToInt32(Math.Ceiling(sampleRate / channel.ParentModule.SampleRateHz));
var preTriggerSamples = rate * (channel.ParentModule.TriggerSampleNumbers[0] - (double)channel.ParentModule.StartRecordSampleNumber);
if (preTriggerSamples < 0)
{
preTriggerSamples = 0D;
}
if (preTriggerSamples > channel.ParentModule.NumberOfSamples)
{
preTriggerSamples = channel.ParentModule.NumberOfSamples;
}
if (preTriggerSamples > Start * sampleRate)
{
preTriggerSamples = Math.Truncate(Start * sampleRate);
}
var postTriggerSamples = (channel.ParentModule.NumberOfSamples - preTriggerSamples) * rate;
if (postTriggerSamples < 0)
{
postTriggerSamples = 0D;
}
if (postTriggerSamples > Stop * sampleRate)
{
postTriggerSamples = Math.Truncate(Stop * sampleRate);
}
var scaler = new DataScaler();
scaler.IsInverted =
channel is IInversionAware ? (channel as IInversionAware).IsInverted : false;
if ((channel as ILinearized).LinearizationFormula.IsValid())
{
scaler.SetLinearizationFormula((channel as ILinearized).LinearizationFormula);
}
else
{
scaler.SetLinearizationFormula(null);
}
scaler.Digital = (channel as Test.Module.AnalogInputChannel).IsDigital();
scaler.SetDigitalMultiplier((channel as Test.Module.AnalogInputChannel).DigitalMultiplier);
scaler.DigitalMode = (channel as Test.Module.AnalogInputChannel).DigitalMode;
scaler.SetScaleFactorMv(channel.Data.ScaleFactorMv);
scaler.SetScaleFactorEU(channel.Data.ScaleFactorEU);
scaler.SetUseEUScaleFactors(channel.Data.UseEUScaleFactors);
scaler.UnitConversion = (channel as Test.Module.AnalogInputChannel).UnitConversion;
scaler.BasedOnOutputAtCapacity = (channel as Test.Module.AnalogInputChannel).AtCapacity;
scaler.CapacityOutputIsBasedOn =
(channel as Test.Module.AnalogInputChannel).CapacityOutputIsBasedOn;
scaler.SensitivityUnits = (channel as Test.Module.AnalogInputChannel).SensitivityUnits;
scaler.Multiplier = (channel as Test.Module.AnalogInputChannel).Multiplier;
scaler.UserOffsetEU = (channel as Test.Module.AnalogInputChannel).UserOffsetEU;
if (channel is Test.Module.AnalogInputChannel)
{
scaler.IEPE = (channel as Test.Module.AnalogInputChannel).Bridge ==
SensorConstants.BridgeType.IEPE;
scaler.Digital = (channel as Test.Module.AnalogInputChannel).Bridge ==
SensorConstants.BridgeType.DigitalInput;
}
scaler.SetMvPerEu(channel.Data.MvPerEu);
scaler.SetDataZeroLevelADC(channel.DataZeroLevelAdc);
scaler.SetRemovedADC(channel.RemovedADC);
scaler.SetRemovedInternalADC(channel.RemovedInternalADC);
scaler.SetZeroMvInADC(channel.ZeroMvInADC);
try
{
scaler.SetWindowAverageADC(channel.WindowAverageADC);
}
catch (System.Exception) { }
if (channel is Test.Module.AnalogInputChannel)
{
//
// Maybe these should be replaced by DTS.DAS.Concepts versions for casting? Would need to
// add excitation voltage as a concept, and proportional to excitation.
//
var analogChannel = channel as Test.Module.AnalogInputChannel;
scaler.SetInitialOffset(analogChannel.InitialOffset);
scaler.ZeroMethodType = analogChannel.ZeroMethod;
scaler.NominalExcitationVoltage = analogChannel.ExcitationVoltage;
if (analogChannel.MeasuredExcitationVoltageValid)
{
try
{
scaler.MeasuredExcitationVoltage = analogChannel.MeasuredExcitationVoltage;
}
catch { }
}
if (analogChannel.FactoryExcitationVoltageValid)
{
try
{
scaler.FactoryExcitationVoltage = analogChannel.FactoryExcitationVoltage;
}
catch { }
}
scaler.ProportionalToExcitation = analogChannel.ProportionalToExcitation;
}
var dStartTime = channel.ParentModule.StartRecordSampleNumber /
(double)channel.ParentModule.SampleRateHz;
if (channel.ParentModule.TriggerSampleNumbers.Count > 0)
{
dStartTime -= channel.ParentModule.TriggerSampleNumbers[0] /
(double)channel.ParentModule.SampleRateHz;
}
channelsWithMeta.Add(
new ChannelWithMeta(
channel,
scaler,
channel.ParentModule.SampleRateHz,
dStartTime
)
);
var originalChannelName = channel.OriginalChannelName;
if (string.IsNullOrWhiteSpace(originalChannelName))
{
//if original channel name is blank, attempt to fall back on names based on whatever the current iso view mode is
//this has to be done because outputsquibchannel does not currently store the property OriginalChannelName
//15619 Channel name is empty in csv export if data is from squib
if (channel is Test.Module.AnalogInputChannel aic)
{
switch (WriterParent.ISOViewMode)
{
case Common.Enums.IsoViewMode.ISOOnly:
originalChannelName = aic.IsoChannelName;
break;
case Common.Enums.IsoViewMode.ISOAndUserCode:
originalChannelName = $"{aic.IsoChannelName}\\{aic.UserChannelName}";
break;
case Common.Enums.IsoViewMode.UserCodeOnly:
originalChannelName = aic.UserChannelName;
break;
case Common.Enums.IsoViewMode.ChannelNameOnly:
originalChannelName = aic.UserChannelName;
break;
}
}
}
//Zero Method and if Average Over Time, then Begin/End
var sb = new StringBuilder(scaler.ZeroMethodType.ToString());
if (scaler.ZeroMethodType == Common.Enums.Sensors.ZeroMethodType.AverageOverTime)
{
sb.Append(" (");
sb.Append((channel as Test.Module.AnalogInputChannel).ZeroAverageWindow.Begin);
sb.Append("/");
sb.Append((channel as Test.Module.AnalogInputChannel).ZeroAverageWindow.End);
sb.Append(")");
}
var minStartTime = ChannelWithMeta.GetMinStartTime(Start, channelsWithMeta);
var minStopTime = ChannelWithMeta.GetMinStopTime(Stop, channelsWithMeta);
var dataCollectionLength = (int)((minStopTime - minStartTime) * sampleRate);
VerifyExportedFileWillFitOnDisk(test.Id,
targetPath.Remove(targetPath.IndexOf(Path.VolumeSeparatorChar) + 1),
channelsWithMeta,
dataCollectionLength,
minStartTime,
sampleRate,
cancelRequested);
fileWriter.WriteLine($"{minStartTime.ToString(NUMBER_FORMAT)} {(1 / sampleRate).ToString(NUMBER_FORMAT)}");
fileWriter.WriteLine(dataCollectionLength);
for (var i = 0; i <= dataCollectionLength; i++)
{
if (null != cancelRequested && cancelRequested())
{
break;
}
var bNeedComma = false;
foreach (var channelWithMeta in channelsWithMeta)
{
if (bNeedComma)
{
fileWriter.Write(System.Globalization.CultureInfo.CurrentCulture.TextInfo
.ListSeparator);
}
else
{
bNeedComma = true;
}
//
// If this channel's data is valid at the current time/index then go ahead and send it out
// to the file; otherwise write out a blank space.
//14513 double rounding error causing data to be offset by one sample incorrectly
// note that using decimals will fix the imprecision issue, but will use more time ...
var delta = Convert.ToDecimal(channelWithMeta.StartTime) - Convert.ToDecimal(minStartTime);
var channelOffsetStart = Convert.ToInt32(delta * Convert.ToDecimal(channelWithMeta.SampleRate));
var rateMeta = sampleRate / channelWithMeta.SampleRate;
var indexAtCurrentTime = (i - channelOffsetStart) / rateMeta;
var thisChannelsIndexAtCurrentTime = Convert.ToInt32(Math.Floor(indexAtCurrentTime));
var step = Convert.ToInt32(Math.Ceiling(indexAtCurrentTime) - thisChannelsIndexAtCurrentTime);
if (Filtered)
{
if (filteredData.ContainsKey(channelWithMeta.Channel.ChannelId))
{
var channelFilteredData = filteredData[channelWithMeta.Channel.ChannelId];
if (thisChannelsIndexAtCurrentTime >= 0 && thisChannelsIndexAtCurrentTime <
channelFilteredData.Data.Length)
{
var indexValue = channelFilteredData.Data[thisChannelsIndexAtCurrentTime];
if (0 == step) { fileWriter.Write(indexValue.ToString(NUMBER_FORMAT)); }
else
{
var increment = 0D;
if ((1 + thisChannelsIndexAtCurrentTime) < channelFilteredData.Data.Length)
{
increment = (channelFilteredData.Data[1 + thisChannelsIndexAtCurrentTime] - indexValue) / rateMeta;
}
else
{
increment = (indexValue - channelFilteredData.Data[thisChannelsIndexAtCurrentTime - 1]) / rateMeta;
}
fileWriter.Write((indexValue + increment * step).ToString(NUMBER_FORMAT));
}
}
}
else
{
fileWriter.Write(double.NaN.ToString());
}
}
else
{
double dInitialEU = 0;
var aChannel = channelWithMeta.Channel as Test.Module.AnalogInputChannel;
if (thisChannelsIndexAtCurrentTime >= 0 && thisChannelsIndexAtCurrentTime <
channelWithMeta.Channel.PersistentChannelInfo.Length)
{
var adc = channelWithMeta.Channel.PersistentChannelInfo[thisChannelsIndexAtCurrentTime];
var increment = 0D;
if ((1 + thisChannelsIndexAtCurrentTime) < channelWithMeta.Channel.PersistentChannelInfo.Length)
{
increment = (channelWithMeta.Channel.PersistentChannelInfo[1 + thisChannelsIndexAtCurrentTime] - adc) / rateMeta;
}
else { increment = (adc - channelWithMeta.Channel.PersistentChannelInfo[thisChannelsIndexAtCurrentTime - 1]) / rateMeta; }
if (null != aChannel)
{
dInitialEU = aChannel.InitialEu;
}
fileWriter.Write(channelWithMeta.Scaler.GetEU((adc + increment * step)).ToString(NUMBER_FORMAT));
}
}
}
if (0 == i % DataSamplesPerTick)
{
OnTick?.Invoke(this, (double)totalWriteTicksDispatched++ / totalWriteTicksNeeded * 100);
System.Windows.Forms.Application.DoEvents();
}
fileWriter.WriteLine();
}
fileWriter.Flush();
}
catch (System.Exception ex)
{
throw new Exception("encountered problem writing FTSS/Excel channel headers", ex);
}
}
public double Start { get; set; } = 0D;
public double Stop { get; set; } = 0D;
public ushort SubSampleInterval { get; set; } = 1;
public bool Filtered { get; set; } = true;
}
}
}