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

907 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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");
}
}
}
}