Files
DP44/Common/DTS.Common.Serialization/DDAS (Chrysler)/DDASChannel.cs
2026-04-17 14:55:32 -04:00

318 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace DTS.Serialization.DDAS
{
public class DDASChannel
{
private readonly DDASTest _parentTest;
private readonly Dictionary<DDASTest.Fields, string> _values = new Dictionary<DDASTest.Fields, string>();
public string GetValue(DDASTest.Fields field)
{
if (!_values.ContainsKey(field)) _values.Add(field, "#NOVALUE");
return _values[field];
}
public void SetValue(DDASTest.Fields field, string value)
{
_values[field] = value;
switch (field)
{
case DDASTest.Fields.DataType:
if (value == "Raw")
{
SetValue(DDASTest.Fields.EngineeringUnits, "ADC");
SetValue(DDASTest.Fields.DigitalFilterType, "");
var actualRange = _parentTest.ActualRangesADC[_channelIndex];
SetValue(DDASTest.Fields.BitResolution, (2D * actualRange / ushort.MaxValue).ToString());
FileName = Path.Combine(Path.Combine(_path, "DDAS"), "Raw");
FileName = Path.Combine(FileName, $"{_parentTest.Test.Id}_{ChannelNumber}.DDAS");
}
else if (value == "Processed")
{
SetValue(DDASTest.Fields.EngineeringUnits, _engineeringUnits);
SetValue(DDASTest.Fields.DigitalFilterType,
$"CFC {_parentTest.DataUnfilteredEU[_channelIndex].FilterDescription}/{_parentTest.DataUnfilteredEU[_channelIndex].FilterFrequencyHz}");
var actualRange = _parentTest.ActualRangesEUFiltered[_channelIndex];
SetValue(DDASTest.Fields.BitResolution, (2D * actualRange / ushort.MaxValue).ToString());
FileName = Path.Combine(Path.Combine(_path, "DDAS"), "Processed");
FileName = Path.Combine(FileName, $"{_parentTest.Test.Id}_{ChannelNumber}.DDAS");
}
else if (value == "Converted")
{
SetValue(DDASTest.Fields.EngineeringUnits, _engineeringUnits);
SetValue(DDASTest.Fields.DigitalFilterType, "");
var actualRange = _parentTest.ActualRangesEUUnfiltered[_channelIndex];
SetValue(DDASTest.Fields.BitResolution, (2D * actualRange / ushort.MaxValue).ToString());
FileName = Path.Combine(Path.Combine(_path, "DDAS"), "Converted");
FileName = Path.Combine(FileName, $"{_parentTest.Test.Id}_{ChannelNumber}.DDAS");
}
break;
}
}
private readonly string _engineeringUnits = "";
public string FileName { get; set; }
public void Serialize(TickEventHandler tickHandler)
{
if (!Directory.Exists(new FileInfo(FileName).Directory.FullName))
Directory.CreateDirectory(new FileInfo(FileName).Directory.FullName);
using (var bw = new BinaryWriter(System.IO.File.Open(FileName, FileMode.Create)))
{
// First write the "fileinfoblock"
//typedef struct tagFILEINFOBLOCK
//{
// UINT Size; // Block Size (including nSize)
// char FileTypeName[12]; // Software Type Name
// char FileTypeVers[12]; // File Version Name
// UINT FileTypeFlags; // File Type Flags
// // - Hardware Type in upper 16 bits
// // - File Type in lower 16 bits
// char CreatedByName[16]; // Created by T-number
// char UpdatedByName[16]; // Updated by T-number
//}FILEINFOBLOCK;
bw.Write((uint)0x40);
var fileType = "DDAS FlPt";
bw.Write(Encoding.ASCII.GetBytes(fileType.PadRight(12, '\0')));
var fileTypeVersion = "Ver 500";
bw.Write(Encoding.ASCII.GetBytes(fileTypeVersion.PadRight(12, '\0')));
bw.Write((uint)0);
bw.Write(
Encoding.ASCII.GetBytes(
_parentTest.Test.Id.Substring(0, Math.Min(16, _parentTest.Test.Id.Length)).PadRight(16, '\0')));
bw.Write(
Encoding.ASCII.GetBytes(
_parentTest.Test.Id.Substring(0, Math.Min(16, _parentTest.Test.Id.Length)).PadRight(16, '\0')));
// Now the test info
// typedef struct tagTESTINFO
// {
// unsigned long Size; // Block Size (including Size)
// unsigned long DeviceID; // DAQ device ID
// long ChannelNo; // Channel number (1-32)
// long SampleRate; // Samples per second
// long TotalSamples; // Total samples in data record
// long PreEventSamples; // Samples before event
// short ChanNumInSys; // Channel Number (1-128) in system (many devices)
// short NumPreCalPts; // Number of preCal points included with data
// short NumPostCalPts; // Number of preCal points included with data
// char TestCreation[TESTPATHSIZE]; // Test path and date
// char TimeAxisTitle[32]; // Time axis title
// byte SpareBytes[2]; // 2 bytes of spare (for 4 byte alignment)
//
// } TESTINFO;
var channel = _parentTest.Test.Channels[_channelIndex] as Test.Module.AnalogInputChannel;
bw.Write((uint)0xC0);
bw.Write((uint)(1 + channel.ParentModule.Number));
bw.Write((uint)(1 + _channelIndex));
bw.Write((uint)channel.ParentModule.SampleRateHz);
bw.Write((uint)channel.ParentModule.NumberOfSamples);
bw.Write((uint)(channel.ParentModule.PreTriggerSeconds * channel.ParentModule.SampleRateHz));
bw.Write((ushort)channel.Number);
bw.Write((ushort)0);
bw.Write((ushort)0);
var testPathAndDate = FileName + " " + channel.ParentModule.ParentTest.InceptionDate.ToLongDateString();
bw.Write(
Encoding.ASCII.GetBytes(
testPathAndDate.Substring(0, Math.Min(128, testPathAndDate.Length)).PadRight(128, '\0')));
var TimeLabel = "Time (msec)";
bw.Write(Encoding.ASCII.GetBytes(TimeLabel.PadRight(32, '\0')));
bw.Write((byte)0x00);
bw.Write((byte)0x00);
// Channel
//typedef struct tagCHANNEL
// {
// short Size; // Size of this object
// short Flags; // Channel Flags
// char Name[64]; // Channel Name
// char Sign[8]; // Sign +, -, or blank
// char Axis[8]; // X,Y,Z,FX,MX,AX,...
// float FilterFreq; // Channel Filter Class (in Hz)
// float SetGain; // Gain setting (1 - n)
// float ActGain; // Actual (measured?) gain setting.
// float Rcal; // Shunt cal resistance
// float Excitation; // Excitation Voltage (when programable)
// byte byteSpares[4]; // Spare bytes (was Cal Date)
// TRANSDUCER Transducer; // "Snapshot" of transducer values
// } CHANNEL;
bw.Write((ushort)0x1001);
bw.Write((ushort)0x0100);
var description =
Encoding.ASCII.GetBytes(
channel.Description.Substring(0, Math.Min(64, channel.Description.Length)).PadRight(64, '\0'));
bw.Write(description);
var polarity = "+";
if (channel.IsInverted)
polarity = "-";
bw.Write(Encoding.ASCII.GetBytes(polarity.PadRight(8, (char)0x00)));
const string empty = "";
bw.Write(Encoding.ASCII.GetBytes(empty.PadRight(8, (char)0x00)));
//ChannelFilter[] filterClasses = Enum.GetValues(typeof(ChannelFilter)).Cast<ChannelFilter>().ToArray();
//float effectiveCFCFrequency = 0;
//foreach(var f in filterClasses)
//{
// if((int)f == (int)_parentTest.DataUnfilteredEU[_channelIndex].FilterFrequencyHz)
// {
// var type = typeof(ChannelFilter);
// var memInfo = type.GetMember(f.ToString());
// var attributes = memInfo[0].GetCustomAttributes(typeof(CFCValueAttribute),
// false);
// effectiveCFCFrequency = (float)((CFCValueAttribute)attributes[0]).CFCValue;
// }
//}
//if (0 == effectiveCFCFrequency) { effectiveCFCFrequency = (float)_parentTest.DataUnfilteredEU[_channelIndex].FilterFrequencyHz; }
bw.Write(channel.ParentModule.AaFilterRateHz);
if (channel.ExpectedGainValid)
bw.Write((float)channel.ExpectedGain);
else
bw.Write((float)(2500.0 / channel.DesiredRange));
if (channel.MeasuredGainValid)
bw.Write((float)channel.MeasuredGain);
else if (channel.ExpectedGainValid)
bw.Write((float)channel.ExpectedGain);
else
bw.Write((float)1.0);
bw.Write((float)channel.BridgeResistanceOhms);
if (channel.MeasuredExcitationVoltageValid)
bw.Write((float)channel.MeasuredExcitationVoltage);
else
bw.Write((float)channel.ExcitationVoltage);
bw.Write((byte)0x00);
bw.Write((byte)0x00);
bw.Write((byte)0x00);
bw.Write((byte)0x00);
//typedef struct tagTRANSDUCER
//{
//char Mfr[32]; // Manufacturer
//char Model[32]; // Model
//char SN[32]; // Transducer Serial No
//char Type[16]; // Transducer Type (load cell, accel, etc)
//char EngUnits[16]; // Engineering Units (applies to Capacity,
// // Sensitivity, and EUCalValue
//float Capacity; // Max applied accel, force, displacement, etc
//float XdcrRactive; // Bridge arm resistance of active half
//float XdcrRinactive; // Bridge arm resistance of inactive half
//float Sensitivity; // Sensitivity in V/EU
//float EUCalValue; // Engineering unit cal value with Rcal
//float Rcal; // Cal Resistor in Ohms
//float Excitation; // Excitation Voltage (when specified)
//long CalDate; // Cal date as time_t (seconds since 1970)
// // (Good til 2038...shouldn't be an issue
// // for this developer).
// long Spare; // Spare long
//} TRANSDUCER;
var Manufacturer = "CHRYSLER";
bw.Write(
Encoding.ASCII.GetBytes(Manufacturer.Substring(0, Math.Min(32, Manufacturer.Length))
.PadRight(32, '\0')));
bw.Write(Encoding.ASCII.GetBytes(empty.PadRight(32, (char)0x00)));
bw.Write(
Encoding.ASCII.GetBytes(
channel.SerialNumber.Substring(0, Math.Min(32, channel.SerialNumber.Length)).PadRight(32, '\0')));
var SensorType = "UNKNOWN";
bw.Write(
Encoding.ASCII.GetBytes(SensorType.Substring(0, Math.Min(16, SensorType.Length)).PadRight(16, '\0')));
bw.Write(
Encoding.ASCII.GetBytes(
channel.EngineeringUnits.Substring(0, Math.Min(16, channel.EngineeringUnits.Length))
.PadRight(16, '\0')));
bw.Write((float)channel.DesiredRange);
bw.Write((float)0);
bw.Write((float)0);
bw.Write((float)(1000 * channel.Sensitivity));
bw.Write((float)0);
bw.Write((float)channel.BridgeResistanceOhms);
bw.Write((float)channel.ExcitationVoltage);
var Epoch = new DateTime(1971, 1, 1, 0, 0, 0);
bw.Write((int)(channel.LastCalibrationDate - Epoch).TotalSeconds);
var TransducerSpare = 0;
bw.Write(TransducerSpare);
var Spare = new byte[0x0230 - 0x0210];
bw.Write(Spare);
double percentageComplete = (float)_channelIndex / _parentTest.Channels.Length;
var weight = 1D / _parentTest.Channels.Length;
for (var i = 0; i < _parentTest.DataUnfilteredEU[_channelIndex].Data.Length; i++)
{
bw.Write((float)_parentTest.DataUnfilteredEU[_channelIndex].Data[i]);
if (0 == i % 1000)
{
var percent = 100D * (percentageComplete + i * weight / channel.ParentModule.NumberOfSamples);
tickHandler(this, percent);
}
}
}
}
private readonly int _channelIndex;
public int ChannelNumber => 1 + _channelIndex;
private readonly string _path;
public DDASChannel(DDASTest parentTest, int channelIndex, string path)
{
_path = path;
_parentTest = parentTest;
_channelIndex = channelIndex;
var aic = parentTest.Test.Channels[channelIndex] as Test.Module.AnalogInputChannel;
_engineeringUnits = aic.EngineeringUnits;
SetValue(DDASTest.Fields.EngineeringUnits, aic.EngineeringUnits);
SetValue(DDASTest.Fields.SensorMakeModelSerial, aic.SerialNumber);
SetValue(DDASTest.Fields.SensorLocation, aic.Description);
// double actualRange = _parentTest.ActualRangesEUFiltered[_channelIndex];
double actualRange = 0;
SetValue(DDASTest.Fields.BitResolution, (2D * actualRange / ushort.MaxValue).ToString());
//FB14282: Option to Flatten Export Folders
FileName = path;
var channel = _parentTest.Test.Channels[_channelIndex] as Test.Module.AnalogInputChannel;
// We need a list of bases in order to properly formulate the file name.
var moduleSerialNumbers = new List<string>();
foreach (var currentChannel in _parentTest.Test.Channels)
if (!moduleSerialNumbers.Contains(currentChannel.ParentModule.SerialNumber))
moduleSerialNumbers.Add(currentChannel.ParentModule.SerialNumber);
var baseIndex = moduleSerialNumbers.IndexOf(channel.ParentModule.SerialNumber);
// Another SLICE Specific short cut. To properly number the channels, we need to know how many channels per module. However, in the case
// of SLICE where a bridge only has one channel _assigned_, channel.ParentModule.NumberOfChannels is 1. AFAIK, in this context, there is
// no way other than assumption to know a bridge has three channels.
var numberOfChannelsPerModule = (channel.ParentModule.NumberOfChannels + 2) / 3 * 3;
FileName = Path.Combine(FileName, $"ch{1 + baseIndex:D2}{1 + channel.Number + numberOfChannelsPerModule * channel.ParentModule.Number:D2}.fpd");
}
}
}