using DTS.Common;
using DTS.Common.Behaviors;
using DTS.Common.Classes.Sensors;
using DTS.Common.Classes.TMAT;
using DTS.Common.Enums;
using DTS.Common.Enums.DASFactory;
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;
using System.Threading.Tasks;
namespace DTS.DASLib.Service.Classes
{
public interface ITmtFile
{
///
/// Writes the TMT file to the device
///
void WriteTmtFile(IDASCommunication das, Dictionary dataChannelIds, Dictionary timeChannelIds, int dasIndex, Common.Interface.DASFactory.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 ITMTTemplate GetTMTTemplate(TMAT_TEMPLATES tmat_TEMPLATES)
{
var tmtFileName = StringMetaDataAttr.GetStringMetaData(tmat_TEMPLATES);
if (string.IsNullOrWhiteSpace(tmtFileName))
{
tmtFileName = "S6ATMTTemplate_PCM.tmt";
}
var path = $"TMTTemplates/{tmtFileName}";
return new TMTTemplate(path);
}
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; }
///
/// writes tmats file to the DASConfigs log directory, file is serial_TMT.txt
///
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: return false;
default: return true;
}
}
///
/// writes a channel to the template in memory
///
protected void UpdateTMTChannel(ITMTTemplate template, int channelIndex, float[] scaleFactors = null, float[] ranges = null, float[] measuredOffset = null)
{
var channelKeys = Enum.GetValues(typeof(TMTChannelKeys)).Cast()
.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();
var scaler = aidc.GetDataScaler();
var tempEU = scaler.GetEU(short.MinValue);
var tempEU2 = scaler.GetEU(short.MaxValue);
var minEU = Math.Min(tempEU, tempEU2);
var maxEU = Math.Max(tempEU, tempEU2);
foreach (var key in channelKeys)
{
switch (key)
{
case TMTChannelKeys.HardwareChannelNumber:
template.UpdateValue(key, number, 1 + channelIndex);
break;
case TMTChannelKeys.ChannelName:
template.UpdateValue(key, TMTTemplate.TMT_LimitString(aidc.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,
RunTestVariables.MaskEUMetaData ? "---" : aidc.EngineeringUnits?.Trim() ?? "",
1 + channelIndex);
break;
case TMTChannelKeys.ScaleFactorEU:
if (scaleFactors != null)
{
template.UpdateValue(key, scaleFactors[channelIndex].ToString("F11"), 1 + channelIndex);
}
else
{
template.UpdateValue(key, RunTestVariables.MaskEUMetaData ? "1" : scaler.GetAdcToEuScalingFactor().ToString("F11"),
1 + channelIndex);
}
break;
case TMTChannelKeys.OffsetEU:
{
var offsetEU = GetOffsetEUString(aidc, scaler, measuredOffset, channelIndex, diagnosticResult);
template.UpdateValue(key, offsetEU, 1 + channelIndex);
}
break;
}
}
}
///
/// returns a string suitable for the TMATS file for the EU offset for a channel
///
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 = isUnsigned ? adcToEU * Constants.ADC_MIDPOINT : 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)
{
return measuredOffset[channelIndex].ToString();
}
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);
}
}
///
/// Writes the TMT file to the S6A
/// 14531 Implement TMATS support for S6A stream on boot
///
public virtual void WriteTmtFile(IDASCommunication das, Dictionary dataChannelIds, Dictionary timeChannelIds, int dasIndex, Common.Interface.DASFactory.ICommunication communication,
float[] scaleFactors = null, float[] ranges = null, float[] measuredOffset = null)
{
var template = GetTMTTemplate(Template);
var globalKeys = Enum.GetValues(typeof(TMTGlobalKeys)).Cast()
.ToArray();
// FB 26736 Get time & data channel Ids from ddictionary based on serial
ushort? dataChannelId = null;
ushort? timeChannelId = null;
var dataChannelIdPair = dataChannelIds.FirstOrDefault(p => p.Key.SerialNumber == SerialNumber);
var bitsPerFrame = Constants.BITS_PER_MINOR_FRAME_S6A;
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;
}
foreach (var key in globalKeys)
{
TMTTemplate.UpdateGlobalField(das, key, template, ConfigData, SerialNumber, timeChannelId, dataChannelId, 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));
if (byteData.Length >= InformationCommands.MAX_FILE_LENGTH_ID100)
{
throw new ArgumentOutOfRangeException(
$"TMT File is too large, {byteData.Length} >= {InformationCommands.MAX_FILE_LENGTH_ID100}");
}
//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
///
/// returns a new template file object given a udp stream profile
/// 29430 Store TMAT file that corresponds to Stream Profile
///
///
///
///
///
///
///
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;
}
}
}