using System; using System.Collections.Generic; using System.Linq; using System.Text; using DTS.Common.Classes.Sensors; using DTS.Common.Enums; using DTS.Common.Enums.Sensors; using DTS.Common.Utilities; using DTS.Common.Utilities.Logging; namespace DTS.Serialization.TDAS { /// /// this is a class for helping to read and write the TLF /// there are a lot of classes for the sections in the tlf, but most of them are very similar /// and it would be pretty easy to combine them into a generic class, but I'll leave that /// till things are up and running first /// public class TLF { public const int MODULE_TYPE_NONE = 0; public const int MODULE_TYPE_SIM = 1; public const int MODULE_TYPE_TOM = 2; public const int MODULE_TYPE_DIM = 3; public const int CHANNEL_TYPE_NONE = 0; public const int CHANNEL_TYPE_AIN = 1; public const int CHANNEL_TYPE_DIGITAL_IN = 2; public const int CHANNEL_TYPE_SQUIB = 3; public const int CHANNEL_TYPE_DIGITAL_OUT = 4; public const int CHANNEL_TYPE_SQUIB_VOLTAGE = 5; public const int CHANNEL_TYPE_SQUIB_CURRENT = 6; public const int MAX_RACKSN = 6; /// /// per Jim Platte, GM RDF requires only 2 digits for module serial /// /// /// public static string GetModuleSerialNumber(string serialNumber) { return $"{serialNumber.Substring(0, 2)}{serialNumber.Substring(serialNumber.Length - 4)}"; } private static bool IsDIM(string serialNumber) { return serialNumber.StartsWith("SPD") || serialNumber.StartsWith("SLD"); } /// /// parses out "racks/modules/channels" in a way familiar to tdas control /// tdas control makes some assumptions in the number of mods/rack and channels per mod /// basically we have to treat each bridge as a module and each base as a rack to /// this function just parses out the bases and the bridges as racks and modules /// /// /// /// /// private Rack[] GetRacks(Test test, out Dictionary moduleSerialNumberToRackIndex) { var racks = new List(); var rackToIndex = new Dictionary(); moduleSerialNumberToRackIndex = new Dictionary(); foreach (var module in test.Modules) { var rackSerialNumber = module.BaseSerialNumber ?? module.SerialNumber; if (!rackToIndex.ContainsKey(rackSerialNumber)) { rackToIndex.Add(rackSerialNumber, racks.Count); racks.Add(new Rack(rackSerialNumber)); } var rackIndex = rackToIndex[rackSerialNumber]; racks[rackIndex].Modules[module.Number] = module.SerialNumber; moduleSerialNumberToRackIndex[module.SerialNumber] = rackIndex; } return racks.ToArray(); } /// /// this class is for simplifying the rack/module/channel relationship between slice and tdas control /// for the purpose of serializing /// internal class Rack { public string SerialNumber { get; set; } public string[] Modules { get; set; } public int RackIndex { get; set; } public Rack(string serialNumber) { SerialNumber = serialNumber; Modules = new string[8]; for (var i = 0; i < 8; i++) { Modules[i] = "-1"; } } } public string SoftwareVersion { get; set; } = "7.1.2t"; public string TSFFile { get; set; } = ""; public DateTime TestTime { get; set; } = DateTime.MinValue; public string TestDescription { get; set; } = null; private readonly List _sections = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine(SoftwareVersion); sw.WriteLine(TSFFile); sw.WriteLine($"{TestTime.Month:00}-{TestTime.Day:00}-{TestTime.Year:00}"); sw.WriteLine(TestTime.ToString("HH:mm:ss")); sw.WriteLine(TestDescription); foreach (var section in _sections) { section.Serialize(sw); } } private readonly Rack[] _racks; private readonly Dictionary _moduleSerialNumberToRackIndex; public static System.Globalization.CultureInfo InvariantCulture = new System.Globalization.CultureInfo(""); public TLF(string file, Test test, string id) { Initialize(); TSFFile = test.Id + "/" + id; if (TestTime == DateTime.MinValue) { TestTime = test.InceptionDate; } //if (TestTime.Date != test.InceptionDate.Date) { throw new NotSupportedException("Tests are on different days"); } //preserve the test description unless null or blank if (string.IsNullOrEmpty(TestDescription)) { TestDescription = test.Description; } SamplingSection.AddTestData(test); _racks = GetRacks(test, out _moduleSerialNumberToRackIndex); RackSection.AddTestData(test, _racks); ModuleSection.AddTestData(test, _racks); SensorChannelSection.AddTestData(test, _racks, _moduleSerialNumberToRackIndex); var lastChannel = SensorChannelSection.LastChannel; CalculatedChannelSection.AddTestData(); TOMSection.AddTestData(test, _racks, _moduleSerialNumberToRackIndex); lastChannel = SensorChannelSection.LastChannel; DIMSection.AddTestData(test, _racks, _moduleSerialNumberToRackIndex, lastChannel); lastChannel = DIMSection.LastChannel; G5DigitalChannelSection.AddTestData(test, _racks, _moduleSerialNumberToRackIndex, lastChannel); } private void BackupFiles(string file) { try { var fi = new System.IO.FileInfo(file); var backupDirectory = System.IO.Path.Combine(fi.Directory.FullName, "PreMerge"); if (!System.IO.Directory.Exists(backupDirectory)) { System.IO.Directory.CreateDirectory(backupDirectory); } var binFiles = fi.Directory.GetFiles("*.BIN"); foreach (var binFile in binFiles) { CopyFile(binFile.FullName, System.IO.Path.Combine(backupDirectory, binFile.Name)); } CopyFile(file, System.IO.Path.Combine(backupDirectory, fi.Name)); } catch (Exception ex) { APILogger.Log("Problem backing up files", file, ex); } } private void CopyFile(string source, string directory) { try { System.IO.File.Copy(source, directory); } catch (Exception ex) { APILogger.Log("Problem copying file", source, directory, ex); } } private SamplingSection SamplingSection => _sections[0] as SamplingSection; private RackSection RackSection => _sections[1] as RackSection; private ModuleSection ModuleSection => _sections[2] as ModuleSection; private SensorChannelSection SensorChannelSection => _sections[3] as SensorChannelSection; private CalculatedChannelSection CalculatedChannelSection => _sections[4] as CalculatedChannelSection; private TOMSection TOMSection => _sections[5] as TOMSection; private DIMSection DIMSection => _sections[6] as DIMSection; private G5DigitalChannelsSection G5DigitalChannelSection => _sections[7] as G5DigitalChannelsSection; private void LoadFile(string file) { using (var sw = new System.IO.StreamReader(file)) { SoftwareVersion = sw.ReadLine(); TSFFile = sw.ReadLine(); var dtTestDate = DateTime.Parse(sw.ReadLine()); var ts = TimeSpan.Parse(sw.ReadLine()); dtTestDate = dtTestDate.Add(ts); TestTime = dtTestDate; TestDescription = sw.ReadLine(); SamplingSection.ReadTestData(sw); RackSection.ReadTestData(sw); ModuleSection.ReadTestData(sw); SensorChannelSection.ReadTestData(sw); CalculatedChannelSection.ReadTestData(sw); TOMSection.ReadTestData(sw); DIMSection.ReadTestData(sw); G5DigitalChannelSection.ReadTestData(sw); ExistingChannels = SensorChannelSection.ExistingChannels; LastChannelNumber = Math.Max(SensorChannelSection.LastChannel, DIMSection.LastChannel); LastChannelNumber = Math.Max(LastChannelNumber, G5DigitalChannelSection.LastChannel); SensorChannelSection.StartingSLICEChannel = LastChannelNumber; } } private void Initialize() { _sections.Add(new SamplingSection()); _sections.Add(new RackSection()); _sections.Add(new ModuleSection()); _sections.Add(new SensorChannelSection()); _sections.Add(new CalculatedChannelSection()); _sections.Add(new TOMSection()); _sections.Add(new DIMSection()); _sections.Add(new G5DigitalChannelsSection()); } public int ExistingChannels { get; set; } = 0; public int LastChannelNumber { get; set; } = 0; } /// /// we could make sections really generic and just be composed of strings /// and so on, but for now lets just add the Serialize method /// Serialize serializes the section to the underlying stream /// internal interface TLFSection { void Serialize(System.IO.StreamWriter sw); } internal class SamplingSection : TLFSection { private SamplingInformationLine _samplingInformation = null; public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start Sampling Information ----"); sw.WriteLine( "rate(Hz),pretrigtime(sec),posttrigtime(sec),AdjAAfilter(Hz),postcaltime(sec),prezerodatapts,postzerodatapts,originalt=0pt"); if (null != _samplingInformation) { _samplingInformation.Serialize(sw); } sw.WriteLine("---- End Sampling Information ----"); } public void AddTestData(Test test) { if (null == _samplingInformation) { _samplingInformation = new SamplingInformationLine(); _samplingInformation.AdjAAFilter = test.Modules[0].AaFilterRateHz; _samplingInformation.OriginalTimeZeroPoint = Convert.ToInt32(test.Modules[0].TriggerSampleNumbers[0] - (double)test.Modules[0].StartRecordSampleNumber); _samplingInformation.PostCalTime = 0D; //rate(hz) _samplingInformation.SamplingRate = test.Modules[0].SampleRateHz; int actualPostZeroDataPoints; if (test.Modules[0].StartRecordSampleNumber > test.Modules[0].TriggerSampleNumbers[0]) { // The test must have specified a positive (post-zero) value for pre-trigger seconds _samplingInformation.PreTriggerSeconds = test.Modules[0].RequestedPreTriggerSeconds; _samplingInformation.PreZeroDataPoints = 0; actualPostZeroDataPoints = Convert.ToInt32((double)test.Modules[0].NumberOfSamples); } else { //pretrigtime(sec) _samplingInformation.PreTriggerSeconds = Math.Abs(test.Modules[0].RequestedPreTriggerSeconds) * -1; //Set it to this whether or not it's ROI or All //prezerodatapts var requestedPreZeroDataPoints = Convert.ToInt32(test.Modules[0].SampleRateHz * Math.Abs(test.Modules[0].RequestedPreTriggerSeconds)); var actualPreZeroDataPoints = Convert.ToInt32(test.Modules[0].TriggerSampleNumbers[0] - test.Modules[0].StartRecordSampleNumber); _samplingInformation.PreZeroDataPoints = Math.Min(requestedPreZeroDataPoints, actualPreZeroDataPoints); //postzerodatapts actualPostZeroDataPoints = Convert.ToInt32(test.Modules[0].NumberOfSamples - (test.Modules[0].TriggerSampleNumbers[0] - (double)test.Modules[0].StartRecordSampleNumber)); } //posttrigtime(sec) _samplingInformation.PostTriggerSeconds = test.Modules[0].RequestedPostTriggerSeconds; //Set it to this whether or not it's ROI or All var requestedPostZeroDataPoints = Convert.ToInt32(test.Modules[0].SampleRateHz * test.Modules[0].RequestedPostTriggerSeconds) + 1; _samplingInformation.PostZeroDataPoints = Math.Min(requestedPostZeroDataPoints, actualPostZeroDataPoints); } } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //--start sampling info sr.ReadLine(); //parameters if (sr.Peek() != '-') { _samplingInformation = new SamplingInformationLine(sr.ReadLine()); } sr.ReadLine(); //end sampling info } } internal class SamplingInformationLine { public double SamplingRate { get; set; } = 0D; public double PreTriggerSeconds { get; set; } = 0D; public double PostTriggerSeconds { get; set; } = 0D; public double AdjAAFilter { get; set; } = 0D; public double PostCalTime { get; set; } = 0D; public int PreZeroDataPoints { get; set; } = 0; public int PostZeroDataPoints { get; set; } = 0; public int OriginalTimeZeroPoint { get; set; } = 0; public void Serialize(System.IO.StreamWriter sw) { sw.Write(SamplingRate.ToString("F1")); sw.Write(","); sw.Write(PreTriggerSeconds.ToString("F2")); sw.Write(","); sw.Write(PostTriggerSeconds.ToString("F2")); sw.Write(","); sw.Write(AdjAAFilter); sw.Write(","); sw.Write(PostCalTime.ToString("F1")); sw.Write(","); sw.Write(PreZeroDataPoints); sw.Write(","); sw.Write(PostZeroDataPoints); //sw.Write(","); //sw.Write(OriginalTimeZeroPoint); //Don't write this because TDC doesn't unless T0 has been adjusted sw.Write(sw.NewLine); } public SamplingInformationLine() { } public SamplingInformationLine(string data) { var tokens = data.Split(','); for (var i = 0; i < tokens.Length; i++) { var dTemp = double.NegativeInfinity; var iTemp = int.MinValue; double.TryParse(tokens[i], System.Globalization.NumberStyles.Float, TLF.InvariantCulture, out dTemp); int.TryParse(tokens[i], out iTemp); switch (i) { case 0: if (!double.IsNegativeInfinity(dTemp)) { SamplingRate = dTemp; } break; case 1: if (!double.IsNegativeInfinity(dTemp)) { PreTriggerSeconds = dTemp; } break; case 2: if (!double.IsNegativeInfinity(dTemp)) { PostTriggerSeconds = dTemp; } break; case 3: if (!double.IsNegativeInfinity(dTemp)) { AdjAAFilter = dTemp; } break; case 4: if (!double.IsNegativeInfinity(dTemp)) { PostCalTime = dTemp; } break; case 5: if (int.MinValue != iTemp) { PreZeroDataPoints = iTemp; } break; case 6: if (int.MinValue != iTemp) { PostZeroDataPoints = iTemp; } break; case 7: if (int.MinValue != iTemp) { OriginalTimeZeroPoint = iTemp; } break; case 8: throw new NotSupportedException("Sampling information has more fields than expected: " + data); } } } } internal class RackSection : TLFSection { private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start Rack Information ----"); sw.WriteLine("Rack,racksn,mod1sn,mod2sn,mod3sn,mod4sn,mod5sn,mod6sn,mod7sn,mod8sn"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } sw.WriteLine("---- End Rack Information ----"); } public void AddTestData(Test test, TLF.Rack[] racks) { for (var i = 0; i < racks.Length; i++) { var rack = racks[i]; var ril = new RackInfoLine(rack); foreach (var existingRIL in _lines) { if (existingRIL.RackSerialNumber == ril.RackSerialNumber) { throw new NotSupportedException( "TDAS::RackSection::AddTestData already exists in TLF, a second export is not supported"); } } ril.Rack = GetMaxRackIndex() + 1; _maxRack = ril.Rack; _lines.Add(ril); rack.RackIndex = ril.Rack; } } private int _maxRack = -1; private int GetMaxRackIndex() { //we have to calculate it if there is data already in a tlf, for all the rest we add ourselves //we can just update our internal count //we have to count max rack since the TLF rack index could be anything and is probably not just //"1" or so on if (-1 == _maxRack) { _maxRack = 0; for (var i = 0; i < _lines.Count; i++) { if (_lines[i].Rack > _maxRack) { _maxRack = _lines[i].Rack; } } } return _maxRack; } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //start rackinfo sr.ReadLine(); //rack parameters while (sr.Peek() != '-') { _lines.Add(new RackInfoLine(sr.ReadLine())); } sr.ReadLine(); //end rack info } } internal class RackInfoLine { public int Rack { get; set; } public string RackSerialNumber { get; set; } private List _modSerialNumbers; public string[] ModSerialNumbers { get => _modSerialNumbers.ToArray(); set => _modSerialNumbers = new List(value); } public RackInfoLine() { _modSerialNumbers = new List() { "0", "0", "0", "0", "0", "0", "0", "0" }; } public RackInfoLine(string data) { _modSerialNumbers = new List() { "0", "0", "0", "0", "0", "0", "0", "0" }; var tokens = data.Split(','); for (var i = 0; i < tokens.Length; i++) { if (0 == i) { Rack = int.Parse(tokens[i]); } else if (1 == i) { RackSerialNumber = tokens[i]; } else if (i < 10) { _modSerialNumbers[i - 2] = tokens[i]; } else { throw new NotSupportedException("Invalid number of parameters in rack info line: " + data); } } } public RackInfoLine(TLF.Rack rack) { RackSerialNumber = rack.SerialNumber; _modSerialNumbers = new List(rack.Modules); } public void Serialize(System.IO.StreamWriter sw) { sw.Write(Rack); sw.Write(","); ////restrict to 7 characters for TDC //sw.Write(RackSerialNumber.Length > TLF.MAX_RACKSN ? TLF.GetModuleSerialNumber(RackSerialNumber) : RackSerialNumber); sw.Write(RackSerialNumber); foreach (var s in _modSerialNumbers) { sw.Write(","); sw.Write(s); } sw.Write(sw.NewLine); } } internal class ModuleSection : TLFSection { private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start Module Information ----"); sw.WriteLine("rack,module,trigmode,trigchan,trigdir,triglevel,moduletype,prediag,postdiag"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } sw.WriteLine("---- End Module Information ----"); } //@TODO - fix difference between tdas and slice //now things get a _little_ complicated //slice can have multiple trigger directions and channels //tdascontrol will only allow one trigger channel and one direction per channel //lets resolve this later and omit the trigger direction and level now /// /// /// /// /// /// public void AddTestData(Test test, TLF.Rack[] racks) { for (var i = 0; i < racks.Length; i++) { var rack = racks[i]; for (var iModule = 0; iModule < rack.Modules.Length; iModule++) { if ("-1" == rack.Modules[iModule]) { continue; } var rackIndex = rack.RackIndex; var moduleIndex = iModule + 1; var moduleType = 0; if (IsSIM(rack.Modules[iModule])) { moduleType = TLF.MODULE_TYPE_SIM; } else if (IsTOM(rack.Modules[iModule])) { moduleType = TLF.MODULE_TYPE_TOM; } _lines.Add(new ModuleInfoLine(rackIndex, moduleIndex, moduleType)); } } } private bool IsSIM(string serialNumber) { return serialNumber.StartsWith("DM") || serialNumber.StartsWith("LM"); } private bool IsTOM(string serialNumber) { return serialNumber.StartsWith("TOM"); } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //StartModuleInfo; sr.ReadLine(); //parametersheader while (sr.Peek() != '-') { _lines.Add(new ModuleInfoLine(sr.ReadLine())); } sr.ReadLine(); //endmoduleinfo } } internal class ModuleInfoLine { public int Rack { get; set; } public int Module { get; set; } public int TriggerMode { get; set; } public int TriggerChannel { get; set; } public int TriggerDirection { get; set; } public double TriggerLevel { get; set; } public int ModuleType { get; set; } public string PreDiag { get; set; } = null; public string PostDiag { get; set; } = null; public void Serialize(System.IO.StreamWriter sw) { sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(TriggerMode); sw.Write(","); sw.Write(TriggerChannel); sw.Write(","); sw.Write(TriggerDirection); sw.Write(","); sw.Write(TriggerLevel.ToString("F1")); sw.Write(","); sw.Write(ModuleType); if (null != PreDiag) { sw.Write(","); sw.Write(PreDiag); } if (null != PostDiag) { sw.Write(","); sw.Write(PostDiag); } sw.Write(sw.NewLine); } public ModuleInfoLine(int rackIdx, int moduleIdx, int moduleType) { Rack = rackIdx; Module = moduleIdx; TriggerMode = 0; TriggerChannel = 1; TriggerDirection = 0; TriggerLevel = 0D; ModuleType = moduleType; PreDiag = null; PostDiag = null; } public ModuleInfoLine(string data) { var tokens = data.Split(','); for (var i = 0; i < tokens.Length; i++) { //int iTemp = int.MinValue; //double dTemp = double.NegativeInfinity; switch (i) { case 0: //rack Rack = int.Parse(tokens[i]); break; case 1: //module Module = int.Parse(tokens[i]); break; case 2: //trigmode TriggerMode = int.Parse(tokens[i]); break; case 3: //trigchan TriggerChannel = int.Parse(tokens[i]); break; case 4: //trigdir TriggerDirection = int.Parse(tokens[i]); break; case 5: //triglevel TriggerLevel = double.Parse(tokens[i]); break; case 6: //moduletype ModuleType = int.Parse(tokens[i]); break; case 7: //prediag PreDiag = tokens[i]; break; case 8: //postdiag PostDiag = tokens[i]; break; default: throw new NotSupportedException("invalid parameter in module line: " + data); } } } } internal class PreTestDataLine { public int DataChannel { get; set; } public int Rack { get; set; } public int Module { get; set; } public int Channel { get; set; } public string Description { get; set; } public string SerialNumber { get; set; } public double OffsetLow { get; set; } public double OffsetHigh { get; set; } public CalMode CalMode { get; set; } public double CalStep { get; set; } public double ShuntValueEU { get; set; } public bool ProportionalToExcitation { get; set; } //mv/eu or mv/v/eu public double Sensitivity { get; set; } public double Gain { get; set; } public double ExcitationVoltage { get; set; } public string EU { get; set; } public int SoftwareFilter { get; set; } public bool Invert { get; set; } public ZeroRef ZeroRef { get; set; } public double DesiredMaxRange { get; set; } public string CommentField { get; set; } public DateTime CalDate { get; set; } public bool Offset { get; set; } public double InitialEU { get; set; } public string SensorId { get; set; } public string ISOCode { get; set; } public double IRTRACCExponent { get; set; } public int Category { get; set; } private string SanitizeCommas(string s) { return s.Replace(',', '_'); } public void Serialize(System.IO.StreamWriter sw) { sw.Write(DataChannel); sw.Write(","); sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(Channel); sw.Write(","); sw.Write(SanitizeCommas(Description)); sw.Write(","); sw.Write(SanitizeCommas(SerialNumber)); sw.Write(","); sw.Write(OffsetLow.ToString("F1")); sw.Write(","); sw.Write(OffsetHigh.ToString("F1")); sw.Write(","); sw.Write(CalMode.ToString()); sw.Write(","); sw.Write(CalStep.ToString("F1")); sw.Write(","); sw.Write(ShuntValueEU.ToString("F1")); sw.Write(","); if (ProportionalToExcitation) { sw.Write("Y"); } else { sw.Write("N"); } sw.Write(","); sw.Write(Sensitivity.ToString("F12")); sw.Write(","); sw.Write(Gain.ToString("F5")); sw.Write(","); sw.Write(ExcitationVoltage.ToString("F5")); sw.Write(","); sw.Write(SanitizeCommas(EU)); sw.Write(","); sw.Write(SoftwareFilter); sw.Write(","); if (Invert) { sw.Write("1"); } else { sw.Write("0"); } sw.Write(","); sw.Write(ZeroRef.ToString()); sw.Write(","); sw.Write(DesiredMaxRange.ToString("F1")); sw.Write(","); if (string.IsNullOrWhiteSpace(CommentField)) { sw.Write("None"); } else { sw.Write(SanitizeCommas(CommentField)); } sw.Write(","); sw.Write($"{CalDate.Month}_{CalDate.Day}_{CalDate.Year}"); sw.Write(","); if (Offset) { sw.Write("Y"); } else { sw.Write("N"); } sw.Write(","); sw.Write(InitialEU.ToString("F1")); sw.Write(","); if (string.IsNullOrWhiteSpace(SensorId)) { sw.Write("0000000000000000"); } else { sw.Write(SensorId); } sw.Write(","); if (!string.IsNullOrWhiteSpace(ISOCode)) { sw.Write(SanitizeCommas(ISOCode)); } sw.Write(","); sw.Write(IRTRACCExponent.ToString("F1")); sw.Write(","); sw.Write(Category); sw.Write(sw.NewLine); } private double GetFilter(Test.Module.AnalogInputChannel aic) { double frequency = 0; try { if (!string.IsNullOrEmpty(aic?.SoftwareFilter)) { if (aic.SoftwareFilter.Contains("Hz")) { if (double.TryParse(aic.SoftwareFilter.Replace("Hz", ""), out frequency)) { } } else { var coder = new DescriptionAttributeCoder(); foreach (ChannelFilter filterType in Enum.GetValues(typeof(ChannelFilter))) { if (coder.DecodeAttributeValue(filterType) .Equals(aic.SoftwareFilter, StringComparison.OrdinalIgnoreCase)) { frequency = (double)filterType; break; } } } } } catch (Exception ex) { APILogger.Log("Could not get software filter: ", ex); } return frequency; } public PreTestDataLine(int datachannel, int rackindex, int moduleindex, Test.Module.Channel channel) { var aic = channel as Test.Module.AnalogInputChannel; DataChannel = datachannel; Rack = rackindex; Module = moduleindex; Channel = channel.Number + 1; CalDate = DateTime.MinValue; CalMode = new CalMode(); CalMode.Filter = true; CalMode.FullBridge = aic.Bridge == SensorConstants.BridgeType.FullBridge; CalMode.ShuntCheck = aic.ShuntEnabled; CalStep = -1; //emulation Category = 0; CommentField = aic.ChannelDescriptionString; if (string.IsNullOrWhiteSpace(aic.ChannelName2)) { Description = "None"; } else { Description = aic.ChannelName2; // http://fogbugz/fogbugz/default.asp?10249 } DesiredMaxRange = aic.DesiredRange; EU = aic.EngineeringUnits.TrimEnd(); try { ExcitationVoltage = aic.FactoryExcitationVoltage; } catch (Exception) { ExcitationVoltage = aic.MeasuredExcitationVoltage; } ExcitationVoltage = aic.MeasuredExcitationVoltage; Gain = (5000D / ushort.MaxValue) / aic.Data.ScaleFactorMv; InitialEU = aic.InitialEu; Invert = aic.IsInverted; ISOCode = aic.IsoCode; Offset = aic.RemoveOffset; OffsetHigh = aic.OffsetToleranceHighMv; OffsetLow = aic.OffsetToleranceLowMv; ProportionalToExcitation = aic.ProportionalToExcitation; Sensitivity = aic.Sensitivity; CalDate = aic.LastCalibrationDate; SensorId = aic.SensorID; SerialNumber = aic.SerialNumber; try { ShuntValueEU = aic.BridgeResistanceOhms; } catch { } SoftwareFilter = Convert.ToInt32(GetFilter(aic)); switch (aic.ZeroMethod) { case ZeroMethodType.AverageOverTime: ZeroRef = new ZeroRef(ZeroRef.ZeroType.AverageOverTime); break; case ZeroMethodType.UsePreEventDiagnosticsZero: ZeroRef = new ZeroRef(ZeroRef.ZeroType.UsePreEventDiagnostics); break; default: ZeroRef = new ZeroRef(ZeroRef.ZeroType.UseZeroMv); break; } } public PreTestDataLine(string data) { var tokens = data.Split(','); for (var i = 0; i < tokens.Length; i++) { var iTemp = int.MinValue; var dTemp = double.NegativeInfinity; //we do this as it's safer since some of the inputs may be blank int.TryParse(tokens[i], out iTemp); double.TryParse(tokens[i], System.Globalization.NumberStyles.Float, TLF.InvariantCulture, out dTemp); switch (i) { case 0: //datachan if (int.MinValue != iTemp) { DataChannel = iTemp; } break; case 1: //rack if (int.MinValue != iTemp) { Rack = iTemp; } break; case 2: //mod if (int.MinValue != iTemp) { Module = iTemp; } break; case 3: //chan if (int.MinValue != iTemp) { Channel = iTemp; } break; case 4: //desc Description = tokens[i]; break; case 5: //s/n SerialNumber = tokens[i]; break; case 6: //offsetlow if (!double.IsNegativeInfinity(dTemp)) { OffsetLow = dTemp; } break; case 7: //ofsethigh if (!double.IsNegativeInfinity(dTemp)) { OffsetHigh = dTemp; } break; case 8: //calmode CalMode = new CalMode(tokens[i]); break; case 9: //calstep if (!double.IsNegativeInfinity(dTemp)) { CalStep = dTemp; } break; case 10: //shuntval if (!double.IsNegativeInfinity(dTemp)) { ShuntValueEU = dTemp; } break; case 11: //proptoext if (tokens[i].ToLower() == "n") { ProportionalToExcitation = false; } else { ProportionalToExcitation = true; } break; case 12: //sens if (!double.IsNegativeInfinity(dTemp)) { Sensitivity = dTemp; } break; case 13: //gain if (!double.IsNegativeInfinity(dTemp)) { Gain = dTemp; } break; case 14: //extvolt if (!double.IsNegativeInfinity(dTemp)) { ExcitationVoltage = dTemp; } break; case 15: //EU EU = tokens[i]; break; case 16: //filter if (int.MinValue != iTemp) { SoftwareFilter = iTemp; } break; case 17: //invert if ("1" == tokens[i]) { Invert = true; } else { Invert = false; } break; case 18: //zeroref ZeroRef = new ZeroRef(tokens[i]); break; case 19: //desiredmaxrange if (!double.IsNegativeInfinity(dTemp)) { DesiredMaxRange = dTemp; } break; case 20: //commentfield CommentField = tokens[i]; break; case 21: //caldate { var subTokens = tokens[i].Split('_'); if (3 == subTokens.Length) { CalDate = new DateTime(int.Parse(subTokens[2]), int.Parse(subTokens[0]), int.Parse(subTokens[1])); } } break; case 22: //offset? if (tokens[i].ToLower() == "n") { Offset = false; } else { Offset = true; } break; case 23: //initialEU if (!double.IsNegativeInfinity(dTemp)) { InitialEU = dTemp; } break; case 24: //sensorid SensorId = tokens[i]; break; case 25: //isocode ISOCode = tokens[i]; break; case 26: //irtracc exponent if (!double.IsNegativeInfinity(dTemp)) { IRTRACCExponent = dTemp; } break; case 27: //category if (int.MinValue != iTemp) { Category = iTemp; } break; default: throw new NotSupportedException("Invalid number of parameters in Sensor line: " + data); } } } } internal class PostTestDataLine { public int DataChannel { get; set; } public int Rack { get; set; } public int Module { get; set; } public int Channel { get; set; } public double ActualMaxRange { get; set; } public string PreDiag { get; set; } public double Offset { get; set; } public double SignalToNoiseRatio { get; set; } public double PreZero { get; set; } public int PreCal { get; set; } //volt/cnt /// /// DANGER, we aren't using the DataScaler here /// (because of the nature of the TLF file it's a little bit ugly to jam in) /// tripple check the results of TLF exports! /// private double _scaleFactorMV; public double ScaleFactorMV { get => _scaleFactorMV; set => _scaleFactorMV = value; } public double ScaleFactorEU { get; set; } public double DataZero { get; set; } public double PostZero { get; set; } public int PostCal { get; set; } public double MaxEU { get; set; } public double MaxTimeMS { get; set; } public double MinEU { get; set; } public double MinTimeMS { get; set; } public bool ChannelSaturated { get; set; } public string PostDiag { get; set; } public long ActualZeroMVCounts { get; set; } public double DesiredMaxRangeScaling { get; set; } public void Serialize(System.IO.StreamWriter sw) { sw.Write(DataChannel); sw.Write(","); sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(Channel); sw.Write(","); sw.Write(ActualMaxRange.ToString("F3")); sw.Write(","); sw.Write(PreDiag); sw.Write(","); sw.Write(Offset.ToString("F5")); sw.Write(","); sw.Write(SignalToNoiseRatio.ToString("F5")); sw.Write(","); sw.Write(PreZero); sw.Write(","); sw.Write(PreCal); sw.Write(","); sw.Write(ScaleFactorMV.ToString("F6")); sw.Write(","); sw.Write(ScaleFactorEU.ToString("F6")); sw.Write(","); sw.Write(DataZero); sw.Write(","); sw.Write(PostZero); sw.Write(","); sw.Write(PostCal); sw.Write(","); sw.Write(MaxEU.ToString("F6")); sw.Write(","); sw.Write(MaxTimeMS.ToString("F1")); sw.Write(","); sw.Write(MinEU.ToString("F6")); sw.Write(","); sw.Write(MinTimeMS.ToString("F4")); sw.Write(","); if (ChannelSaturated) { sw.Write("Y"); } else { sw.Write("N"); } sw.Write(","); sw.Write(PostDiag); sw.Write(","); sw.Write(ActualZeroMVCounts); sw.Write(","); sw.Write(DesiredMaxRangeScaling.ToString("F1")); sw.Write(sw.NewLine); } private string GetPreDiag(Test.Module.Channel channel, double signalToNoiseRatio, double offset) { var aic = channel as Test.Module.AnalogInputChannel; if (null == aic) { return "PPPPPPP"; } var sb = new StringBuilder(); //1 noise level //noiseFloorDB = -20 * Math.Log10(3 * stddev / 65536.0); //from tdascontrol var noiseFloorDB = -20 * Math.Log10(-3 * aic.NoiseAsPercentageOfFullScale / ushort.MinValue); if (noiseFloorDB < 0 || noiseFloorDB > 100) { noiseFloorDB = 0; } if (noiseFloorDB < 50) { sb.Append("F"); } else { sb.Append("P"); } //step 2 excitationvoltage //tdas control also does a little dance around the excitation voltage at -10.1 //but I'm not really sure why it does that and I'm a little suspicious of it //so I leave it out of the implementation /* * if (-10.1 > extvolt[r][m][c]){ extvolt[r][m][c] = fabs (extvolt[r][m][c]); }*/ var excitationVoltage = 5D; try { excitationVoltage = aic.FactoryExcitationVoltage; } catch { switch (aic.ExcitationVoltage) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: excitationVoltage = 5D; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: excitationVoltage = 10D; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2_5: excitationVoltage = 2.5D; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2: excitationVoltage = 2D; break; } } var measuredExcitation = 5D; if (aic.MeasuredExcitationVoltageValid) { measuredExcitation = aic.MeasuredExcitationVoltage; } else if (aic.FactoryExcitationVoltageValid) { measuredExcitation = aic.FactoryExcitationVoltage; } else { APILogger.Log("both factory and measured excitation invalid, using 5V instead"); } var delta = 100D * (excitationVoltage - measuredExcitation) / excitationVoltage; if (delta < -2 || delta > 2) { sb.Append("F"); } else { sb.Append("P"); } //offset - this information isn't available yet (offset low/high) sb.Append("P"); //gain accuracy - range checking isn't available sb.Append("P"); if (aic.ZeroMethod == ZeroMethodType.None) { sb.Append("P"); } else { delta = 100D * aic.PreTestZeroLevelAdc / (-1 * short.MinValue + short.MaxValue); if (delta < -5 || delta > 5) { sb.Append("F"); } else { sb.Append("P"); } } //signal to noise ratio //these seem like _really_ loose ratios ... if (signalToNoiseRatio > 90 || signalToNoiseRatio < 0) { sb.Append("F"); } else { sb.Append("P"); } //don't have access to the settings here, so the information needs to be passed in if (aic.ShuntEnabled && aic.MeasureShuntDeflectionMvValid && aic.TargetShuntDeflectionMvValid) { if (aic.MeasuredShuntDeflectionMv < .8 * aic.TargetShuntDeflectionMv || aic.MeasuredShuntDeflectionMv > 2 * aic.TargetShuntDeflectionMv) { sb.Append("F"); } else { sb.Append("P"); } } else { sb.Append("T"); } return sb.ToString(); } public PostTestDataLine(int datachan, int rackindex, Test.Module.Channel channel) { DataChannel = datachan; Rack = rackindex; Module = channel.ParentModule.Number + 1; Channel = channel.Number + 1; var aic = channel as Test.Module.AnalogInputChannel; var stddev = aic.NoiseAsPercentageOfFullScale / (-1D) * short.MinValue; stddev /= 100D; if (0 == stddev) { SignalToNoiseRatio = 0D; } else { SignalToNoiseRatio = 20D * Math.Log10(-.8D * short.MinValue / stddev); } if (aic.ProportionalToExcitation) { var x = DesiredMaxRangeScaling; ActualMaxRange = 0.95 * (aic.DesiredRange / (((5000D / ushort.MaxValue) / aic.Data.ScaleFactorMv) * aic.Sensitivity * aic.MeasuredExcitationVoltage)); } else { ActualMaxRange = 0.95 * (aic.DesiredRange / (((5000D / ushort.MaxValue) / aic.Data.ScaleFactorMv) * aic.Sensitivity)); } //@TODO - is this the right field? ActualZeroMVCounts = 0; ChannelSaturated = false; var minValue = short.MaxValue; var maxValue = short.MinValue; ulong minIndex = 0; ulong maxIndex = 0; for (ulong i = 0; i < channel.PersistentChannelInfo.NumberOfSamples; i++) { var sValue = channel.PersistentChannelInfo[i]; if (sValue < minValue) { minValue = sValue; minIndex = i; } if (sValue > maxValue) { maxValue = sValue; maxIndex = i; } if (minValue == short.MinValue && maxValue == short.MaxValue) { ChannelSaturated = true; break; } } DataZero = aic.DataZeroLevelAdc; DesiredMaxRangeScaling = 1; try { MaxTimeMS = 1000 * (aic.ParentModule.RequestedPreTriggerSeconds + ((double)(maxIndex - 1) / aic.ParentModule.SampleRateHz)); } catch (Exception) { } try { MinTimeMS = 1000 * (aic.ParentModule.RequestedPreTriggerSeconds + ((double)(minIndex - 1) / aic.ParentModule.SampleRateHz)); } catch (Exception) { } PostCal = Convert.ToInt32(.8D * short.MaxValue); //@TODO - pre and post diagnostics, is it ok for sliceware to just output P's? PostDiag = "P"; PostZero = aic.PreTestZeroLevelAdc; PreCal = Convert.ToInt32(.7D * short.MaxValue); PreZero = aic.PreTestZeroLevelAdc; var excitationVoltage = 5D; //if (slice) to-do: use FactoryExcitationVoltage if this is SLICE hardware //{ try { excitationVoltage = aic.FactoryExcitationVoltage; } catch (Exception) { excitationVoltage = aic.MeasuredExcitationVoltage; } //} //else //{ excitationVoltage = aic.MeasuredExcitationVoltage; //} if (aic.ProportionalToExcitation) { ScaleFactorEU = aic.Data.ScaleFactorMv / (excitationVoltage * aic.Data.MvPerEu); } else { ScaleFactorEU = aic.Data.ScaleFactorMv / aic.Data.MvPerEu; } if (aic.IsInverted) { ScaleFactorEU *= -1D; } ScaleFactorMV = aic.Data.ScaleFactorMv; Offset = aic.DataZeroLevelAdc * ScaleFactorMV; MaxEU = ScaleFactorEU * (maxValue - aic.DataZeroLevelAdc); MinEU = ScaleFactorEU * (minValue - aic.DataZeroLevelAdc); PreDiag = GetPreDiag(channel, SignalToNoiseRatio, Offset); } public PostTestDataLine(string data) { var tokens = data.Split(','); for (var i = 0; i < tokens.Length; i++) { var dTemp = double.NegativeInfinity; var iTemp = int.MinValue; double.TryParse(tokens[i], System.Globalization.NumberStyles.Float, TLF.InvariantCulture, out dTemp); int.TryParse(tokens[i], out iTemp); switch (i) { case 0: //datachan if (int.MinValue != iTemp) { DataChannel = iTemp; } break; case 1: //rack if (int.MinValue != iTemp) { Rack = iTemp; } break; case 2: //mod if (int.MinValue != iTemp) { Module = iTemp; } break; case 3: //chan if (int.MinValue != iTemp) { Channel = iTemp; } break; case 4: //actmaxrange if (!double.IsNegativeInfinity(dTemp)) { ActualMaxRange = dTemp; } break; case 5: //prediag PreDiag = tokens[i]; break; case 6: //offsetmv if (!double.IsNegativeInfinity(dTemp)) { Offset = dTemp; } break; case 7: //snRatio(dB) if (!double.IsNegativeInfinity(dTemp)) { SignalToNoiseRatio = dTemp; } break; case 8: //prezero if (int.MinValue != iTemp) { PreZero = iTemp; } break; case 9: //precal if (int.MinValue != iTemp) { PreCal = iTemp; } break; case 10: //scalefactormv if (!double.IsNegativeInfinity(dTemp)) { ScaleFactorMV = dTemp; } break; case 11: //scalefactoreu if (!double.IsNegativeInfinity(dTemp)) { ScaleFactorEU = dTemp; } break; case 12: //datazero if (int.MinValue != iTemp) { DataZero = iTemp; } break; case 13: //postzero if (int.MinValue != iTemp) { PostZero = iTemp; } break; case 14: //postcal if (int.MinValue != iTemp) { PostCal = iTemp; } break; case 15: //maxeu if (!double.IsNegativeInfinity(dTemp)) { MaxEU = dTemp; } break; case 16: //maxtime(msec) if (!double.IsNegativeInfinity(dTemp)) { MaxTimeMS = dTemp; } break; case 17: //min(eu) if (!double.IsNegativeInfinity(dTemp)) { MinEU = dTemp; } break; case 18: //mintime(msec) if (!double.IsNegativeInfinity(dTemp)) { MinTimeMS = dTemp; } break; case 19: //chansat if (tokens[i].ToLower() != "n") { ChannelSaturated = true; } else { ChannelSaturated = false; } break; case 20: //postdiag PostDiag = tokens[i]; break; case 21: //actual 0mv if (int.MinValue != iTemp) { ActualZeroMVCounts = iTemp; } break; case 22: //desired max range scaling if (!double.IsNegativeInfinity(dTemp)) { DesiredMaxRangeScaling = dTemp; } break; default: throw new NotSupportedException("invalid number of parameters in sensor line: " + data); } } } } internal class CalculatedChannelLine { private readonly List _values = new List(); private enum Fields { chan = 0, descrip, processtype, firstchan, secondchan, thirdchan, value, EU, expmaxrange } private readonly Fields LastField = Fields.expmaxrange; public void Serialize(System.IO.StreamWriter sw) { for (var i = 0; i <= (int)LastField; i++) { if (i > 0) { sw.Write(","); } sw.Write(_values[i]); } sw.Write(sw.NewLine); } public CalculatedChannelLine(string data) { for (var i = 0; i <= (int)LastField; i++) { _values.Add(""); } var tokens = data.Split(','); if (tokens.Length > _values.Count) { throw new NotSupportedException("Unexpected number of parameters " + data); } for (var i = 0; i < tokens.Length; i++) { _values[i] = tokens[i]; } } } //SQUIBFireChannels //rack,module,chan,descrip,id,type, internal class TOMSQUIBFireLine { public int Rack { get; set; } public int Module { get; set; } public int Channel { get; set; } public string Description { get; set; } public string Id { get; set; } public int ChannelType { get; set; } public string Current { get; set; } public string Delay { get; set; } public string DurationOn { get; set; } public string Duration { get; set; } public string OhmLow { get; set; } public string OhmHigh { get; set; } public string ISOCode { get; set; } public double MeasuredOhms { get; set; } public string RecordType { get; set; } public string ScaleFactor1 { get; set; } public string ScaleFactor2 { get; set; } public string PostMeasuredOhms { get; set; } public string VoltageZeroAverageADC { get; set; } public string CurrentZeroAverageADC { get; set; } private readonly List _values = new List(); public void Serialize(System.IO.StreamWriter sw) { //for (int i = 0; i <= (int)LastField; i++) //{ // if (i > 0) { sw.Write(","); } // sw.Write(_values[i]); //} //sw.Write(sw.NewLine); sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(Channel); sw.Write(","); sw.Write(Description); sw.Write(","); sw.Write(Id); sw.Write(","); sw.Write(ChannelType); sw.Write(","); sw.Write(Current); sw.Write(","); sw.Write(Delay); sw.Write(","); sw.Write(DurationOn); sw.Write(","); sw.Write(Duration); sw.Write(","); sw.Write(OhmLow); sw.Write(","); sw.Write(OhmHigh); sw.Write(","); sw.Write(ISOCode); sw.Write(","); sw.Write(MeasuredOhms); sw.Write(","); sw.Write(RecordType); sw.Write(","); sw.Write(ScaleFactor1); sw.Write(","); sw.Write(ScaleFactor2); sw.Write(","); sw.Write(PostMeasuredOhms); sw.Write(","); sw.Write(VoltageZeroAverageADC); sw.Write(","); sw.Write(CurrentZeroAverageADC); sw.Write(sw.NewLine); } public TOMSQUIBFireLine(int rackIndex, Test.Module.Channel channel) { var aic = channel as Test.Module.AnalogInputChannel; Rack = rackIndex; Module = channel.ParentModule.Number + 1; switch (channel.Number) { case 0: case 1: Channel = 1; break; case 2: case 3: Channel = 2; break; case 4: case 5: Channel = 3; break; case 6: case 7: Channel = 4; break; } if (string.IsNullOrWhiteSpace(aic.Description)) { Description = "None"; } else { Description = aic.Description; } Id = aic.SensorID; ChannelType = TLF.CHANNEL_TYPE_SQUIB; //Needed: current,delay,durationON,duration,OhmLow,OhmHigh,ISO Code,measuredohms,recordtype,scalefactor1,scalefactor2,postmeasuredohms,voltage zero average ADC,current zero average ADC //Also needed: combine both TOM channels into one line } public TOMSQUIBFireLine(string data) { for (var i = 0; i <= (int)LastField; i++) { _values.Add(""); } var tokens = data.Split(','); if (tokens.Length > _values.Count) { throw new NotSupportedException("Unexpected number of parameters " + data); } for (var i = 0; i < tokens.Length; i++) { _values[i] = tokens[i]; } } private readonly Fields LastField = Fields.currentZeroAverageADC; private enum Fields { rack = 0, module, chan, descrip, id, type, current, delay, durationON, duration, OhmLow, OhmHigh, ISOCode, measuredohms, recordtype, scalefactor1, scalefactor2, postmeasuredohms, voltageZeroAverageADC, currentZeroAverageADC }; } internal class TOMDigitalChannelLine { private readonly List _values = new List(); private enum Fields { rack = 0, module, chan, type, delay, durationON, duration } private readonly Fields LastField = Fields.duration; public void Serialize(System.IO.StreamWriter sw) { for (var i = 0; i <= (int)LastField; i++) { if (i > 0) { sw.Write(","); } sw.Write(_values[i]); } sw.Write(sw.NewLine); } public TOMDigitalChannelLine(string data) { for (var i = 0; i <= (int)LastField; i++) { _values.Add(""); } var tokens = data.Split(','); if (tokens.Length > _values.Count) { throw new NotSupportedException("Unexpected number of parameters " + data); } for (var i = 0; i < tokens.Length; i++) { _values[i] = tokens[i]; } } } internal class DIMLine { private readonly List _values = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.Write(DataChannel); sw.Write(","); sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(Channel); sw.Write(","); sw.Write(Description); sw.Write(","); sw.Write(SerialNo); sw.Write(","); sw.Write(Mode); sw.Write(","); sw.Write(Inverted); sw.Write(","); sw.Write(EID); sw.Write(","); sw.Write(Filename); sw.Write(","); sw.Write(Scale.ToString("F6")); sw.Write(","); sw.Write(FilterMode); sw.Write(","); sw.Write(FilterThreshold.ToString("F6")); sw.Write(","); sw.Write(ISOCODE); sw.Write(","); sw.Write(CableTest); sw.Write(","); sw.Write(PreTestResults); sw.Write(","); sw.Write(PostTestResults); sw.Write(sw.NewLine); } private readonly Fields LastField = Fields.PostTestResults; private enum Fields { Datachan = 0, Rack, Module, Chan, Description, SerialNo, Mode, Inverted, EID, Filename, Scale, FilterMode, FilterThreshold, ISOCODE, CableTest, PreTestResults, PostTestResults } public int DataChannel { get; set; } public int Rack { get; set; } public int Module { get; set; } public int Channel { get; set; } public string Description { get; set; } public string SerialNo { get; set; } public DigitalInputModes Mode { get; set; } public int Inverted { get; set; } public string EID { get; set; } public string Filename { get; set; } public double Scale { get; set; } public int FilterMode { get; set; } public double FilterThreshold { get; set; } public string ISOCODE { get; set; } public int CableTest { get; set; } public string PreTestResults { get; set; } public string PostTestResults { get; set; } public DIMLine(int dataChannel, int rackIndex, Test.Module.Channel channel) { var aic = channel as Test.Module.AnalogInputChannel; DataChannel = dataChannel; Rack = rackIndex; Module = channel.ParentModule.Number; Channel = channel.Number + 1; Description = channel.ChannelDescriptionString; if (string.IsNullOrWhiteSpace(channel.SensorID)) { SerialNo = "No Sensor"; } else { SerialNo = channel.SensorID; } Mode = aic.DigitalMode; if (aic.IsInverted) { Inverted = 1; } else { Inverted = 0; } if (string.IsNullOrWhiteSpace(channel.SensorID)) { EID = "NONE"; } else { EID = channel.SensorID; } Filename = string.Empty; Scale = aic.DigitalMultiplier.ActiveValue; FilterMode = 1; //None=1; Latch=2; Debounce=3 FilterThreshold = 10.000000; //The default ISOCODE = aic.IsoCode; CableTest = 0; //No=0; Yes=1 PreTestResults = "PPPP"; //The default PostTestResults = "PPPP"; //The default } public DIMLine(string data) { for (var i = 0; i <= (int)LastField; i++) { _values.Add(""); } var tokens = data.Split(','); if (tokens.Length > _values.Count) { throw new NotSupportedException("Unexpected number of parameters " + data); } for (var i = 0; i < tokens.Length; i++) { _values[i] = tokens[i]; } } } internal class G5DigitalChannelLine { private readonly List _values = new List(); private enum Fields { datachan = 0, rack, mod, chan, descrip, ISOCode, scale, invert } public int DataChannel { get; set; } public int Rack { get; set; } public int Module { get; set; } public int Channel { get; set; } public string Description { get; set; } public string ISOCode { get; set; } public double Scale { get; set; } public int Invert { get; set; } readonly Fields LastField = Fields.invert; public G5DigitalChannelLine(int dataChannel, int rackIndex, Test.Module.Channel channel) { var aic = channel as Test.Module.AnalogInputChannel; DataChannel = dataChannel; Rack = rackIndex; Module = channel.ParentModule.Number + 1; Channel = channel.Number + 1; Description = channel.ChannelDescriptionString; ISOCode = aic.IsoCode; Scale = aic.DigitalMultiplier.ActiveValue; if (aic.IsInverted) { Invert = 1; } else { Invert = 0; } } public G5DigitalChannelLine(string data) { for (var i = 0; i <= (int)LastField; i++) { _values.Add(""); } var tokens = data.Split(','); if (tokens.Length > _values.Count) { throw new NotSupportedException("Unexpected number of parameters " + data); } for (var i = 0; i < tokens.Length; i++) { if ((int)Fields.datachan == i) { int datachan; if (int.TryParse(tokens[i], out datachan)) { DataChannel = datachan; } } _values[i] = tokens[i]; } } public void Serialize(System.IO.StreamWriter sw) { sw.Write(DataChannel); sw.Write(","); sw.Write(Rack); sw.Write(","); sw.Write(Module); sw.Write(","); sw.Write(Channel); sw.Write(","); sw.Write(Description); sw.Write(","); sw.Write(ISOCode); sw.Write(","); sw.Write(Scale.ToString("F1")); sw.Write(","); sw.Write(Invert); sw.Write(sw.NewLine); } } internal class SensorChannelSection : TLFSection { public int ExistingChannels => _preTestData.NumberOfLines; public int LastChannel => _preTestData.LastDataChannel; private readonly PreTestDataSection _preTestData = new PreTestDataSection(); private readonly PostTestDataSection _postTestData = new PostTestDataSection(); public int StartingSLICEChannel { set { _preTestData.StartingSLICEChannel = value; _postTestData.StartingSliceChannel = value; } } public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start Sensor Channel Information ----"); _preTestData.Serialize(sw); _postTestData.Serialize(sw); sw.WriteLine("---- End Sensor Channel Information ----"); } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx) { _preTestData.AddTestData(test, racks, moduleSNToRackIdx); _postTestData.AddTestData(test, racks, moduleSNToRackIdx); } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //start sensor channel information var curState = 0; while (sr.Peek() != '-') { var line = sr.ReadLine(); if (line == "PreTest Data") { curState = 1; sr.ReadLine(); //parameters; } else if (line == "PostTest Data") { curState = 2; sr.ReadLine(); //parameters } else { if (1 == curState) { _preTestData.ReadTestData(line); } else if (2 == curState) { _postTestData.ReadTestData(line); } else { throw new NotSupportedException("unexpected data in sensor channel section: " + line); } } } sr.ReadLine(); //endSensorChannelInformation } } internal class PreTestDataSection : TLFSection { private readonly List _lines = new List(); public int NumberOfLines => _lines.Count; public int StartingSLICEChannel { get; set; } = int.MinValue; public int LastDataChannel { get { if (_lines.Count < 1) { return 0; } var maxExiting = _lines.Max(ptdl => ptdl.DataChannel); return Math.Max(maxExiting, StartingSLICEChannel); } } public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("PreTest Data"); sw.WriteLine( "datachan,rack,mod,chan,descrip,s/n,offsetlow,offsethigh,calmode,calstep(ohm/volt),shuntval(eu),proptoext,sens(mv/eu or mv/v/eu),gain,extvolt,EU,filter,invert,zeroref,desiredmaxrange,commentfield,caldate,Offset?,InitialEU,sensorID,ISOcode,IRTRACC Exponent,Category"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx) { foreach (var channel in test.Channels) { if ((!(channel as Test.Module.AnalogInputChannel).IsSquibChannel) && ((channel as Test.Module.AnalogInputChannel).Bridge != SensorConstants.BridgeType.DigitalInput)) { var serialNumber = channel.ParentModule.SerialNumber; var rack = racks[moduleSNToRackIdx[serialNumber]]; var dataChannel = LastDataChannel + 1; var rackIndex = rack.RackIndex; var moduleIndex = channel.ParentModule.Number + 1; _lines.Add(new PreTestDataLine(dataChannel, rackIndex, moduleIndex, channel)); } } } public void ReadTestData(string line) { _lines.Add(new PreTestDataLine(line)); } } internal class PostTestDataSection : TLFSection { private readonly List _lines = new List(); public int StartingSliceChannel { get; set; } = int.MinValue; public int LastDataChannel { get { if (_lines.Count < 1) { return 0; } var maxExiting = _lines.Max(ptdl => ptdl.DataChannel); return Math.Max(maxExiting, StartingSliceChannel); } } public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("PostTest Data"); sw.WriteLine( "datachan,rack,mod,chan,actmaxrange,prediag,offset(mv),SNRatio(dB),prezero,precal,scalefactor(volts/cnt),scalefactor(eu/cnt),datazero,postzero,postcal,max(eu),maxtime(msec),min(eu),mintime(msec),chansat,postdiag,actual 0 mV cnts,desired max range scaling"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx) { foreach (var channel in test.Channels) { if ((!(channel as Test.Module.AnalogInputChannel).IsSquibChannel) && ((channel as Test.Module.AnalogInputChannel).Bridge != SensorConstants.BridgeType.DigitalInput)) { var dataChannel = LastDataChannel + 1; var serialNumber = channel.ParentModule.SerialNumber; var rack = racks[moduleSNToRackIdx[serialNumber]]; var rackIndex = rack.RackIndex; _lines.Add(new PostTestDataLine(dataChannel, rackIndex, channel)); } } } public void ReadTestData(string line) { _lines.Add(new PostTestDataLine(line)); } } internal class CalculatedChannelSection : TLFSection { private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start Calculated Channel Information ----"); sw.WriteLine("chan,descrip,processtype,1stchan,2ndchan,3rdchan,value,EU,expmaxrange"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } sw.WriteLine("---- End Calculated Channel Information ----"); } public void AddTestData() { } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //start calculated sr.ReadLine(); //parameters while (sr.Peek() != '-') { _lines.Add(new CalculatedChannelLine(sr.ReadLine())); } sr.ReadLine(); //end calculated } } internal class TOMSection : TLFSection { private readonly TOMSquibFireSection _squibFireSection = new TOMSquibFireSection(); private readonly TOMDigitalChannelsSection _digitalChannelsSection = new TOMDigitalChannelsSection(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- Start TOM Channel Information ----"); _squibFireSection.Serialize(sw); _digitalChannelsSection.Serialize(sw); sw.WriteLine("---- End TOM Channel Information ----"); } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx) { _squibFireSection.AddTestData(test, racks, moduleSNToRackIdx); _digitalChannelsSection.AddTestData(test, racks); } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //start tom channel info sr.ReadLine(); //squib fire channels sr.ReadLine(); //parameters while (sr.Peek() != '-') { _squibFireSection.ReadTestData(sr.ReadLine()); } sr.ReadLine(); //tom digital channels sr.ReadLine(); //parameters while (sr.Peek() != '-') { _digitalChannelsSection.ReadTestData(sr.ReadLine()); } sr.ReadLine(); //end tom channel information } } internal class TOMSquibFireSection : TLFSection { private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- TOM Squib Fire Channels ----"); sw.WriteLine( "rack,module,chan,descrip,id,type,current,delay,durationON,duration,OhmLow,OhmHigh,ISO Code,measuredohms,recordtype,scalefactor1,scalefactor2,postmeasuredohms,voltage zero average ADC,current zero average ADC"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx) { foreach (var channel in test.Channels) { var result = 0; //Only output one line for each Current/Voltage pair of squib channels Math.DivRem(channel.Number, 2, out result); if ((channel as Test.Module.AnalogInputChannel).IsSquibChannel && (result == 0)) { var serialNumber = channel.ParentModule.SerialNumber; var rack = racks[moduleSNToRackIdx[serialNumber]]; var rackIndex = rack.RackIndex; _lines.Add(new TOMSQUIBFireLine(rackIndex, channel)); } } } public void ReadTestData(string data) { _lines.Add(new TOMSQUIBFireLine(data)); } } internal class TOMDigitalChannelsSection : TLFSection { private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- TOM Digital Channels ----"); sw.WriteLine("rack,module,chan,type,delay,durationON,duration"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } } public void AddTestData(Test test, TLF.Rack[] racks) { } public void ReadTestData(string s) { _lines.Add(new TOMDigitalChannelLine(s)); } } internal class DIMSection : TLFSection { public int LastChannel { get; } = int.MinValue; private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- DIM Begin (1.0) ----"); sw.WriteLine( "Datachan,Rack,Module,Chan,Description,Serial No,Mode,Inverted,EID,Filename,Scale,Filter Mode,Filter Threshold,ISO CODE,Cable Test,Pre Test Results, Post TestResults"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } sw.WriteLine("---- DIM End ----"); } private static bool IsG5(Test.Module.Channel channel) { return channel.ParentModule.BaseSerialNumber.StartsWith("5M"); } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx, int lastChannel) { var LastChannel = lastChannel; foreach (var channel in test.Channels) { if (((channel as Test.Module.AnalogInputChannel).Bridge == SensorConstants.BridgeType.DigitalInput) && (!IsG5(channel))) { var serialNumber = channel.ParentModule.SerialNumber; var dataChannel = LastChannel + 1; LastChannel++; var rack = racks[moduleSNToRackIdx[serialNumber]]; var rackIndex = rack.RackIndex; _lines.Add(new DIMLine(dataChannel, rackIndex, channel)); } } } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //dimbegin sr.ReadLine(); //parameters while (sr.Peek() != '-') { _lines.Add(new DIMLine(sr.ReadLine())); } sr.ReadLine(); //dimend } } internal class G5DigitalChannelsSection : TLFSection { public int LastChannel { get; private set; } = int.MinValue; private readonly List _lines = new List(); public void Serialize(System.IO.StreamWriter sw) { sw.WriteLine("---- G5 Digital Input Channels Begin ----"); sw.WriteLine("datachan,rack,mod,chan,descrip,ISOCode,scale,invert"); if (null != _lines) { foreach (var line in _lines) { line.Serialize(sw); } } sw.WriteLine("---- G5 Digital Input Channels End ----"); } private static bool IsG5(Test.Module.Channel channel) { return channel.ParentModule.BaseSerialNumber.StartsWith("5M"); } public void AddTestData(Test test, TLF.Rack[] racks, Dictionary moduleSNToRackIdx, int lastChannel) { var LastChannel = lastChannel; foreach (var channel in test.Channels) { if (((channel as Test.Module.AnalogInputChannel).Bridge == SensorConstants.BridgeType.DigitalInput) && (IsG5(channel))) { var serialNumber = channel.ParentModule.SerialNumber; var dataChannel = LastChannel + 1; LastChannel++; var rack = racks[moduleSNToRackIdx[serialNumber]]; var rackIndex = rack.RackIndex; _lines.Add(new G5DigitalChannelLine(dataChannel, rackIndex, channel)); } } } public void ReadTestData(System.IO.StreamReader sr) { sr.ReadLine(); //g5digital begin sr.ReadLine(); //parameters while (sr.Peek() != '-') { var g5 = new G5DigitalChannelLine(sr.ReadLine()); _lines.Add(g5); LastChannel = Math.Max(LastChannel, g5.DataChannel); } sr.ReadLine(); //end line } } }