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

220 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DTS.Common.DAS.Concepts;
using DTS.Common.Utilities.Logging;
namespace DTS.Serialization.TDM
{
public class Writer : Serialization.File.Writer<File>, IWriter<Test>
{
public bool AllowTTSExportFiltering { get; set; } = false;
public double Start { get; set; }
public double Stop { get; set; }
public List<FilteredData> FilteredData { get; set; }
public Test Test { get; set; }
public ulong IncrementLevel { get; private set; }
public ushort SubSampleInterval { get; set; }
public string ExtensionPrefix { get; set; } = string.Empty;
internal Writer(File fileType, int encoding)
: base(fileType, encoding)
{
}
///
/// <summary>
/// Generate the path-specified serialization from the given object.
/// </summary>
///
/// <param name="pathname">
/// The <see cref="string"/> pathname to which the serialization will be written.
/// </param>
///
/// <param name="target">
/// The object to be serialized.
/// </param>
///
public void Write(string pathname, string id, Test target, bool bFiltering, bool includeGroupNameInISOExport, double minStartTime, int dataCollectionLength)
{
throw new NotSupportedException("TDM::Writer Write(pathname, id, test, bFiltering) not supported");
}
/// <summary>
/// Generate the path-specified serialization from the given object.
/// </summary>
///
/// <param name="pathname">
/// The <see cref="string"/> pathname to which the serialization will be written.
/// </param>
///
/// <param name="target">
/// The object to be serialized.
/// </param>
///
/// <param name="onBeginEvent">
/// The <see cref="DTS.Serialization.BeginEventHandler"/> to be notified when the write begins.
/// </param>
///
/// <param name="onEndEvent">
/// The <see cref="DTS.Serialization.EndEventHandler"/> to be notified when the write completes.
/// </param>
///
/// <param name="onTickEvent">
/// The <see cref="DTS.Serialization.TickEventHandler"/> to be notified when the write "progresses".
/// </param>
///
public void Write(string pathname,
string id,
string dataFolder,
Test target,
bool bFiltering,
bool includeGroupNameInISOExport,
FilteredData fd,
Test.Module.Channel tmChannel,
int channelNumber,
BeginEventHandler onBeginEvent,
CancelEventHandler onCancelEvent,
EndEventHandler onEndEvent,
TickEventHandler onTickEvent,
ErrorEventHandler onErrorEvent,
CancelRequested cancelRequested,
double minStartTime,
int dataCollectionLength)
{
Test = target;
try
{
_onTickEvent = onTickEvent;
//There are some cases in Data Pro where the UI is storing precision that is not communicated to the user. For example, an ROI of 0.500 seconds
// may actually be represented as 0.5001. There is nothing magic about it - standard floating point problem. However the roots
// of it are actually in differences between the download code of TDAS and SLICE. SLICE doesn't care about trigger sample numbers, but
// TDAS download is expressed as sample around t=0 with t=0 sample implied. This too is not really a big deal. Where it gets ugly is in reconciling
// the fact that Data PRO, SLICEWare, and the services are written around the SLICE model.
// For now (and this is definitely a hack), round to the nearest millisecond. This should be transparent if/when the root problem is fixed.
Start = Math.Round(Start, 3);
Stop = Math.Round(Stop, 3);
var maxRate = Test.Channels.Select(ch => ch.ParentModule.SampleRateHz).Max();
var numSamples = (Stop - Start) * maxRate;
//the way the dataform handles ticks is a little weird, it expects an uint below and can only handle single tick
//progress updates, despite saying "percentage", so this is just a safety check for the case of large number of samples
//it's probably won't be needed.
if (numSamples * 2D / 100 > int.MaxValue) { IncrementLevel = 10000; }
else if (numSamples < 10000) { IncrementLevel = 10; }//also probably unlikely, but in the case of
//a huge number of channels and not many samples, it'd be nice to see something
else { IncrementLevel = 1000; }
var ticksNeeded = 4D + 2D * numSamples / IncrementLevel;
onBeginEvent?.Invoke(this, Convert.ToUInt32(ticksNeeded));
DoExport(false, pathname, Start, Stop, AllowTTSExportFiltering);
}
catch (System.Exception ex) { throw ex; }
finally
{
onTickEvent?.Invoke(this, 100D);
onEndEvent?.Invoke(this);
}
}
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)
{
}
private TickEventHandler _onTickEvent;
public void IncrementDone(double amount)
{
_onTickEvent(this, amount);
}
private void DoExport(bool bFiltered, string pathname, double start, double stop, bool allowTTSFilteredExport)
{
var format = "{0}{1}.csv";
var fileName = System.IO.Path.Combine(pathname, Test.Id);
if (1 != SubSampleInterval)
{
fileName += "_Subsampled";
}
fileName = string.Format(format, fileName, ExtensionPrefix ?? "");
System.IO.TextWriter tw = null;
try
{
Encoding encoder = new UTF8Encoding(true);
try
{
if (DefaultEncoding != Encoding.UTF8.CodePage)
{
encoder = Common.Utils.FileUtils.GetEncoding(DefaultEncoding);
}
}
catch (System.Exception ex)
{
APILogger.Log("Problem getting encoding", ex);
encoder = Encoding.Default;
}
tw = new System.IO.StreamWriter(fileName, false, encoder);
var th = new TestHeader();
var ch = new ChannelHeader();
var cd = new ChannelData();
var bFilterThisExport = bFiltered && allowTTSFilteredExport;
cd.GenerateChannelData(this, tw, bFilterThisExport, start, stop, SubSampleInterval, out ulong practicalNumSamples, out ulong numSamples, out Test test, out DataScaler[] scalers, out double sampleRate,
out List<short[]> ChannelDataUnFiltered, out List<double[]> ChannelDataFiltered, out int preTriggerSamples);
th.WriteTestHeader(this, tw, bFilterThisExport, SubSampleInterval, Math.Min(numSamples, practicalNumSamples), preTriggerSamples);
ch.WriteChannelHeaderToString(this, tw, bFilterThisExport, start, stop);
cd.WriteChannelData(this, tw, bFilterThisExport, start, stop, SubSampleInterval, practicalNumSamples, numSamples, test, scalers, sampleRate, ChannelDataUnFiltered,
ChannelDataFiltered);
}
catch (System.Exception ex)
{
APILogger.Log("Exception in DoExport, ", ex);
throw ex;
}
finally
{
if (null != tw)
{
try
{
tw.Flush();
tw.Close();
tw.Dispose();
}
catch (System.Exception ex)
{
APILogger.Log("Exception in DoExport cleanup, ", ex);
}
tw = null;
}
}
}
}
}