init
This commit is contained in:
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* ToyotaCsv.File.Writer.cs
|
||||
*
|
||||
* Copyright © 2009
|
||||
* Diversified Technical Systems, Inc.
|
||||
* All Rights Reserved
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using DTS.Common.DAS.Concepts;
|
||||
using DTS.Common.Utilities.DotNetProgrammingConstructs;
|
||||
using DTS.Common.Enums.Sensors;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.Serialization.ToyotaCsv
|
||||
{
|
||||
// *** see ToyotaCsv.File.cs ***
|
||||
public partial class File
|
||||
{ ///
|
||||
/// <summary>
|
||||
/// Utility object for serializing <see cref="DTS.Serialization.Test"/>s to disk
|
||||
/// in the Toyota CSV format.
|
||||
/// </summary>
|
||||
///
|
||||
public class Writer : Writer<File>, IWriter<Test>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize an instance of the ToyotaCsv.File.Writer class.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="fileType">
|
||||
/// The associated <see cref="DTS.SErialization.ToyotaCsv.File"/> object.
|
||||
/// </param>
|
||||
///
|
||||
internal Writer(File fileType, int encoding)
|
||||
: base(fileType, encoding)
|
||||
{
|
||||
}
|
||||
|
||||
private const string NUMBER_FORMAT = "F8";
|
||||
|
||||
/// <summary>
|
||||
/// The different export modes that should theoretically be supported by this format.
|
||||
/// </summary>
|
||||
public enum ExportMode
|
||||
{
|
||||
FtssExcel,
|
||||
Ttc,
|
||||
Standard,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the current CSV export format.
|
||||
/// </summary>
|
||||
public ExportMode CurrentExportMode
|
||||
{
|
||||
get => _CurrentExportMode.Value;
|
||||
set => _CurrentExportMode.Value = value;
|
||||
}
|
||||
private readonly Property<ExportMode> _CurrentExportMode
|
||||
= new Property<ExportMode>(
|
||||
typeof(Writer).Namespace + ".File.Writer.CurrentExportMode",
|
||||
ExportMode.FtssExcel,
|
||||
true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Notify <see cref="DTS.Serialization.BeginEventHandler"/> subscribers that the write
|
||||
/// is starting.
|
||||
/// </summary>
|
||||
public event BeginEventHandler OnBegin;
|
||||
|
||||
/// <summary>
|
||||
/// Notify <see cref="DTS.Serialization.EndEventHandler"/> subscribers that the write
|
||||
/// is finished.
|
||||
/// </summary>
|
||||
public event EndEventHandler OnEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Notify <see cref="DTS.Serialization.TickEventHandler"/> subscribers that we are one
|
||||
/// tick closer to write completion.
|
||||
/// </summary>
|
||||
public event TickEventHandler OnTick;
|
||||
|
||||
/// <summary>
|
||||
/// notify subscribers that writing is cancelling
|
||||
/// </summary>
|
||||
public event CancelEventHandler OnCancel;
|
||||
|
||||
/// <summary>
|
||||
/// notify subscribers that 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.ToString() + "\"" : "<NULL>"), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 List<double[]> FilteredChannelData
|
||||
{
|
||||
get => _FilteredChannelData.Value;
|
||||
set => _FilteredChannelData.Value = value;
|
||||
}
|
||||
private readonly Property<List<double[]>> _FilteredChannelData
|
||||
= new Property<List<double[]>>(
|
||||
typeof(Writer).FullName + ".FilteredChannelData",
|
||||
new List<double[]>(),
|
||||
true
|
||||
);
|
||||
|
||||
/// <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 fs = new FileStream(pathname, FileMode.Create);
|
||||
|
||||
|
||||
using (var fileWriter = new StreamWriter(fs, Encoding.Unicode))
|
||||
{
|
||||
var exportChannels = test.Channels;
|
||||
|
||||
switch (CurrentExportMode)
|
||||
{
|
||||
case ExportMode.FtssExcel:
|
||||
case ExportMode.Ttc:
|
||||
WriteChannelInfo(fileWriter,
|
||||
test,
|
||||
FilteredChannelData,
|
||||
tickEventHandler,
|
||||
totalWriteTicksNeeded,
|
||||
ref totalWriteTicksDispatched,
|
||||
cancelRequested);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("ToyotaCSV::File::Writer export mode not supported: " + CurrentExportMode.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
exception = new Exception("encountered problem writing TTS test files", ex);
|
||||
APILogger.Log("encountered problem writing TTS 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>
|
||||
/// A collection of a channel and associated metadata that will all be needed together when
|
||||
/// this file format starts laying down bytes.
|
||||
/// </summary>
|
||||
private sealed class ChannelWithMeta
|
||||
{
|
||||
/// <summary>
|
||||
/// Get/set the <see cref="DTS.Serialization.Test.Module.Channel"/> to be serialized.
|
||||
/// </summary>
|
||||
public Test.Module.Channel Channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the <see cref="DataScaler"/> associated with the
|
||||
/// specified channel.
|
||||
/// </summary>
|
||||
public DataScaler Scaler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the <see cref="double"/> sample rate associated with the specified channel.
|
||||
/// </summary>
|
||||
public double SampleRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the <see cref="double"/> start time associated with the specified channel.
|
||||
/// </summary>
|
||||
public double StartTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of the ChannelWithMeta class.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="channel">
|
||||
/// A <see cref="DTS.Serialization.Test.Module.Channel"/> to be serialized.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="scaler">
|
||||
/// The <see cref="DataScaler"/> containing scaling information for
|
||||
/// the specified channel.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="samplerate">
|
||||
/// The <see cref="double"/> sample rate of the associated channel.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="starttime">
|
||||
/// The <see cref="double"/> start time of the associated channel.
|
||||
/// </param>
|
||||
///
|
||||
public ChannelWithMeta(Test.Module.Channel channel, DataScaler scaler, double samplerate, double starttime)
|
||||
{
|
||||
Channel = channel;
|
||||
Scaler = scaler;
|
||||
SampleRate = samplerate;
|
||||
StartTime = starttime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified test to the specified stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="fileWriter">
|
||||
/// The <see cref="System.IO.StreamWriter"/> to which the specified test should be serialized.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="test">
|
||||
/// The <see cref="DTS.Serialization.Test"/> to be serialized.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="writeEvent">
|
||||
/// The <see cref="ToyotaCsv.File.Writer.EventHandler"/> that should handle write-progress
|
||||
/// reporting; null if none is to be used.
|
||||
/// </param>
|
||||
///
|
||||
protected void WriteChannelInfo
|
||||
(
|
||||
StreamWriter fileWriter,
|
||||
Test test,
|
||||
List<double[]> filteredData,
|
||||
TickEventHandler tickEventHandler,
|
||||
uint totalWriteTicksNeeded,
|
||||
ref uint totalWriteTicksDispatched,
|
||||
CancelRequested cancelRequested
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Row 1: Test number
|
||||
fileWriter.WriteLine(test.Id);
|
||||
|
||||
// Row 2:
|
||||
fileWriter.WriteLine(test.Id);
|
||||
|
||||
// Row 3: Test Date
|
||||
fileWriter.WriteLine($"{test.InceptionDate.Year - 2000}-{test.InceptionDate.Month}-{test.InceptionDate.Day}");
|
||||
|
||||
// Row 4: Impact Speed
|
||||
fileWriter.WriteLine("");
|
||||
|
||||
// Row 5, 6:
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
|
||||
// Row 7:
|
||||
fileWriter.WriteLine(test.Description);
|
||||
|
||||
// Row 8: Sampling Rate
|
||||
fileWriter.WriteLine(test.Modules.Count > 0 ? test.Modules[0].SampleRateHz.ToString() : "NO MODULES");
|
||||
|
||||
// Row 9, 10, 11, 12:
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
|
||||
// Row 13:
|
||||
//fprintf(fpExport, "%s,\n", TestPtr->VehicleList[0].Description);
|
||||
|
||||
// Row 14: Dummies
|
||||
fileWriter.WriteLine();
|
||||
//for(iIndex = 0; iIndex < MAX_DEVICE_DISPLAY; iIndex++)
|
||||
//{
|
||||
// if(strlen(TestPtr->DeviceList[iIndex].DeviceID) > 0)
|
||||
// {
|
||||
// fprintf(fpExport, "%s,", TestPtr->DeviceList[iIndex].DeviceID);
|
||||
// }
|
||||
//}
|
||||
//fprintf(fpExport, "\n");
|
||||
|
||||
// Row 15, 16, 17, 18, 19, 20:
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
|
||||
// Row 21: Header: Time and codes
|
||||
fileWriter.Write("TIME,");
|
||||
|
||||
foreach (var channel in test.Channels)
|
||||
fileWriter.Write(channel.ChannelDescriptionString + ",");
|
||||
fileWriter.WriteLine();
|
||||
|
||||
//for(iIndex = 0; iIndex < iChannelListCount; iIndex++)
|
||||
//{
|
||||
// RackIndex = ChannelList[iIndex].iRack;
|
||||
// ModuleIndex = ChannelList[iIndex].iModule;
|
||||
// ChannelIndex = ChannelList[iIndex].iChannel;
|
||||
// ChannelPtr = &SystemPtr->HWRack[RackIndex].HWModuleList[ModuleIndex].HWChannelList[ChannelIndex];
|
||||
// if(IsChannelValid(ChannelPtr))
|
||||
// {
|
||||
// if(strlen(ChannelPtr->SensorUserChannelDescription) > 0)
|
||||
// {
|
||||
// fprintf(fpExport, "%s,", ChannelPtr->SensorUserChannelDescription);
|
||||
// }
|
||||
// else if(ChannelPtr->UserPointerValid[0])
|
||||
// {
|
||||
// fprintf(fpExport, "%s,", ((SensorDBItem *)(ChannelPtr->UserPointer[0]))->Code);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//fprintf(fpExport, "\n");
|
||||
|
||||
// Row 22, 23, 24:
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
fileWriter.WriteLine();
|
||||
|
||||
var channelsWithMeta = new List<ChannelWithMeta>();
|
||||
var filteredDataIndex = 0;
|
||||
|
||||
foreach (var channel in test.Channels)
|
||||
{ //
|
||||
// Bundle data and associated scaling information for in preparation for
|
||||
// upcoming write.
|
||||
//
|
||||
var scaler = new DataScaler();
|
||||
scaler.IsInverted = channel is Common.DAS.Concepts.DAS.Channel.IInversionAware ? (channel as Common.DAS.Concepts.DAS.Channel.IInversionAware).IsInverted : false;
|
||||
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;
|
||||
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.SetDigitalMultiplier((channel as Test.Module.AnalogInputChannel).DigitalMultiplier);
|
||||
scaler.DigitalMode = (channel as Test.Module.AnalogInputChannel).DigitalMode;
|
||||
}
|
||||
if ((channel as Common.DAS.Concepts.DAS.Channel.ILinearized).LinearizationFormula.IsValid())
|
||||
{
|
||||
scaler.SetLinearizationFormula((channel as Common.DAS.Concepts.DAS.Channel.ILinearized).LinearizationFormula);
|
||||
}
|
||||
else
|
||||
{
|
||||
scaler.SetLinearizationFormula(null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
scaler.SetRemovedADC(channel.RemovedADC);
|
||||
scaler.SetRemovedInternalADC(channel.RemovedInternalADC);
|
||||
scaler.SetDataZeroLevelADC(channel.DataZeroLevelAdc);
|
||||
scaler.SetZeroMvInADC(channel.ZeroMvInADC);
|
||||
try { scaler.SetWindowAverageADC(channel.WindowAverageADC); }
|
||||
catch (System.Exception) { }
|
||||
//try { scaler.SetIRTraccZeros(Convert.ToDouble(channel.DataZeroLevelAdc), Convert.ToDouble(channel.PreTestZeroLevelAdc)); }
|
||||
//catch (System.Exception) { }
|
||||
//initialEU
|
||||
}
|
||||
catch (System.Exception) { }
|
||||
|
||||
scaler.SetMvPerEu(channel.Data.MvPerEu);
|
||||
|
||||
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;
|
||||
try
|
||||
{
|
||||
scaler.SetInitialOffset(analogChannel.InitialOffset);
|
||||
}
|
||||
catch (System.Exception) { }
|
||||
scaler.ZeroMethodType = analogChannel.ZeroMethod;
|
||||
scaler.NominalExcitationVoltage = analogChannel.ExcitationVoltage;
|
||||
try { scaler.FactoryExcitationVoltage = analogChannel.FactoryExcitationVoltage; }
|
||||
catch { };
|
||||
try { scaler.MeasuredExcitationVoltage = analogChannel.MeasuredExcitationVoltage; }
|
||||
catch { };
|
||||
scaler.ProportionalToExcitation = analogChannel.ProportionalToExcitation;
|
||||
}
|
||||
|
||||
|
||||
channelsWithMeta.Add(
|
||||
new ChannelWithMeta(
|
||||
channel,
|
||||
scaler,
|
||||
channel.ParentModule.SampleRateHz,
|
||||
channel.ParentModule.TriggerSampleNumbers.Count > 0 ? channel.ParentModule.TriggerSampleNumbers[0] / channel.ParentModule.SampleRateHz * -1.0 : 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var minStartTime = channelsWithMeta.Min(cwm => cwm.StartTime);
|
||||
var maxStartTime = channelsWithMeta.Max(cwm => cwm.StartTime);
|
||||
// LINQ statement will sometimes hand back float-rounding error numbers (4.999999999999995 v 5.0), use same rounding we use for time.
|
||||
var minStopTime = double.Parse(channelsWithMeta.Min(cwm => cwm.StartTime + (cwm.Channel.PersistentChannelInfo.Length - 1) * 1.0 / cwm.SampleRate).ToString(NUMBER_FORMAT));
|
||||
var maxStopTime = double.Parse(channelsWithMeta.Max(cwm => cwm.StartTime + (cwm.Channel.PersistentChannelInfo.Length - 1) * 1.0 / cwm.SampleRate).ToString(NUMBER_FORMAT));
|
||||
|
||||
var minSampleRate = channelsWithMeta.Min(cwm => cwm.SampleRate);
|
||||
var maxSampleRate = channelsWithMeta.Max(cwm => cwm.SampleRate);
|
||||
if (minSampleRate != maxSampleRate)
|
||||
throw new UserException("Lowest sample rate (" + minSampleRate.ToString() + ") does not match highest sample rate (" + maxSampleRate.ToString() + ").\nThey must be identical.");
|
||||
var sampleRate = minSampleRate;
|
||||
|
||||
var dataCollectionLength = (int)((maxStopTime - minStartTime) * sampleRate); //maxDataLengthInAllChannels;
|
||||
|
||||
for (var i = 0; i < dataCollectionLength; i++)
|
||||
{
|
||||
if (null != cancelRequested && cancelRequested()) { break; }
|
||||
double time;
|
||||
fileWriter.Write((time = minStartTime + i * 1.0 / sampleRate).ToString("F6") + ",");
|
||||
foreach (var channelWithMeta in channelsWithMeta)
|
||||
{
|
||||
if (null != cancelRequested && cancelRequested()) { break; }
|
||||
//
|
||||
// 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 thisChannelsIndexAtCurrentTime = i - Convert.ToInt32(delta * Convert.ToDecimal(channelWithMeta.SampleRate));
|
||||
if (null != filteredData && filteredData.Count > 0)
|
||||
fileWriter.Write((thisChannelsIndexAtCurrentTime >= 0 && thisChannelsIndexAtCurrentTime < filteredData[filteredDataIndex].Length
|
||||
? (filteredData[filteredDataIndex][thisChannelsIndexAtCurrentTime]).ToString("F6")
|
||||
: "") + ",");
|
||||
else
|
||||
fileWriter.Write((thisChannelsIndexAtCurrentTime >= 0 && thisChannelsIndexAtCurrentTime < channelWithMeta.Channel.PersistentChannelInfo.Length
|
||||
? channelWithMeta.Scaler.GetEU(channelWithMeta.Channel.PersistentChannelInfo[thisChannelsIndexAtCurrentTime]).ToString("F6")
|
||||
: "") + ",");
|
||||
filteredDataIndex++;
|
||||
}
|
||||
|
||||
if (0 == i % DataSamplesPerTick)
|
||||
{
|
||||
OnTick?.Invoke(this, ((double)totalWriteTicksDispatched++) / totalWriteTicksNeeded * 100);
|
||||
System.Windows.Forms.Application.DoEvents();
|
||||
}
|
||||
|
||||
fileWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new Exception("encountered problem writing FTSS/Excel channel headers", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user