/* * 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 { /// /// /// Utility object for serializing s to disk /// in the Diadem /// /// public class Writer : Writer, IWriter { public File WriterParent { get; set; } public TestPlan TestPlan { get; set; } public string ExtensionPrefix { get; set; } = string.Empty; /// /// Initialize an instance of the Diadem.File.Writer class. /// /// /// /// The associated object. /// /// internal Writer(File fileType, int encoding) : base(fileType, encoding) { } /// /// Write the specified test to the specified pathname. /// /// /// /// The pathname to which the specified test should be serialized. /// /// /// /// The to be written out. /// /// /// 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); } } /// /// Write the representation file/files of the specified DTS.Serialization.Test /// at the given pathname. /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// 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()); }*/ /// /// this is a function to output CSV data for non linear channels /// it's used for debugging non linear output. /// /// /// /// // 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()); } /// /// 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 /// /// /// /// 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(); } /// /// Write the specified test to the specified stream. /// /// /// /// The to which the specified test should be serialized. /// /// /// /// The to be serialized. /// /// /// /// /// /// /// /// 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"); } } } }