907 lines
46 KiB
C#
907 lines
46 KiB
C#
/*
|
||
* Diadem.File.Writer.cs
|
||
*
|
||
* Copyright © 2009
|
||
* Diversified Technical Systems, Inc.
|
||
* All Rights Reserved
|
||
*/
|
||
|
||
using System;
|
||
using System.Globalization;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using DTS.Common.DAS.Concepts;
|
||
using DTS.Common.Utilities.Logging;
|
||
using DTS.Common.ISO;
|
||
using DTS.Common.Enums.Sensors;
|
||
|
||
namespace DTS.Serialization.RDF
|
||
{
|
||
// *** see Diadem.File.cs ***
|
||
public partial class File
|
||
{
|
||
///
|
||
/// <summary>
|
||
/// Utility object for serializing <see cref="DTS.Serialization.Test"/>s to disk
|
||
/// in the Diadem
|
||
/// </summary>
|
||
///
|
||
public class Writer : Writer<File>, IWriter<Test>
|
||
{
|
||
public File WriterParent { get; set; }
|
||
|
||
public TestPlan TestPlan { get; set; }
|
||
public string ExtensionPrefix { get; set; } = string.Empty;
|
||
/// <summary>
|
||
/// Initialize an instance of the Diadem.File.Writer class.
|
||
/// </summary>
|
||
///
|
||
/// <param name="fileType">
|
||
/// The associated <see cref="DTS.Serialization.Diadem.File"/> object.
|
||
/// </param>
|
||
/// <param name="encoding"></param>
|
||
internal Writer(File fileType, int encoding)
|
||
: base(fileType, encoding)
|
||
{
|
||
}
|
||
|
||
/// <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="id"></param>
|
||
/// <param name="test">
|
||
/// The <see cref="DTS.Serialization.Test"/> to be written out.
|
||
/// </param>
|
||
/// <param name="bFiltering"></param>
|
||
/// <param name="includeGroupNameInISOExport"></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>
|
||
/// Write the representation file/files of the specified DTS.Serialization.Test
|
||
/// at the given pathname.
|
||
/// </summary>
|
||
/// <param name="pathname"></param>
|
||
/// <param name="id"></param>
|
||
/// <param name="dataFolder"></param>
|
||
/// <param name="test"></param>
|
||
/// <param name="bFiltering"></param>
|
||
/// <param name="includeGroupNameInISOExport"></param>
|
||
/// <param name="fd"></param>
|
||
/// <param name="tmChannel"></param>
|
||
/// <param name="channelNumber"></param>
|
||
/// <param name="beginEventHandler"></param>
|
||
/// <param name="cancelEventHandler"></param>
|
||
/// <param name="endEventHandler"></param>
|
||
/// <param name="tickEventHandler"></param>
|
||
/// <param name="errorEventHandler"></param>
|
||
/// <param name="cancelRequested"></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)
|
||
{
|
||
Exception exception = null;
|
||
try
|
||
{
|
||
if (!Directory.Exists(pathname))
|
||
Directory.CreateDirectory(pathname);
|
||
|
||
var idBad = !id.ToLower().Contains(SUFFIX_CHECKOUT.ToLower()) &&
|
||
!id.ToLower().Contains(SUFFIX_RUNTEST.ToLower());
|
||
if (null != test && !string.IsNullOrEmpty(test.Id) && idBad)
|
||
{
|
||
//don't use that id, use the one in test
|
||
//http://manuscript.dts.local/f/cases/36769/RDF-Export-is-not-named-correctly
|
||
id = test.Id;
|
||
}
|
||
//FB8419 - RDF files must contain an 8char test name without "runtest" or "checkout"
|
||
if (id.ToLower().EndsWith(SUFFIX_CHECKOUT.ToLower()))
|
||
{
|
||
id = id.Substring(0, id.Length - SUFFIX_CHECKOUT.Length);
|
||
}
|
||
else if (id.ToLower().EndsWith(SUFFIX_RUNTEST.ToLower()))
|
||
{
|
||
id = id.Substring(0, id.Length - SUFFIX_RUNTEST.Length);
|
||
}
|
||
//id = id.PadRight(MAX_TEST_NAME_LENGTH); //FB8418 - JCL file names must be 8 characters long. No longer/shorter
|
||
|
||
var filename = Path.Combine(pathname, id + (ExtensionPrefix ?? "") + Extension);
|
||
using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
|
||
{
|
||
using (var fileWriter = new BinaryWriter(fs))
|
||
{
|
||
beginEventHandler?.Invoke(this, 1);
|
||
|
||
WriteTestInfo(fileWriter, test, id, tickEventHandler, dataFolder);
|
||
WriteChannelInfo(fileWriter, test, id, tickEventHandler, dataFolder, cancelRequested,
|
||
pathname, TestPlan);
|
||
|
||
tickEventHandler?.Invoke(this, 100.0);
|
||
endEventHandler?.Invoke(this);
|
||
}
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
exception = new Exception("encountered problem writing RDF test files", ex);
|
||
APILogger.Log("encountered problem writing RDF test files", ex);
|
||
}
|
||
if (null != errorEventHandler && null != exception && null != endEventHandler)
|
||
{
|
||
endEventHandler(this);
|
||
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)
|
||
{
|
||
}
|
||
|
||
public bool UseIsoCodeForDiadem200 { get; set; }
|
||
public bool UseZeroForUnfiltered { get; set; }
|
||
public bool FilteredExport { get; set; }
|
||
|
||
// ReSharper disable once UnusedParameter.Local
|
||
private void CreateLinearizedData(Test.Module.Channel currentChannel, string dataFolder, long startSample, long endSample)
|
||
{
|
||
var sChnName = currentChannel.PersistentChannelInfo.Filename;
|
||
|
||
var sNewFileName = sChnName.Replace(".chn", ".lin");
|
||
|
||
var sb = new StringBuilder();
|
||
|
||
var data = GetEUData(currentChannel);
|
||
if (endSample >= data.Length)
|
||
{
|
||
endSample = data.Length - 1;
|
||
}
|
||
for (var i = startSample; i <= endSample; i++)
|
||
{
|
||
sb.Append(data[i].ToString(CultureInfo.InvariantCulture));
|
||
if (i < endSample)
|
||
{
|
||
sb.Append(CultureInfo.InvariantCulture.TextInfo.ListSeparator);
|
||
}
|
||
sb.AppendLine();
|
||
}
|
||
System.IO.File.WriteAllText(sNewFileName, sb.ToString(), Encoding.ASCII);
|
||
}
|
||
|
||
//this commented out function was used during testing for 3D ir-tracc, it creates CSV files for all channels in the test with
|
||
//several key values, which makes it useful for debugging data set issues.
|
||
/*private void Create3DIRTraccDataCSV(Test.Module.Channel CurrentChannel,
|
||
string dataFolder,
|
||
DataScaler scaler)
|
||
{
|
||
// renable code to produce a nice CSV with ADC and the variables needed to calculate EU
|
||
string sChnName = CurrentChannel.PersistentChannelInfo.Filename;
|
||
string newFileName2 = sChnName.Replace(".chn", ".csv").Replace(".cchn", ".csv");
|
||
|
||
if (System.IO.File.Exists(newFileName2))
|
||
{
|
||
System.IO.File.Delete(newFileName2);
|
||
}
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
sb.AppendFormat("{0},{1}", CurrentChannel.ChannelDescriptionString, CurrentChannel.ChannelName2);
|
||
|
||
sb.AppendLine();
|
||
sb.AppendFormat("Sensitivity,Excitation,LinearizationExponent,ZeroPosition,Intercept");
|
||
sb.AppendLine();
|
||
if (CurrentChannel is Test.Module.AnalogInputChannel)
|
||
{
|
||
var aic = CurrentChannel as Test.Module.AnalogInputChannel;
|
||
if (aic.LinearizationFormula.IsValid() &&
|
||
aic.LinearizationFormula.NonLinearStyle == Common.DAS.Concepts.NonLinearStyles.IRTraccCalFactor)
|
||
{
|
||
sb.Append(aic.LinearizationFormula.CalibrationFactor);
|
||
}
|
||
else
|
||
{
|
||
sb.Append(aic.Sensitivity);
|
||
}
|
||
sb.Append(",");
|
||
|
||
sb.Append(aic.FactoryExcitationVoltage);
|
||
sb.Append(",");
|
||
if (aic.LinearizationFormula.IsValid())
|
||
{
|
||
sb.Append(aic.LinearizationFormula.LinearizationExponent);
|
||
}
|
||
else
|
||
{
|
||
sb.Append("0");
|
||
}
|
||
sb.Append(",");
|
||
|
||
sb.Append(aic.ZeroPoint);
|
||
sb.Append(",");
|
||
|
||
if (aic.LinearizationFormula.IsValid() &&
|
||
aic.LinearizationFormula.NonLinearStyle == Common.DAS.Concepts.NonLinearStyles.IRTraccCalFactor)
|
||
{
|
||
sb.Append(aic.LinearizationFormula.Intercept);
|
||
}
|
||
else
|
||
{
|
||
sb.Append("0");
|
||
}
|
||
|
||
sb.AppendLine();
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine();
|
||
}
|
||
|
||
sb.AppendLine("ADC, mV, EU");
|
||
|
||
for (int i = 0; i < CurrentChannel.PersistentChannelInfo.Data.Length; i++)
|
||
{
|
||
if (0 == i % 10)
|
||
{
|
||
sb.AppendFormat("{0},{1},{2}", CurrentChannel.PersistentChannelInfo.Data[i],
|
||
scaler.GetMv(CurrentChannel.PersistentChannelInfo.Data[i]),
|
||
scaler.GetEU(CurrentChannel.PersistentChannelInfo.Data[i]));
|
||
sb.AppendLine();
|
||
}
|
||
}
|
||
System.IO.File.AppendAllText(newFileName2, sb.ToString());
|
||
}*/
|
||
|
||
/// <summary>
|
||
/// this is a function to output CSV data for non linear channels
|
||
/// it's used for debugging non linear output.
|
||
/// </summary>
|
||
/// <param name="currentChannel"></param>
|
||
/// <param name="dataFolder"></param>
|
||
/// <param name="scaler"></param>
|
||
// ReSharper disable once UnusedParameter.Local
|
||
private static void CreateLinearizedDataCSV(Test.Module.Channel currentChannel, string dataFolder,
|
||
DataScaler scaler, long startSample, long endSample)
|
||
{
|
||
try
|
||
{
|
||
/* re-enable code to produce a nice CSV with ADC and the variables needed to calculate EU*/
|
||
var sChnName = currentChannel.PersistentChannelInfo.Filename;
|
||
var newFileName2 = sChnName.Replace(".chn", ".csv");
|
||
|
||
if (System.IO.File.Exists(newFileName2))
|
||
{
|
||
System.IO.File.Delete(newFileName2);
|
||
}
|
||
//for comparison reasons it can be useful to normalize your mV using a specific target
|
||
//this code arranges for the first mV to match your target.
|
||
var oldzeroMvinADC = scaler.ZeroMvInADC;
|
||
var oldmV = scaler.GetMv(currentChannel.PersistentChannelInfo.Data[0]);
|
||
var deltamV = 61.5D - oldmV;
|
||
var deltamVinADC = deltamV / scaler.GetScaleFactorMv();
|
||
var newzeromvinADC = scaler.ZeroMvInADC - deltamVinADC;
|
||
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine(
|
||
"ScaleFactorMV,Excitation,ZeromVInADCOriginal,ZeromVInADCAdjusted,DeltaInADC,DeltaInmV");
|
||
sb.AppendFormat("{0},{1},{2},{3},{4},{5}", scaler.GetAdcToMvScalingFactor(),
|
||
scaler.FactoryExcitationVoltage,
|
||
oldzeroMvinADC,
|
||
newzeromvinADC,
|
||
deltamVinADC,
|
||
deltamV);
|
||
sb.AppendLine();
|
||
sb.AppendLine("ADC, mV, EU");
|
||
|
||
if (endSample >= currentChannel.PersistentChannelInfo.Data.Length)
|
||
{
|
||
endSample = currentChannel.PersistentChannelInfo.Data.Length - 1;
|
||
}
|
||
for (var i = startSample; i <= endSample; i++)
|
||
{
|
||
if (0 != i % 10) continue;
|
||
sb.AppendFormat("{0},{1},{2}", currentChannel.PersistentChannelInfo.Data[i],
|
||
scaler.GetMv(currentChannel.PersistentChannelInfo.Data[i]),
|
||
scaler.GetEU(currentChannel.PersistentChannelInfo.Data[i]));
|
||
sb.AppendLine();
|
||
}
|
||
System.IO.File.AppendAllText(newFileName2, sb.ToString());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
}
|
||
|
||
// ReSharper disable once UnusedParameter.Local
|
||
private void CreateDigitizedData(Test.Module.Channel currentChannel, string dataFolder, long startSample,
|
||
long endSample)
|
||
{
|
||
try
|
||
{
|
||
var sChnName = currentChannel.PersistentChannelInfo.Filename;
|
||
|
||
var sNewFileName = sChnName.Replace(".chn", ".dig");
|
||
|
||
var sb = new StringBuilder();
|
||
|
||
var data = GetEUData(currentChannel);
|
||
if (endSample >= data.Length)
|
||
{
|
||
endSample = data.Length - 1;
|
||
}
|
||
|
||
for (var i = startSample; i <= endSample; i++)
|
||
{
|
||
sb.Append(data[i].ToString(CultureInfo.InvariantCulture));
|
||
if (i < endSample - 1L)
|
||
{
|
||
sb.Append(CultureInfo.InvariantCulture.TextInfo.ListSeparator);
|
||
}
|
||
sb.AppendLine();
|
||
}
|
||
|
||
System.IO.File.WriteAllText(sNewFileName, sb.ToString(), Encoding.ASCII);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
}
|
||
|
||
private double[] GetEUData(Test.Module.Channel channel)
|
||
{
|
||
return WriterParent.GetEUData(channel.ChannelId).Data;
|
||
}
|
||
|
||
|
||
protected void WriteTestInfo(
|
||
BinaryWriter fileWriter,
|
||
Test test,
|
||
string testId,
|
||
TickEventHandler tickEventHandler,
|
||
string dataFolder)
|
||
{
|
||
var emptyBuffer = Enumerable.Repeat(' ', 80).ToArray();
|
||
|
||
fileWriter.Write(
|
||
testId.PadRight(MAX_TEST_NAME_LENGTH, ' ').Substring(0, MAX_TEST_NAME_LENGTH).ToCharArray());
|
||
fileWriter.Write("1".PadLeft(3, ' ').ToCharArray()); // Number of files
|
||
fileWriter.Write(" 1".ToCharArray()); // File number always 1
|
||
fileWriter.Write(test.Channels.Count.ToString().PadLeft(3, ' ').ToCharArray()); // Number of channels
|
||
fileWriter.Write(test.InceptionDate.Month.ToString("00").ToCharArray());
|
||
fileWriter.Write(test.InceptionDate.Day.ToString("00").ToCharArray());
|
||
fileWriter.Write(test.InceptionDate.Year.ToString().Substring(2, 2).ToCharArray());
|
||
fileWriter.Write(test.InceptionDate.Hour.ToString("00").ToCharArray());
|
||
fileWriter.Write(test.InceptionDate.Minute.ToString("00").ToCharArray());
|
||
fileWriter.Write(emptyBuffer, 0, 53); // Complete the 80 byte record
|
||
}
|
||
|
||
private static int DisplayOrderComparer(object x, object y)
|
||
{
|
||
var channelX = (Test.Module.Channel)x;
|
||
var channelY = (Test.Module.Channel)y;
|
||
|
||
if (channelX.AbsoluteDisplayOrder > channelY.AbsoluteDisplayOrder)
|
||
{
|
||
return 1;
|
||
}
|
||
if (channelX.AbsoluteDisplayOrder < channelY.AbsoluteDisplayOrder)
|
||
{
|
||
return -1;
|
||
}
|
||
return channelX.ToString().CompareTo(channelY.ToString());
|
||
}
|
||
|
||
/// <summary>
|
||
/// returns a gm formatted 5 character string for a rack serial number
|
||
/// for now it takes the last 5 characters and pads as necessary
|
||
///
|
||
/// 2019-09-16
|
||
/// 14823 DP vs TDC TDAS PRO Rack Naming Consistency
|
||
/// modified to accept baseserialnumber as well as function was outputting module serial numbers with TDAS
|
||
/// </summary>
|
||
/// <param name="serialNumber"></param>
|
||
/// <param name="baseSerialNumber"></param>
|
||
/// <returns></returns>
|
||
private static string GetRackSerialNumber(string serialNumber, string baseSerialNumber)
|
||
{
|
||
var sn = string.IsNullOrWhiteSpace(baseSerialNumber) ? serialNumber : baseSerialNumber;
|
||
if (string.IsNullOrWhiteSpace(sn))
|
||
{
|
||
return "EMPTY";
|
||
}
|
||
return sn.Length >= 4 ? sn.Substring(sn.Length - 4, 4).PadLeft(5) : sn.PadLeft(5, ' ');
|
||
}
|
||
|
||
private static readonly double MidpointDeflection = Math.Floor(0.7D * Common.Constants.ADC_MIDPOINT);
|
||
|
||
private static bool IsSlice(string sn)
|
||
{
|
||
return sn.StartsWith("SPS") || sn.StartsWith("SPT") || sn.StartsWith("SPD") || sn.StartsWith("BA") ||
|
||
sn.StartsWith("SLT") || sn.StartsWith("SLS") || sn.StartsWith("SLD");
|
||
}
|
||
|
||
private bool HasNaturallyInvertedScaleFactor(Test.Module.Channel channel)
|
||
{
|
||
var serialNumberStart = channel.ParentModule.SerialNumber.Substring(0, 3);
|
||
serialNumberStart = serialNumberStart.ToUpper();
|
||
if (serialNumberStart != "SPS" && serialNumberStart != "SLS") return false;
|
||
|
||
if (!(channel is Test.Module.AnalogInputChannel)) return false;
|
||
var aic = channel as Test.Module.AnalogInputChannel;
|
||
if (aic.LinearizationFormula.IsValid())
|
||
{
|
||
return false;
|
||
}
|
||
return !aic.IsDigital();
|
||
}
|
||
|
||
/// <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="testId"></param>
|
||
/// <param name="tickEventHandler"></param>
|
||
/// <param name="dataFolder"></param>
|
||
/// <param name="cancelRequested"></param>
|
||
/// <param name="pathName"></param>
|
||
/// <param name="testPlan"></param>
|
||
///
|
||
protected void WriteChannelInfo(
|
||
BinaryWriter fileWriter,
|
||
Test test,
|
||
string testId,
|
||
TickEventHandler tickEventHandler,
|
||
string dataFolder,
|
||
CancelRequested cancelRequested,
|
||
string pathName,
|
||
TestPlan testPlan)
|
||
{
|
||
try
|
||
{
|
||
var emptyBuffer = Enumerable.Repeat(' ', 160).ToArray();
|
||
var channels = test.Channels;
|
||
channels.Sort(DisplayOrderComparer);
|
||
|
||
var dMaxStart = double.MinValue;
|
||
var dMinEnd = double.MaxValue;
|
||
|
||
//find the subset of time from all channels, this is the max time data starts between all channels and the min time the data ends between all channels
|
||
//10132 [Zendesk] Trim RDF exports so all channels from all DAS have same totalsample and triggersample
|
||
//there is a hack here, in the above case I noticed TotalSamplesRecorded = 40k, but TriggerSampleNumber = 1,xxx,xxx meaning you would have
|
||
//never have seen a trigger in the first point ... I don't think this is expected ... I hacked around this case to since the RDF export is very contained
|
||
//and only used by one customer, while changing the totalsamplesrecorded might not be
|
||
foreach (var channel in channels)
|
||
{
|
||
var dStart = (channel.ParentModule.StartRecordSampleNumber -
|
||
(double)channel.ParentModule.TriggerSampleNumbers[0]) /
|
||
channel.ParentModule.SampleRateHz;
|
||
dMaxStart = Math.Max(dMaxStart, dStart);
|
||
|
||
var dEnd = ((double)channel.ParentModule.NumberOfSamples -
|
||
channel.ParentModule.TriggerSampleNumbers[0] +
|
||
channel.ParentModule.StartRecordSampleNumber - 1D) / channel.ParentModule.SampleRateHz;
|
||
|
||
if (channel.ParentModule.NumberOfSamples > channel.ParentModule.TriggerSampleNumbers[0])
|
||
{
|
||
dEnd = (channel.ParentModule.NumberOfSamples -
|
||
(double)channel.ParentModule.TriggerSampleNumbers[0]) /
|
||
channel.ParentModule.SampleRateHz;
|
||
}
|
||
dMinEnd = Math.Min(dMinEnd, dEnd);
|
||
}
|
||
for (var iChannelIdx = 0; iChannelIdx < channels.Count; iChannelIdx++)
|
||
{
|
||
var currentChannel = channels[iChannelIdx];
|
||
var dStart = (currentChannel.ParentModule.StartRecordSampleNumber -
|
||
(double)currentChannel.ParentModule.TriggerSampleNumbers[0]) /
|
||
currentChannel.ParentModule.SampleRateHz;
|
||
var dEnd = ((double)currentChannel.ParentModule.NumberOfSamples -
|
||
currentChannel.ParentModule.TriggerSampleNumbers[0] +
|
||
currentChannel.ParentModule.StartRecordSampleNumber - 1D) / currentChannel.ParentModule.SampleRateHz;
|
||
|
||
if (currentChannel.ParentModule.NumberOfSamples > currentChannel.ParentModule.TriggerSampleNumbers[0])
|
||
{
|
||
dEnd = (currentChannel.ParentModule.NumberOfSamples -
|
||
(double)currentChannel.ParentModule.TriggerSampleNumbers[0]) /
|
||
currentChannel.ParentModule.SampleRateHz;
|
||
}
|
||
|
||
var startSample = Convert.ToInt64((dMaxStart - dStart) * currentChannel.ParentModule.SampleRateHz);
|
||
var endSample = startSample + Convert.ToInt64((dMinEnd - dMaxStart) * currentChannel.ParentModule.SampleRateHz);
|
||
|
||
var bHasNaturallyInvertedScaleFactor = HasNaturallyInvertedScaleFactor(currentChannel);
|
||
// Channel #
|
||
fileWriter.Write((1 + iChannelIdx).ToString().PadLeft(5).ToCharArray());
|
||
|
||
// DAS Channel #.
|
||
// This entry is simply the integer number representing the channel on the Sensor Input Module (SIM).
|
||
// As with the .ISF file format, the Timed Output Module (TOM) has two channels associated with it.
|
||
// One channel is the TOM Indication, which is the measure of the signal of TOM command to deploy an SIR.
|
||
// The other channel is TOM Current, which measures the output current while deploying the SIR.
|
||
// The channel number for a TOM begins with the channel number on the TOM. The channel number
|
||
// is then followed by an ‘I’ or ‘C’. The ‘I’ represents channel indication and the ‘C’ indicates
|
||
// current. G5 and TDAS Pro systems have a DAS Channel Number from 1-8 for the channel number field.
|
||
// The DAS Rack Number and the DAS Module Number are required for DTS systems to setup. Barrier,
|
||
// Hydraulic Sled, and Rollover are examples of sites that use this format. Slice systems have a DAS
|
||
// Channel Number from 1-18.
|
||
|
||
var supposedChannelNumber = 1 + currentChannel.Number;
|
||
var channel = currentChannel as Test.Module.AnalogInputChannel;
|
||
if (channel != null && channel.IsSquib())
|
||
{
|
||
//0 = init, 1 = current
|
||
var decorator = "I";
|
||
if (0 == supposedChannelNumber % 2)
|
||
{
|
||
decorator = "C";
|
||
supposedChannelNumber--;
|
||
}
|
||
var text = string.Format(" {0}{1}", supposedChannelNumber.ToString()[0], decorator);
|
||
fileWriter.Write(text.ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
if (IsSlice(currentChannel.ParentModule.SerialNumber))
|
||
{
|
||
//slice are in the dts file by module, so SPS00041 0,1,2 SPS00041 0,1,2
|
||
//but we want it in DAS order (1-18), so we hack and convert for SLICE.
|
||
supposedChannelNumber += currentChannel.ParentModule.Number * 3;
|
||
}
|
||
fileWriter.Write(supposedChannelNumber.ToString()
|
||
.PadLeft(2, '0')
|
||
.PadLeft(4, ' ')
|
||
.ToCharArray());
|
||
}
|
||
|
||
fileWriter.Write(" ".ToCharArray()); // DAS Sub channel, not used
|
||
fileWriter.Write(" 0".ToCharArray());
|
||
// Number of scaling samples in data. 0, scaling is explicit
|
||
var totalSamplesHomogenized =
|
||
Convert.ToInt64((dMinEnd - dMaxStart) * channel.ParentModule.SampleRateHz);
|
||
|
||
fileWriter.Write((1L - 2L + totalSamplesHomogenized).ToString().PadLeft(10).ToCharArray());
|
||
// Pre trigger samples.
|
||
var preTrigger = Convert.ToInt64(Math.Abs(dMaxStart) *
|
||
currentChannel.ParentModule.SampleRateHz);
|
||
fileWriter.Write(preTrigger.ToString().PadLeft(10).ToCharArray());
|
||
fileWriter.Write(currentChannel.ParentModule.SampleRateHz.ToString("+0.000E+000").ToCharArray());
|
||
|
||
// All ADC values are 0-65,536. Several uses of +32767 to center the value.
|
||
// Data zero
|
||
var dataZeroLevelADC = currentChannel.DataZeroLevelAdc;
|
||
var currentAnalogChannel = currentChannel as Test.Module.AnalogInputChannel;
|
||
|
||
if (bHasNaturallyInvertedScaleFactor)
|
||
{
|
||
dataZeroLevelADC *= -1;
|
||
}
|
||
if (null != currentAnalogChannel && currentAnalogChannel.Bridge ==
|
||
SensorConstants.BridgeType.DigitalInput)
|
||
{
|
||
//18142 digital input, just set ADC zero level
|
||
fileWriter.Write(dataZeroLevelADC.ToString(CultureInfo.InvariantCulture).PadLeft(6, ' ').ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
fileWriter.Write((dataZeroLevelADC + DTS.Common.Constants.ADC_MIDPOINT).ToString(CultureInfo.InvariantCulture).PadLeft(6, ' ').ToCharArray());
|
||
}
|
||
//Cal point 1 is always 0
|
||
fileWriter.Write("+0.000E+000".ToCharArray());
|
||
|
||
if (currentChannel is Test.Module.AnalogInputChannel && (currentChannel as Test.Module.AnalogInputChannel).LinearizationFormula.IsValid())
|
||
{
|
||
CreateLinearizedData(currentChannel, dataFolder, startSample, endSample); //Temp place holder to output lin files
|
||
}
|
||
else if (currentChannel is Test.Module.AnalogInputChannel && (currentChannel as Test.Module.AnalogInputChannel).IsDigital())
|
||
{
|
||
CreateDigitizedData(currentChannel, dataFolder, startSample, endSample);
|
||
}
|
||
|
||
//Cal point 2 is the voltage insertion deflection, or 70% of the range in ADC
|
||
if (null != currentAnalogChannel && currentAnalogChannel.Bridge ==
|
||
SensorConstants.BridgeType.DigitalInput)
|
||
{
|
||
//18142 except for digital input, where cal point 2 is one louder
|
||
// see note in digital input loop below regarding on/off routine
|
||
fileWriter.Write((dataZeroLevelADC + 1).ToString(CultureInfo.InvariantCulture).PadLeft(6, ' ').ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
fileWriter.Write(
|
||
(DTS.Common.Constants.ADC_MIDPOINT + MidpointDeflection).ToString(CultureInfo.InvariantCulture)
|
||
.PadLeft(6, ' ')
|
||
.ToCharArray());
|
||
}
|
||
|
||
var scaler = new DataScaler
|
||
{
|
||
IsInverted = currentAnalogChannel.IsInverted,
|
||
UnitConversion = currentAnalogChannel.UnitConversion,
|
||
BasedOnOutputAtCapacity = currentAnalogChannel.AtCapacity,
|
||
CapacityOutputIsBasedOn = currentAnalogChannel.CapacityOutputIsBasedOn,
|
||
SensitivityUnits = currentAnalogChannel.SensitivityUnits,
|
||
Multiplier = currentAnalogChannel.Multiplier
|
||
};
|
||
scaler.SetLinearizationFormula(currentAnalogChannel.LinearizationFormula);
|
||
scaler.SetScaleFactorMv(currentChannel.Data.ScaleFactorMv);
|
||
scaler.SetScaleFactorEU(currentChannel.Data.ScaleFactorEU);
|
||
scaler.SetUseEUScaleFactors(currentChannel.Data.UseEUScaleFactors);
|
||
if (currentChannel is Test.Module.AnalogInputChannel)
|
||
{
|
||
scaler.IEPE = (currentChannel as Test.Module.AnalogInputChannel).Bridge ==
|
||
SensorConstants.BridgeType.IEPE;
|
||
}
|
||
scaler.SetMvPerEu(currentChannel.Data.MvPerEu);
|
||
try
|
||
{
|
||
scaler.SetInitialOffset(currentAnalogChannel.InitialOffset);
|
||
scaler.ZeroMethodType = currentAnalogChannel.ZeroMethod;
|
||
scaler.SetRemovedADC(currentAnalogChannel.RemovedADC);
|
||
scaler.SetRemovedInternalADC(currentAnalogChannel.RemovedInternalADC);
|
||
scaler.SetDataZeroLevelADC(currentAnalogChannel.DataZeroLevelAdc);
|
||
scaler.SetZeroMvInADC(currentAnalogChannel.ZeroMvInADC);
|
||
try
|
||
{
|
||
scaler.SetWindowAverageADC(currentAnalogChannel.WindowAverageADC);
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
|
||
scaler.NominalExcitationVoltage = currentAnalogChannel.ExcitationVoltage;
|
||
if (currentAnalogChannel.MeasuredExcitationVoltageValid)
|
||
{
|
||
try
|
||
{
|
||
scaler.MeasuredExcitationVoltage = currentAnalogChannel.MeasuredExcitationVoltage;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
}
|
||
if (currentAnalogChannel.FactoryExcitationVoltageValid)
|
||
{
|
||
try
|
||
{
|
||
scaler.FactoryExcitationVoltage = currentAnalogChannel.FactoryExcitationVoltage;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
APILogger.Log(ex);
|
||
}
|
||
}
|
||
|
||
scaler.ProportionalToExcitation = currentAnalogChannel.ProportionalToExcitation;
|
||
|
||
double[] nonLinearEUData = null;
|
||
var nonLinearScaleFactor = 1D;
|
||
//Cal point 2 in EU
|
||
if (null != currentAnalogChannel && currentAnalogChannel.IsSquib())
|
||
{
|
||
var d = Math.Abs((MidpointDeflection - scaler.GetDataZeroLevelADC()) *
|
||
scaler.GetAdcToEuScalingFactor());
|
||
var dStr = d.ToString("+0.00000E+0;-0.00000E+0");
|
||
fileWriter.Write(dStr.ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
if (null != currentAnalogChannel && currentAnalogChannel.Bridge ==
|
||
SensorConstants.BridgeType.DigitalInput)
|
||
{
|
||
//digital input, use 1ADC = 5EU?
|
||
fileWriter.Write(5D.ToString("+0.000E+000;-0.000E+000").ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
if (null != currentAnalogChannel && currentAnalogChannel.LinearizationFormula.IsValid())
|
||
{
|
||
nonLinearEUData = GetEUData(currentChannel);
|
||
var max = nonLinearEUData.Max();
|
||
var min = nonLinearEUData.Min();
|
||
|
||
max = Math.Max(Math.Abs(max), Math.Abs(min));
|
||
var biPolar = min < 0 && max > 0;
|
||
if (biPolar)
|
||
{
|
||
max = max * 2;
|
||
}
|
||
//we need twice the space to handle both the negative and positive values
|
||
nonLinearScaleFactor = max / Common.Constants.ADC_MIDPOINT;
|
||
fileWriter.Write(
|
||
(MidpointDeflection * nonLinearScaleFactor).ToString(
|
||
"+0.000E+000;-0.000E+000").ToCharArray());
|
||
}
|
||
else
|
||
{
|
||
var dScaleFactor = scaler.GetAdcToEuScalingFactor() * scaler.Multiplier;
|
||
if (bHasNaturallyInvertedScaleFactor)
|
||
{
|
||
dScaleFactor *= -1;
|
||
}
|
||
fileWriter.Write(
|
||
(MidpointDeflection * dScaleFactor).ToString(
|
||
"+0.000E+000;-0.000E+000").ToCharArray());
|
||
}
|
||
}
|
||
}
|
||
|
||
// Resolution
|
||
fileWriter.Write(" 16".ToCharArray());
|
||
// "rack" serial number
|
||
fileWriter.Write(GetRackSerialNumber(currentChannel.ParentModule.SerialNumber, currentChannel.ParentModule.BaseSerialNumber).ToCharArray());
|
||
|
||
// "module" number
|
||
fileWriter.Write(
|
||
(1 + currentChannel.ParentModule.Number).ToString().PadLeft(2, ' ').ToCharArray());
|
||
// Padding
|
||
fileWriter.Write(emptyBuffer, 0, 63);
|
||
|
||
|
||
// Write ADC data centered at 32767
|
||
if (!(currentChannel is Test.Module.AnalogInputChannel)) continue;
|
||
//ADC DIM data is -/+ rail, convert it to 0,1, then center around 32767
|
||
if ((currentChannel as Test.Module.AnalogInputChannel).Bridge ==
|
||
SensorConstants.BridgeType.DigitalInput)
|
||
{
|
||
scaler.SetDigitalMultiplier(
|
||
(currentChannel as Test.Module.AnalogInputChannel)
|
||
.DigitalMultiplier);
|
||
scaler.Digital = true;
|
||
scaler.DigitalMode =
|
||
(currentChannel as Test.Module.AnalogInputChannel).DigitalMode;
|
||
|
||
// adjusted to remove 2 samples to be consistent with TDC RDF exports
|
||
// TDC appears to be miscounting by 1 sample, developing 1 sample too few
|
||
// DataPRO download appears to download 1 extra sample, so to be consistent with TDC, remove 2 samples...
|
||
for (var iPt = startSample; iPt <= endSample - 2; iPt++)
|
||
{
|
||
var dataPoint = currentChannel.PersistentChannelInfo.Data[iPt];
|
||
//note that this is an arbitrary over the threshold = on, below = off
|
||
//more than likely we need to actually look at the input mode to make sense of
|
||
//the information we are reading (CCNO/CCNC/LowToHigh/HighToLow)
|
||
//the mode is accessible via (CurrentChannel as Test.Module.AnalogInputChannel).DigitalMode
|
||
var value = dataZeroLevelADC;
|
||
var eu = scaler.GetEU(dataPoint);
|
||
|
||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||
if (eu != 0)
|
||
{
|
||
value += 1;
|
||
}
|
||
var byte2 = (byte)(value >> 8);
|
||
var byte1 = (byte)(value & 0xFF);
|
||
fileWriter.Write(byte1);
|
||
fileWriter.Write(byte2);
|
||
}
|
||
}
|
||
else if (null != currentAnalogChannel && currentAnalogChannel.LinearizationFormula.IsValid())
|
||
{
|
||
CreateLinearizedDataCSV(currentChannel, dataFolder, scaler, startSample, endSample);
|
||
// adjusted to remove 2 samples to be consistent with TDC RDF exports
|
||
// TDC appears to be miscounting by 1 sample, developing 1 sample too few
|
||
// DataPRO download appears to download 1 extra sample, so to be consistent with TDC, remove 2 samples...
|
||
for (var iPt = startSample; iPt <= endSample - 2; iPt++)
|
||
{
|
||
var dValue = nonLinearEUData[iPt];
|
||
var adc = double.IsNaN(dValue / nonLinearScaleFactor) ? 0 : dValue / nonLinearScaleFactor;
|
||
|
||
var value = (ushort)(Convert.ToInt16(adc) + DTS.Common.Constants.ADC_MIDPOINT);
|
||
var byte2 = (byte)(value >> 8);
|
||
var byte1 = (byte)(value & 0xFF);
|
||
fileWriter.Write(byte1);
|
||
fileWriter.Write(byte2);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (bHasNaturallyInvertedScaleFactor && currentChannel.ZeroMvInADC != 0)
|
||
{
|
||
currentChannel.ZeroMvInADC *= -1;
|
||
scaler.SetZeroMvInADC(currentChannel.ZeroMvInADC);
|
||
}
|
||
// adjusted to remove 2 samples to be consistent with TDC RDF exports
|
||
// TDC appears to be miscounting by 1 sample, developing 1 sample too few
|
||
// DataPRO download appears to download 1 extra sample, so to be consistent with TDC, remove 2 samples...
|
||
for (var pt = startSample; pt <= endSample - 2; pt++)
|
||
{
|
||
var dataPoint = currentChannel.PersistentChannelInfo.Data[pt];
|
||
if (bHasNaturallyInvertedScaleFactor)
|
||
{
|
||
dataPoint *= -1;
|
||
}
|
||
var value = (ushort)(dataPoint + DTS.Common.Constants.ADC_MIDPOINT);
|
||
var byte2 = (byte)(value >> 8);
|
||
var byte1 = (byte)(value & 0xFF);
|
||
fileWriter.Write(byte1);
|
||
fileWriter.Write(byte2);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
throw new Exception("encountered problem writing RDF channel headers", ex);
|
||
}
|
||
}
|
||
|
||
public string GetTestObjectNumber(string isocode, TestPlan plan)
|
||
{
|
||
var ic = new IsoCode(isocode);
|
||
var testobjectcharacter = ic.TestObject;
|
||
|
||
var objectNumber = 1;
|
||
foreach (var to in plan.ISOTestObjects)
|
||
{
|
||
if (to.TypeOfTestObject == testobjectcharacter)
|
||
{
|
||
return objectNumber.ToString(CultureInfo.InvariantCulture);
|
||
}
|
||
objectNumber++;
|
||
}
|
||
return objectNumber.ToString("1");
|
||
}
|
||
}
|
||
}
|
||
}
|