Files
2026-04-17 14:55:32 -04:00

2767 lines
93 KiB
C#

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
{
/// <summary>
/// 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
/// </summary>
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;
/// <summary>
/// per Jim Platte, GM RDF requires only 2 digits for module serial
/// </summary>
/// <param name="serialNumber"></param>
/// <returns></returns>
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");
}
/// <summary>
/// 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
/// </summary>
/// <param name="test"></param>
/// <param name="rackToIndex"></param>
/// <param name="moduleSerialNumberToRackIndex"></param>
/// <returns></returns>
private Rack[] GetRacks(Test test, out Dictionary<string, int> moduleSerialNumberToRackIndex)
{
var racks = new List<Rack>();
var rackToIndex = new Dictionary<string, int>();
moduleSerialNumberToRackIndex = new Dictionary<string, int>();
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();
}
/// <summary>
/// this class is for simplifying the rack/module/channel relationship between slice and tdas control
/// for the purpose of serializing
/// </summary>
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<TLFSection> _sections = new List<TLFSection>();
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<string, int> _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;
}
/// <summary>
/// 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
/// </summary>
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<RackInfoLine> _lines = new List<RackInfoLine>();
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<string> _modSerialNumbers;
public string[] ModSerialNumbers
{
get => _modSerialNumbers.ToArray();
set => _modSerialNumbers = new List<string>(value);
}
public RackInfoLine()
{
_modSerialNumbers = new List<string>() { "0", "0", "0", "0", "0", "0", "0", "0" };
}
public RackInfoLine(string data)
{
_modSerialNumbers = new List<string>() { "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<string>(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<ModuleInfoLine> _lines = new List<ModuleInfoLine>();
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
/// <summary>
///
/// </summary>
/// <param name="test"></param>
/// <param name="racks"></param>
/// <param name="moduleSNToRackIdx"></param>
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<ChannelFilter>();
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
/// <summary>
/// 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!
/// </summary>
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<string> _values = new List<string>();
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<string> _values = new List<string>();
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<string> _values = new List<string>();
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<string> _values = new List<string>();
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<string> _values = new List<string>();
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<string, int> 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<PreTestDataLine> _lines = new List<PreTestDataLine>();
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<string, int> 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<PostTestDataLine> _lines = new List<PostTestDataLine>();
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<string, int> 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<CalculatedChannelLine> _lines = new List<CalculatedChannelLine>();
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<string, int> 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<TOMSQUIBFireLine> _lines = new List<TOMSQUIBFireLine>();
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<string, int> 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<TOMDigitalChannelLine> _lines = new List<TOMDigitalChannelLine>();
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<DIMLine> _lines = new List<DIMLine>();
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<string, int> 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<G5DigitalChannelLine> _lines = new List<G5DigitalChannelLine>();
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<string, int> 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
}
}
}