Files
DP44/DataPRO/IService/Classes/TMTFile.cs
2026-04-17 14:55:32 -04:00

507 lines
24 KiB
C#

using DTS.Common;
using DTS.Common.Behaviors;
using DTS.Common.Classes.Sensors;
using DTS.Common.Classes.TMAT;
using DTS.Common.DAS.Concepts;
using DTS.Common.Enums;
using DTS.Common.Enums.DASFactory;
using DTS.Common.Enums.Hardware;
using DTS.Common.Enums.Sensors;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DASFactory.Config;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Command.SLICE;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace DTS.DASLib.Service.Classes
{
public interface ITmtFile
{
/// <summary>
/// Writes the TMT file to the device
/// </summary>
void WriteTmtFile(IDASCommunication das, Dictionary<IDASCommunication, ushort> dataChannelIds, Dictionary<IDASCommunication, ushort> timeChannelIds, Dictionary<IDASCommunication, ushort> uartChannelIds, int dasIndex, ICommunication communication, float[] scaleFactors = null, float[] ranges = null, float[] measuredOffset = null);
}
//FB 25526 This class encapsulates the methods used in SLICE6AIR.cs before to intract with TMT files, the subclasses can support multiple types need like SLICE6AIR & TSRAIR
public abstract class TmtFile : ITmtFile
{
protected TmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults)
{
SerialNumber = serialNumber;
DASInfo = dasInfo;
ConfigData = configData;
ChannelDiagnosticsResults = channelDiagnosticsResults;
}
protected static ITMTTemplate GetTMTTemplate(TMAT_TEMPLATES tmat_TEMPLATES)
{
var tmtFileName = StringMetaDataAttr.GetStringMetaData(tmat_TEMPLATES);
var tokens = tmtFileName.Split(';');
if ( tokens.Length == 2) { return GetSplitFile(tokens[0], tokens[1]); }
if (string.IsNullOrWhiteSpace(tmtFileName))
{
tmtFileName = "S6ATMTTemplate_PCM.tmt";
}
return GetTMTSingleFile(tmtFileName);
}
/// <summary>
/// returns a single file template
/// </summary>
private static ITMTTemplate GetTMTSingleFile(string tmtFileName)
{
return new TmtSingleFile($"TMTTemplates/{tmtFileName}");
}
/// <summary>
/// returns split file template
/// </summary>
private static ITMTTemplate GetSplitFile(string dasTemplate, string channelTemplate)
{
return new TmtSplitFile($"TMTTemplates/{dasTemplate}", $"TMTTemplates/{channelTemplate}");
}
protected TMAT_TEMPLATES Template { get; set; }
protected int MaxChannels { get; set; }
protected string SerialNumber { get; set; }
protected IInfoResult DASInfo { get; set; }
protected IConfigurationData ConfigData { get; set; }
protected IDiagnosticResult[] ChannelDiagnosticsResults { get; set; }
/// <summary>
/// writes tmats file to the DASConfigs log directory, file is serial_TMT.txt
/// </summary>
protected static void WriteTMTFilePC(byte[] data, string testDirectory, string serialNumber)
{
const string dasConfigs = "DASConfigs";
try
{
var fileName = Path.Combine(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), dasConfigs),
$"{serialNumber}_TMT.txt");
if (File.Exists(fileName)) { File.Delete(fileName); }
File.WriteAllBytes(fileName, data);
//43789 Now, also write the file to the Data folder because the copy in the current directory gets overwritten every test
fileName = Path.Combine(testDirectory);
if (!Directory.Exists(fileName))
{
Directory.CreateDirectory(fileName);
}
fileName = Path.Combine(fileName, $"{serialNumber}_TMT.txt");
File.WriteAllBytes(fileName, data);
}
catch (Exception ex) { APILogger.Log(ex); }
try
{
var fileName = Path.Combine(testDirectory, dasConfigs);
if (!Directory.Exists(fileName))
{
Directory.CreateDirectory(fileName);
}
fileName = Path.Combine(fileName, $"{serialNumber}_TMT.txt");
File.WriteAllBytes(fileName, data);
}
catch (Exception ex) { APILogger.Log(ex); }
}
private static bool DoesChannelStreamUnsigned(AnalogInputDASChannel aic)
{
if (null == aic) { return true; }
if (null == aic.OwningModule) { return true; }
if (null == aic.OwningModule.OwningDAS) { return true; }
switch (aic.OwningModule.OwningDAS.GetHardwareType())
{
case Common.Enums.Hardware.HardwareTypes.TSR_AIR_RevB:
case Common.Enums.Hardware.HardwareTypes.SLICE6_AIR_TC: return false;
default: return true;
}
}
private static HashSet<HardwareTypes> _signedDAS = new HashSet<HardwareTypes>() { HardwareTypes.TSR_AIR, HardwareTypes.TSR_AIR_RevB, HardwareTypes.SLICE6_AIR_TC };
private static bool GetIsSignedData(AnalogInputDASChannel aic)
{
if ( null == aic || null == aic.OwningModule || aic == aic.OwningModule.OwningDAS)
{
return false;
}
var type = aic.OwningModule.OwningDAS.GetHardwareType();
if (_signedDAS.Contains(type)) { return true; }
return false;
}
/// <summary>
/// writes a channel to the template in memory
/// </summary>
protected void UpdateTMTChannel(ITMTTemplate template, int channelIndex, float[] scaleFactors = null, float[] ranges = null, float[] measuredOffset = null)
{
var channelKeys = Enum.GetValues(typeof(TMTChannelKeys)).Cast<TMTChannelKeys>()
.ToArray();
var number = (1 + channelIndex).ToString();
var moduleArrayIndex = DASInfo.MapDASChannelNumber2ModuleArrayIndex(channelIndex);
var moduleChannelIndex = DASInfo.MapDASChannelNumber2ModuleChannelNumber(channelIndex);
var aidc = ConfigData.Modules[moduleArrayIndex].Channels[moduleChannelIndex] as AnalogInputDASChannel;
var diagnosticResult =
(from dr in ChannelDiagnosticsResults where dr.DASChannelNumber == channelIndex select dr)
.FirstOrDefault();
//this was throwing an exception for SLICE-TC, but it doesn't really need a datascaler
//as the scale factor is always 1, so just create a new default scaler
//note that I fixed diagnostics which may have fixed the lack of scaler with thermo channels too
DataScaler scaler = new DataScaler();
try
{
scaler = aidc.GetDataScaler();
}
catch( Exception ex) { APILogger.Log(ex); }
var tempEU = scaler.GetEU(short.MinValue);
var tempEU2 = scaler.GetEU(short.MaxValue);
var minEU = Math.Min(tempEU, tempEU2);
var maxEU = Math.Max(tempEU, tempEU2);
var channelName2 = TmtBase.TMT_LimitString(aidc.ChannelName2);
var eu = RunTestVariables.MaskEUMetaData ? "---" : aidc.EngineeringUnits?.Trim() ?? "";
var offsetEU = GetOffsetEUString(aidc, scaler, measuredOffset, channelIndex, diagnosticResult);
var keys2 = Enum.GetValues(typeof(TMTChannelKeysEx)).Cast<TMTChannelKeysEx>();
var adcToEUScalingFactor = 1D;
if (scaleFactors != null && !RunTestVariables.MaskEUMetaData)
{
adcToEUScalingFactor = scaleFactors[channelIndex];
}
else if (!RunTestVariables.MaskEUMetaData)
{
adcToEUScalingFactor = scaler.GetAdcToEuScalingFactor();
}
var isSigned = GetIsSignedData(aidc);
foreach ( var key2 in keys2)
{
TmtBase.UpdateChannelField(key2, template, channelIndex, ranges, minEU, maxEU,
eu, scaleFactors, adcToEUScalingFactor, channelName2, offsetEU, isSigned);
}
foreach (var key in channelKeys)
{
switch (key)
{
case TMTChannelKeys.HardwareChannelNumber:
template.UpdateValue(key, number, 1 + channelIndex);
break;
case TMTChannelKeys.ChannelName:
template.UpdateValue(key, channelName2, 1 + channelIndex);
break;
case TMTChannelKeys.CouplingMode:
var couplingMode = "D";
if (aidc.CouplingMode == SensorConstants.CouplingModes.AC && aidc.IEPEChannel)
{
couplingMode = "A";
}
template.UpdateValue(key, couplingMode, 1 + channelIndex);
break;
case TMTChannelKeys.BridgeResistance:
if (null != diagnosticResult.BridgeResistance)
{
template.UpdateValue(key, ((double)diagnosticResult.BridgeResistance).ToString("F0"),
1 + channelIndex);
}
else
{
template.UpdateValue(key, "0", 1 + channelIndex);
}
break;
case TMTChannelKeys.AAF:
template.UpdateValue(key, ConfigData.Modules[0].AAFilterRateHz.ToString("F0"),
1 + channelIndex);
break;
case TMTChannelKeys.OffsetMV:
template.UpdateValue(key, RunTestVariables.MaskEUMetaData ?
"0" : ((short)diagnosticResult.FinalOffsetADC * diagnosticResult.ScalefactorMilliVoltsPerADC)
.ToString("F2"),
1 + channelIndex);
break;
case TMTChannelKeys.InputRangeMV:
template.UpdateValue(key, RunTestVariables.MaskEUMetaData ? "1" : (diagnosticResult.ScalefactorMilliVoltsPerADC * ushort.MaxValue).ToString("F0"),
1 + channelIndex);
break;
// Additional Max Range EU needed for C-1/MOT1 entry for Max input range EU
case TMTChannelKeys.MaxRangeEU:
if (ranges != null)
{
template.UpdateValue(key, ranges[channelIndex].ToString("F0"), 1 + channelIndex);
}
else
{
template.UpdateValue(key, RunTestVariables.MaskEUMetaData ? "1" :
maxEU.ToString("F0"), 1 + channelIndex);
}
break;
case TMTChannelKeys.MinRangeEU:
if (ranges != null)
{
template.UpdateValue(key, (-1 * ranges[channelIndex]).ToString("F0"), 1 + channelIndex);
}
else
{
template.UpdateValue(key, RunTestVariables.MaskEUMetaData ? "1" :
minEU.ToString("F0"), 1 + channelIndex);
}
break;
case TMTChannelKeys.EU:
template.UpdateValue(key,
eu,
1 + channelIndex);
break;
case TMTChannelKeys.ScaleFactorEU:
template.UpdateValue(key, adcToEUScalingFactor.ToString("F11"), 1 + channelIndex);
break;
case TMTChannelKeys.OffsetEU:
{
template.UpdateValue(key, offsetEU, 1 + channelIndex);
}
break;
}
}
}
/// <summary>
/// returns a string suitable for the TMATS file for the EU offset for a channel
/// </summary>
public static string GetOffsetEUString(AnalogInputDASChannel aidc, Common.DAS.Concepts.DataScaler scaler,
float [] measuredOffset, int channelIndex, IDiagnosticResult diagnosticResult)
{
var isUnsigned = DoesChannelStreamUnsigned(aidc);
var adcToEU = scaler.GetAdcToEuScalingFactor();
var midPointRemoval = 0;
//FB15339 take the final offset ADC value and normalize it to zero rather than the ADC midpoint before converting to EU
var offset = (scaler.GetEU((short)diagnosticResult.FinalOffsetADC) - midPointRemoval).ToString("F11");
if (aidc.ZeroMethod == ZeroMethodType.None)
{
//18806 & 44255
//the above offset takes into consideration midpoint, initial EU, AND final offset
//with none we don't want the final offset, but we want the midpoint and initial EU
offset = (-1D * midPointRemoval + aidc.InitialEU).ToString("F11");
}
AdjustOffsetForEUAtMv(ref offset, aidc, midPointRemoval, scaler, diagnosticResult);
if (measuredOffset != null)
{
//we _do_ have a measured offset, but in the case of TSR AIR that's actually the EU
//AND if we set return it as the offset it's going to double the reading ... this could be the same problem with TC
//so lets just return 0 for signed das ...
return isUnsigned ? measuredOffset[channelIndex].ToString() : "0.00";
}
else
{
return RunTestVariables.MaskEUMetaData ? "0" : offset;
}
}
private static void AdjustOffsetForEUAtMv(ref string offset, AnalogInputDASChannel aidc, double midPointRemoval, Common.DAS.Concepts.DataScaler scaler,
IDiagnosticResult diagnosticResult)
{
try
{
if (null == aidc) { return; }
if (null == scaler) { return; }
if (string.IsNullOrWhiteSpace(aidc.InitialOffset)) { return; }
var io = new InitialOffset();
io.FromDbSerializeString(aidc.InitialOffset);
if (io.Form != InitialOffsetTypes.EUAtMV) { return; }
//DataScaler should take care of most of the work here, if we feed it in the final offset ADC it should adjust that by the eu@mv difference
var eu = scaler.GetEU((short)diagnosticResult.FinalOffsetADC);
offset = ( eu - midPointRemoval).ToString("F11");
}
catch( Exception ex)
{
APILogger.Log(ex);
}
}
/// <summary>
/// Writes the TMT file to the S6A
/// 14531 Implement TMATS support for S6A stream on boot
/// </summary>
public virtual void WriteTmtFile(IDASCommunication das, Dictionary<IDASCommunication, ushort> dataChannelIds, Dictionary<IDASCommunication, ushort> timeChannelIds,
Dictionary<IDASCommunication, ushort> uartChannelIds, int dasIndex, ICommunication communication,
float[] scaleFactors = null, float[] ranges = null, float[] measuredOffset = null)
{
if ( das is EthernetSlice6AirThermocoupler)
{
//temporary hack to allow all TC channels
MaxChannels = 24;
}
var template = GetTMTTemplate(Template);
var globalKeys = Enum.GetValues(typeof(TMTGlobalKeys)).Cast<TMTGlobalKeys>()
.ToArray();
// FB 26736 Get time & data channel Ids from dictionary based on serial
ushort? dataChannelId = null;
ushort? timeChannelId = null;
ushort? uartChannelId = null;
var dataChannelIdPair = dataChannelIds.FirstOrDefault(p => p.Key.SerialNumber == SerialNumber);
var bitsPerFrame = 32 + 16 * MaxChannels;
if (dataChannelIdPair.Key != null)
{
dataChannelId = dataChannelIdPair.Value;
if (dataChannelIdPair.Key.IsTSRAIR()) { bitsPerFrame = Constants.BITS_PER_MINOR_FRAME_TSRAIR; }
}
var timeChannelIdPair = timeChannelIds.FirstOrDefault(p => p.Key.SerialNumber == SerialNumber);
if (timeChannelIdPair.Key != null)
{
timeChannelId = timeChannelIdPair.Value;
}
//FB43761
var uartChannelIdPair = uartChannelIds.FirstOrDefault(p => p.Key.SerialNumber == SerialNumber);
if (uartChannelIdPair.Key != null)
{
uartChannelId = uartChannelIdPair.Value;
}
foreach (var key in globalKeys)
{
TmtSingleFile.UpdateGlobalField(das, key, template, ConfigData, SerialNumber, timeChannelId, dataChannelId, uartChannelId, dasIndex, bitsPerFrame);
}
for (int i = 0; i < MaxChannels; i++)
{
UpdateTMTChannel(template, i, scaleFactors, ranges, measuredOffset);
}
WriteTMTFile((ICommunication)das, template.GetAllLines(), das.TestDirectory);
}
public static void WriteTMTFile(ICommunication communication, string [] allLines,
string testDirectory)
{
var sfd = new SetFileData(communication);
var byteData = Encoding.UTF8.GetBytes(string.Join("\n", allLines));
var maxFileSize = InformationCommands.MAX_FILE_LENGTH_ID100;
if (communication is ITMATSStreamingDevice tmatsStreamer)
{
maxFileSize = tmatsStreamer.GetMaxFileLengthTMATS();
}
if (byteData.Length >= maxFileSize)
{
throw new ArgumentOutOfRangeException(
$"TMT File is too large, {byteData.Length} >= {maxFileSize}");
}
//15241 store uploaded TMATS file to DASConfigs folder
WriteTMTFilePC(byteData, testDirectory, communication.SerialNumber);
sfd.StartByteCount = 0;
sfd.FileID = SetFileData.TMT_FILE_ID;
sfd.Data = Constants.XML_STORE_MAGIC_BYTES;
sfd.SyncExecute();
//Store Header - data length
var maxLen = Convert.ToUInt32(byteData.Length);
sfd.StartByteCount = Constants.XML_HEADER_LENGTH / 2;
sfd.FileID = SetFileData.TMT_FILE_ID;
sfd.Data = BitConverter.GetBytes(maxLen);
sfd.SyncExecute();
//Store Data
for (uint i = 0; i < maxLen; i += (uint)sfd.MaximumFileStreamBytes)
{
long array_size = sfd.MaximumFileStreamBytes;
if ((i + sfd.MaximumFileStreamBytes) > maxLen)
{
array_size = maxLen - i;
}
var dataToSend = new byte[array_size];
Array.Copy(byteData, i, dataToSend, 0, array_size);
sfd.Data = dataToSend;
sfd.FileID = SetFileData.TMT_FILE_ID;
sfd.StartByteCount = i + Constants.XML_HEADER_LENGTH;
sfd.SyncExecute();
}
}
//FB 30035 Refactored to create TmtFile object and return it based on profile
/// <summary>
/// returns a new template file object given a udp stream profile
/// 29430 Store TMAT file that corresponds to Stream Profile
/// </summary>
/// <param name="profile"></param>
/// <param name="serialNumber"></param>
/// <param name="dasInfo"></param>
/// <param name="configData"></param>
/// <param name="channelDiagnosticsResults"></param>
/// <returns></returns>
public static ITmtFile GetS6ATMATSFileTypeForProfile(UDPStreamProfile profile, string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults)
{
switch (profile)
{
case UDPStreamProfile.CH10_ANALOG:
case UDPStreamProfile.CH10_ANALOG_2HDR:
return new Slice6AirAnalogTmtFile(serialNumber, dasInfo, configData, channelDiagnosticsResults);
case UDPStreamProfile.CH10_PCM128_MM:
case UDPStreamProfile.CH10_PCM_128BIT_2HDR:
case UDPStreamProfile.CH10_PCM_STANDARD:
case UDPStreamProfile.CH10_PCM_STANDARD_2HDR:
case UDPStreamProfile.CH10_PCM_SUPERCOM:
case UDPStreamProfile.CH10_PCM_SUPERCOM_2HDR:
return new Slice6AirPcmTmtFile(serialNumber, dasInfo, configData, channelDiagnosticsResults);
//FB 28292 "TmNS 144 bit PCM" and "TmNS Supercom 4x ADC PCM" profiles
case UDPStreamProfile.TMNS_PCM_STANDARD:
return new Slice6AirTmNs144PcmTmtFile(serialNumber, dasInfo, configData, channelDiagnosticsResults);
case UDPStreamProfile.TMNS_PCM_SUPERCOM:
return new Slice6AirTmNsSuperCom4XPcmTmtFile(serialNumber, dasInfo, configData, channelDiagnosticsResults);
default:
APILogger.Log($"GetTMATSTypeForProfile unsupported profile type: {profile}");
return new Slice6AirAnalogTmtFile(serialNumber, dasInfo, configData, channelDiagnosticsResults);
}
}
}
public class Slice6AirAnalogTmtFile : TmtFile
{
public Slice6AirAnalogTmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults) :
base(serialNumber, dasInfo, configData, channelDiagnosticsResults)
{
MaxChannels = 6;
Template = TMAT_TEMPLATES.S6Air_ANALOG;
}
}
public class Slice6AirPcmTmtFile : TmtFile
{
public Slice6AirPcmTmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults) :
base(serialNumber, dasInfo, configData, channelDiagnosticsResults)
{
MaxChannels = 6;
Template = TMAT_TEMPLATES.S6Air_PCM;
}
}
public class Slice6AirTmNs144PcmTmtFile : TmtFile
{
public Slice6AirTmNs144PcmTmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults) :
base(serialNumber, dasInfo, configData, channelDiagnosticsResults)
{
MaxChannels = 6;
Template = TMAT_TEMPLATES.S6Air_TmNS_144PPCM;
}
}
public class Slice6AirTmNsSuperCom4XPcmTmtFile : TmtFile
{
public Slice6AirTmNsSuperCom4XPcmTmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnosticsResults) :
base(serialNumber, dasInfo, configData, channelDiagnosticsResults)
{
MaxChannels = 6;
Template = TMAT_TEMPLATES.S6Air_TmNS_SuperCom4xPCM;
}
}
}