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

1758 lines
86 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using DataPROWin7.Common;
using DTS.Common.Classes.Hardware;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Service;
using DTS.Common.SharedResource.Strings;
using System.Threading;
using DataPROWin7.DataModel.Classes.Hardware;
using DTS.Common.Interface.Channels;
using DTS.Common.Interface.Groups.GroupList;
using DTS.Common.Interface.DASFactory.Config;
using DTS.Common.Enums.DASFactory;
using DTS.Common.Enums;
using DTS.Slice.Users.UserSettings;
using DTS.Common.Enums.Sensors;
using DTS.Common.Classes.Groups;
using DTS.SensorDB;
using DTS.Common.Classes.Sensors;
using DTS.Common.Constant.DASSpecific;
using DTS.Common.Enums.Hardware;
using DTS.Common.Classes.Groups.ChannelSettings;
using DTS.Common.DataModel.Common;
using DTS.Common.Enums.TSRAIRGo;
using DTS.Common.DataModel.Classes.TSRAIRGo;
using DTS.Common.Interface.Sensors.AnalogDiagnostics;
using DTS.Common;
namespace DataPROWin7.DataModel.Classes
{
public class LevelTriggerCapableChannel
{
public HardwareChannel HardwareChannel { get; }
public string DASOrModuleSerialNumber
{
get
{
switch (HardwareChannel.Hardware.DASTypeEnum)
{
case HardwareTypes.TDAS_Pro_Rack:
case HardwareTypes.TDAS_LabRack:
//need module Serial Number
return HardwareChannel.ModuleSerialNumber;
default:
return HardwareChannel.Hardware.SerialNumber;
}
}
}
private readonly SensorData _sd;
private readonly IGroupChannel _groupChannel;
private DTS.Common.ISO.LevelTriggerChannel _testSetupLevelTrigger = null;
public LevelTriggerCapableChannel(HardwareChannel hwch,
SensorData sd,
SensorCalibration sc,
IGroup group,
IGroupChannel groupChannel)
{
HardwareChannel = hwch;
_sd = sd;
sd.Calibration = sc;
_groupChannel = groupChannel;
}
public double LessThanValue { get; private set; } = 0D;
public double GreaterThanValue { get; private set; } = 0D;
private bool _isLessThanThresholdEnabled = true;
public bool IsLessThanThresholdEnabled
{
get => _isLessThanThresholdEnabled;
set
{
_isLessThanThresholdEnabled = value;
if (null == _testSetupLevelTrigger) return;
_testSetupLevelTrigger.LessThanEnabled = value;
}
}
private bool _isGreaterThanThresholdEnabled = false;
public bool IsGreatherThanThresholdEnabled
{
get => _isGreaterThanThresholdEnabled;
set
{
_isGreaterThanThresholdEnabled = value;
if (null == _testSetupLevelTrigger) return;
_testSetupLevelTrigger.GreaterThanEnabled = value;
}
}
public double InsideUpperBoundValue { get; private set; } = 0D;
public double InsideLowerBoundValue { get; private set; } = 0D;
public double OutsideUpperBoundValue { get; private set; } = 0D;
public double OutsideLowerBoundValue { get; private set; } = 0D;
private bool _bTriggerBetweenBounds = false;
private bool _bTriggerOutsideBounds = false;
public DTS.Common.ISO.LevelTriggerChannel ToISOLevelTriggerChannel()
{
var lt = new DTS.Common.ISO.LevelTriggerChannel(
_groupChannel.Id.ToString(),
HardwareChannel.GetId(),
_sd.SerialNumber,
!SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) && _isGreaterThanThresholdEnabled,
GreaterThanValue,
!SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) && _isLessThanThresholdEnabled,
LessThanValue,
InsideLowerBoundValue,
InsideUpperBoundValue,
OutsideLowerBoundValue,
OutsideUpperBoundValue,
SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) || _bTriggerOutsideBounds,
_bTriggerBetweenBounds);
lt.GroupChannel = _groupChannel;
return lt;
}
public void FromISOLevelTriggerChannel(DTS.Common.ISO.LevelTriggerChannel channel)
{
LessThanValue = channel.LessThanThresholdEU;
GreaterThanValue = channel.GreaterThanThresholdEU;
_isGreaterThanThresholdEnabled = !SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) && channel.GreaterThanEnabled;
_isLessThanThresholdEnabled = !SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) && channel.LessThanEnabled;
_testSetupLevelTrigger = channel;
InsideUpperBoundValue = channel.InsideUpperLevelEU;
InsideLowerBoundValue = channel.InsideLowerLevelEU;
OutsideUpperBoundValue = channel.OutsideUpperLevelEU;
OutsideLowerBoundValue = channel.OutsideLowerLevelEU;
_bTriggerBetweenBounds = channel.TriggerBetweenBounds;
_bTriggerOutsideBounds = SensorConstants.IsTSRAirHighGChannel(HardwareChannel.ModuleSerialNumber) || channel.TriggerOutsideBounds;
}
}
public class Diagnostics
{
private readonly Configuration configuration = new Configuration();
public Diagnostics()
{
}
public bool Reset(DataModel.TestTemplate currentTest,
List<IDASCommunication> dasList,
Dictionary<string, int> dasSampleRateList,
DASHardware[] hardware,
StatusHelpers.SetProgressValueDelegate setProgressFunction,
DTS.Slice.Users.User currentUser)
{
DASHardware.MarkAllDASUnclean(dasList);
ConnectIfNeededPartial(dasList);
if (DataNeverDownloaded(dasList))
{
return false;
}
else
{
return true;
}
}
public void ContinueReset(DataModel.TestTemplate currentTest,
List<IDASCommunication> dasList,
Dictionary<string, int> dasSampleRateList,
DASHardware[] hardware,
StatusHelpers.SetProgressValueDelegate setProgressFunction,
DTS.Slice.Users.User currentUser)
{
UpdateConfigAndPrepareForDiagnostics(currentTest, dasList, dasSampleRateList, hardware, setProgressFunction, currentUser);
RunDiagnostics(dasList, hardware);
}
private void ConnectIfNeededPartial(List<IDASCommunication> dasList)
{
var maxQueryConfigTimeout = DetermineMaxQueryConfigTime();
StartAutoResolutionPartial(dasList, maxQueryConfigTimeout);
}
private const int TSRAIRGO_QUERYCONFIG_TIMEOUT = 150000;
private int DetermineMaxQueryConfigTime()
{
if (RunTestVariables.IsTSRAIRGo)
{
return TSRAIRGO_QUERYCONFIG_TIMEOUT;
}
int maxQueryConfigTimeout = 0;
maxQueryConfigTimeout = Math.Max(maxQueryConfigTimeout,
SerializedSettings.ResolveChannels_SLICE_QueryConfigTimeoutSec * 1000);
return maxQueryConfigTimeout;
}
private void StartAutoResolutionPartial(List<IDASCommunication> dasList, int maxQueryConfigTimeout)
{
BuildLookups(dasList, maxQueryConfigTimeout);
}
private void BuildLookups(List<IDASCommunication> dasList, int maxQueryConfigTimeout)
{
QueryConfigurationPartial(dasList, maxQueryConfigTimeout);
}
private void QueryConfigurationPartial(List<IDASCommunication> das, int maxQueryConfigTimeout)
{
try
{
var doneEvent = new ManualResetEvent(false);
using (var cs = new ConfigurationService())
{
cs.ServiceCallbackError += CS_ServiceCallbackError;
cs.GetConfiguration(das, true, delegate (ServiceBase.CallbackData data)
{
switch (data.Status)
{
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
doneEvent.Set();
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
doneEvent.Set();
break;
}
}, das);
var totalTimeWaited = 0;
while (totalTimeWaited < maxQueryConfigTimeout && !doneEvent.WaitOne(100, false))
{
totalTimeWaited += 100;
}
if (totalTimeWaited >= maxQueryConfigTimeout)
{
cs.Cancel();
APILogger.Log("timeout");
throw new TimeoutException();
}
}
}
catch (Exception ex)
{
APILogger.Log("QueryConfig failed: " + ex.Message);
throw;
}
}
private static void CS_ServiceCallbackError(object sender, string msg, Exception caught)
{
APILogger.Log("error querying configuration", msg);
}
private void UpdateConfigAndPrepareForDiagnostics(DataModel.TestTemplate currentTest,
List<IDASCommunication> ldas,
Dictionary<string, int> dasSampleRateList, DASHardware[] hardware,
StatusHelpers.SetProgressValueDelegate setProgressFunction,
DTS.Slice.Users.User currentUser)
{
try
{
//Store the entire Test Setup .xml in the SETUP folder
ExportCurrentTestSetup(currentTest, currentUser);
//Also, store a portion (the DAS and Fields) of the Test Setup on the DAS (in the TestSetup filestore).
var testSetupXML = ExportCurrentTestSetupFields(currentTest);
if (testSetupXML != null)
{
StoreTestSetup(ldas, testSetupXML);
}
}
catch (Exception ex) { APILogger.Log(ex); }
try
{
var channelLookup = new Dictionary<string, HardwareChannel>();
var channelsForDas = new Dictionary<int, List<HardwareChannel>>();
foreach (var group in currentTest.Groups)
{
AddChannelsForDAS(channelsForDas, channelLookup, hardware);
}
var dasLookup = new Dictionary<string, DASHardware>();
foreach (var h in hardware) { dasLookup[h.SerialNumber] = h; }
foreach (var idas in ldas)
{
DASHardware h = null;
if (dasLookup.ContainsKey(idas.SerialNumber))
{
h = dasLookup[idas.SerialNumber];
}
if (null == h)
{
h = DASHardwareList.GetList()
.GetHardware(idas.SerialNumber, ((ICommunication)idas).ConnectString);
}
if (!channelsForDas.ContainsKey(h.DASId)) continue;
foreach (var ch in channelsForDas[h.DASId])
{
ch.DiagnosticStatus = DiagnosticStatus.Untested;
}
}
UpdateConfig(currentTest, ldas, hardware, setProgressFunction, currentUser);
PrepareForDiagnostics(ldas, dasSampleRateList);
}
catch (Exception ex) { APILogger.Log(ex); }
}
private static void AddChannelsForDAS(Dictionary<int, List<HardwareChannel>> channelsForDas, Dictionary<string, HardwareChannel> channelLookup, DASHardware[] hardwares)
{
foreach (var h in hardwares)
{
if (!channelsForDas.ContainsKey(h.DASId))
{
channelsForDas.Add(h.DASId, new List<HardwareChannel>());
}
foreach (var ch in h.Channels)
{
if (null == ch.Sensor) continue;
if (ch.IsClock) continue;
var key = $"{h.DASId}_{ch.ChannelNumber}";
if (!channelLookup.ContainsKey(key))
{
channelLookup[key] = ch;
channelsForDas[h.DASId].Add(ch);
}
}
}
}
private void ExportCurrentTestSetup(DataModel.TestTemplate currentTest,
DTS.Slice.Users.User currentUser)
{
var path = System.IO.Path.Combine(currentTest.TestDirectory.Trim(), "SETUP");
try
{
if (!System.IO.Directory.Exists(path))
{
System.IO.Directory.CreateDirectory(path);
}
var filename = System.IO.Path.Combine(path, string.IsNullOrEmpty(currentTest.TestId) ? "TestSetup.xml" : $"{currentTest.TestId}.xml");
if (System.IO.File.Exists(filename))
{
System.IO.File.Delete(filename);
}
var includedTests = new Dictionary<string, DataModel.TestTemplate>();
var includedGroups = new Dictionary<string, IGroup>();
var includedDAS = new Dictionary<string, DASHardware>();
var includedSensors = new Dictionary<string, SensorData>();
var sensorsAlreadyAdded = new HashSet<int>();
var includedSensorModels = new Dictionary<string, SensorModel>();
var includedCalibration = new Dictionary<string, SensorCalibration[]>();
var includedCustomerDetails = new Dictionary<string, DTS.Common.ISO.CustomerDetails>();
var includedTestEngineerDetails = new Dictionary<string, DTS.Common.ISO.TestEngineerDetails>();
var includedLabDetails = new Dictionary<string, DTS.Common.ISO.LabratoryDetails>();
var includedUsers = new Dictionary<string, DTS.Slice.Users.User>();
var includedGlobalSettings = new Dictionary<string, string>();
ExportTestSetup.PrepareForExport(currentTest, includedTests,
includedGroups,
includedDAS,
includedSensors,
sensorsAlreadyAdded,
includedSensorModels,
includedCalibration,
includedCustomerDetails,
includedTestEngineerDetails,
includedLabDetails,
true,
false
);
ExportTestSetup.ExportToFile(includedTests,
includedGroups,
includedDAS,
includedSensors,
includedSensorModels,
includedCalibration,
includedCustomerDetails,
includedTestEngineerDetails,
includedLabDetails,
includedUsers,
includedGlobalSettings,
filename,
string.Empty
);
//Store as the latest Test Setup
Defaults.SetUserSetting(currentUser.Id, PropertyEnums.PropertyIds.LastRunTestSetup, filename);
currentTest.SetupFile = filename;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
private string ExportCurrentTestSetupFields(DataModel.TestTemplate currentTest)
{
try
{
var includedTests = new Dictionary<string, DataModel.TestTemplate>();
var includedDAS = new Dictionary<string, DASHardware>();
//Remove the Test Channels group
var tempCurrentTest = new DataModel.TestTemplate(currentTest);
tempCurrentTest.Groups.Clear();
ExportTestSetup.PrepareForExportFields(tempCurrentTest, includedTests, includedDAS);
return ExportTestSetup.ExportToFileFields(includedTests,
includedDAS,
string.Empty
);
}
catch (Exception ex)
{
APILogger.Log(ex);
return null;
}
}
private void StoreTestSetup(List<IDASCommunication> dasList, string testSetupXML)
{
using (var configService = new ConfigurationService())
{
var mreLocal = new ManualResetEvent(false);
configService.StoreTestSetupXML(dasList, delegate (ServiceBase.CallbackData cbd)
{
switch (cbd.Status)
{
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mreLocal.Set();
break;
}
}, dasList, testSetupXML);
mreLocal.WaitOne();
}
}
private void UpdateConfig(DataModel.TestTemplate currentTest,
List<IDASCommunication> ldas,
DASHardware[] hardware,
StatusHelpers.SetProgressValueDelegate setProgressFunction,
DTS.Slice.Users.User currentUser)
{
SortOutConfig(currentTest, ldas, true, hardware, currentUser);
var exceptionThrown = false;
//Check the AAFilter rate
var OkToProceed = true;
try
{
OkToProceed = CheckAAFilterRate(ldas);
}
catch (Exception ex)
{
APILogger.Log("UpdateConfig - failed to checkaafilterrate", ex);
exceptionThrown = true;
}
if (exceptionThrown || !OkToProceed)
{
return; //we have failed
}
foreach (var das in ldas)
{
setProgressFunction(das, 0D, TSRAIRGoStatus.StatusTypes.UPDATING_DAS_CONFIG);
}
configuration.SetConfig(currentTest, ldas, true, setProgressFunction);
}
public delegate void ReportErrorsDelegate(List<string> errors);
/// <summary>
/// Sorts out analog channels in the test setup versus the AnalogInputDASchannel object and sets properties on the daschannel
/// This function was in multiple places with near identical code, I brought it together to one place and abstracted it out of the
/// huge SortOutConfig function
/// </summary>
/// <param name="dasChannel"></param>
/// <param name="currentTest"></param>
/// <param name="harwarechanneltoAbsoluteDisplayOrder"></param>
/// <param name="key"></param>
/// <param name="moduleChannelNumber"></param>
/// <param name="channelLookup"></param>
/// <param name="das"></param>
/// <param name="h"></param>
/// <param name="allHardware"></param>
/// <param name="mod"></param>
/// <param name="testObjectChannelIdToGroup"></param>
/// <param name="ReportErrors"></param>
/// <returns></returns>
public static bool SortOutConfigAnalog(AnalogInputDASChannel dasChannel,
string key, int moduleChannelNumber,
IDASCommunication das, DASHardware h,
IDASModule mod, SortOutConfigParams soParams, ReportErrorsDelegate ReportErrors = null)
{
if (null == dasChannel) { return false; }
APILogger.DebugLog($"SortOutConfig hc={dasChannel?.HardwareChannelName ?? "N/A"}, key={key}, das={das?.SerialNumber??"N/A"}, h={h?.SerialNumber??"N/A"}");
if (soParams.ContainsHardwareChannelDisplayOrder(key))
{
dasChannel.AbsoluteDisplayOrder = soParams.GetHardwareChannelAbsoluteDisplayOrder(key);
}
dasChannel.ModuleChannelNumber = moduleChannelNumber;
var hc = soParams.GetHardwareChannel(key);
var sensor = hc.Sensor;
SensorCalibration sc = null;
//SensorID is the EID of the sensor, and is saved in the config so
//that if the associated DAS is connected to a different computer,
//this test-used EID can be found. This is helpful, for example,
//when the Quick build button is used to create a new Test Setup and
//the same sensor is connected to a different DAS. Prior to this
//saving in the config, only the "live" EID was known when reading the config.
dasChannel.SensorID = sensor.EID;
dasChannel.SetupEID = sensor.EID;
if ( null != dasChannel.IDs && dasChannel.IDs.Length > 0 && null != dasChannel.IDs[0] && dasChannel.IDs[0].IsValid())
{
dasChannel.DataCollectionEID = dasChannel.IDs[0].ID;
}
foreach (var se in sensor.SupportedExcitation)
{
//for now for thermocouples just accept any excitation
if (!dasChannel.IsSupported(se) && !sensor.IsThermocoupler())
{
APILogger.DebugLog($"SortOutConfig hc={dasChannel?.HardwareChannelName ?? "N/A"}, {se.ToString()} not supported on channel");
continue;
}
sc = SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sensor, se);
if (null != sc) { break; }
else
{
APILogger.DebugLog($"SortOutConfig hc={dasChannel?.HardwareChannelName ?? "N/A"}, no sensor calibration for {se.ToString()}");
}
}
if (null == sc)
{
APILogger.DebugLog($"SortOutConfig hc={dasChannel?.HardwareChannelName ?? "N/A"}, no calibration!");
return false;
}
// 14055 use non-linear cal if we're a single-cal'd sensor or if we're a dual-cal and behavior isn't to use linear
var bUseNonLinear = !sc.NonLinear || !sc.LinearAdded || CalibrationBehaviors.LinearIfAvailable !=
soParams.TestTemplate.CalibrationBehavior;
if (sensor.Bridge == SensorConstants.BridgeType.IEPE)
{
dasChannel.CouplingMode = sensor.CouplingMode;
}
dasChannel.ACCouplingModeEnabled = sensor.ACCouplingModeEnabled;
if (null != hc.GroupChannel)
{
var groupChannel = (GroupChannel)hc.GroupChannel;
if (groupChannel.IsTSRAIRLowG)
{
if (groupChannel.ACCouplingEnabled)
{
dasChannel.ACCouplingModeEnabled = true;
dasChannel.CouplingMode = SensorConstants.CouplingModes.AC;
}
else
{
dasChannel.ACCouplingModeEnabled = false;
dasChannel.CouplingMode = SensorConstants.CouplingModes.DC;
}
}
}
dasChannel.BridgeResistanceOhms = sensor.BridgeResistance;
if (sc.NonLinear && bUseNonLinear &&
sc.IRTraccCalculationType ==
NonLinearStyles.IRTraccCalFactor)
{
if (!Equals(sc.Records.Records[0].Poly.CalibrationFactor, 0.0))
{
dasChannel.ZeroPoint = sc.Records.Records[0].Poly.ZeroPositionIntercept /
sc.Records.Records[0].Poly.CalibrationFactor;
}
else
{
dasChannel.ZeroPoint = 0.0;
}
}
else
{
dasChannel.ZeroPoint = sc.Records.Records[sc.Records.Records.Count() - 1].ZeroPoint;
}
dasChannel.BypassAAFilter = sensor.ByPassFilter;
dasChannel.CalSignalIsEnabled = sensor.CalSignal;
dasChannel.Description = sensor.Comment;
dasChannel.Manufacturer = sensor.Manufacturer;
dasChannel.Model = sensor.Model;
dasChannel.OriginalChannelName = hc.ChannelName;
dasChannel.ChannelName2 = hc.DisplayName;
dasChannel.ChannelId = hc.GroupChannel.Id.ToString();
dasChannel.ChannelGroupName = hc.GroupChannel.GroupName;
dasChannel.HardwareChannelName = hc.GetIdSimple(soParams.AllHardware);
dasChannel.SensorCapacity = sensor.Capacity;
dasChannel.SensorPolarity = sensor.Polarity;
dasChannel.DesiredRangeWithHeadroomEU = sensor.Capacity * SerializedSettings.DesiredRangeOverheadPercent *
MeasurementUnitList.GetMeasurementUnit(sc.Records.Records[0].EngineeringUnits).GetScalerConversionFrom(sensor.DisplayUnit);
if (sensor.UniPolar)
{
//we only want half the above range
//note SLICEWare does this slightly different and uses capacity/2D, but I think it's better to have the safety net of the headroom
dasChannel.DesiredRangeWithHeadroomEU /= 2D;
}
dasChannel.Excitation = GetExcitationVoltageEnum(sc, hc.GroupChannel, soParams.GetChannelLookup(), sensor);
//25452 non optimal gain selected for non-linear sensors
if (sc.NonLinear && bUseNonLinear)
{
//we have to pick a gain by doing some guesswork
var inputRanges = new List<double>(das.GetNominalRanges(sensor.Bridge));
inputRanges.Sort();
//iterate through, find the smallest input range mV that satisfies the desired headroom and min value (0)
var exc = DTS.Common.DAS.Concepts.Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(dasChannel.Excitation);
int i;
for (i = 0; i < inputRanges.Count - 1; i++)
{
double[] eu = { sc.GetPolynomialEU(0, exc), sc.GetPolynomialEU(inputRanges[i], exc) };
//this one is acceptable if both the min and max values are satisfied
//we're ignoring a whole bunch of problems, like negative or inverted outputs, and so on
if (eu[0] <= 1 && eu[1] >= dasChannel.DesiredRangeWithHeadroomEU) { break; }
if (eu[1] <= 1 && eu[0] >= dasChannel.DesiredRangeWithHeadroomEU) { break; }
}
dasChannel.DesiredRangeWithHeadroomEU = inputRanges[i];
}
if (sensor.IsDigitalInput() || sensor.IsDigitalOutput()) { dasChannel.DesiredRangeWithHeadroomEU = sensor.FullScaleCapacity; }
dasChannel.DiagnosticsMode = sensor.DiagnosticsMode;
dasChannel.EngineeringUnits = sc.Records.Records[0].EngineeringUnits;
dasChannel.EventStartTime = DateTime.Now;
dasChannel.InitialOffset = hc.GroupChannel.InitialOffset.ToDbSerializeString();
switch (hc.GroupChannel.InitialOffset.Form)
{
case InitialOffsetTypes.EU:
dasChannel.InitialEU = hc.GroupChannel.InitialOffset.EU; break;
default:
dasChannel.InitialEU = 0D;
break;
}
dasChannel.IsInverted = sensor.Invert;
if (sensor.FilterClassIso == "?")
{
sensor.FilterClassIso = "P"; // "Prefiltered > CFC 1000" (Unfiltered)
}
dasChannel.ISOCode = hc.GroupChannel.IsoCode;
//IEPE should nto be marked as proportional to excitation
dasChannel.IsProportionalToExcitation = sc.IsProportional
&& sensor.Bridge != SensorConstants.BridgeType.IEPE;
dasChannel.AtCapacity = bUseNonLinear ? sc.Records.Records[0].AtCapacity : sc.Records.Records[sc.Records.Records.Count() - 1].AtCapacity;
dasChannel.SensitivityUnits = bUseNonLinear ? sc.Records.Records[0].SensitivityUnits : sc.Records.Records[sc.Records.Records.Count() - 1].SensitivityUnits;
dasChannel.CapacityOutputIsBasedOn = bUseNonLinear ? sc.Records.Records[0].CapacityOutputIsBasedOn : sc.Records.Records[sc.Records.Records.Count() - 1].CapacityOutputIsBasedOn;
dasChannel.UserCode = hc.UserCode;
dasChannel.UserChannelName = hc.UserChannelName;
dasChannel.IsoChannelName = hc.IsoChannelName;
dasChannel.LastCalibrationDate = sc.CalibrationDate;
dasChannel.CalDueDate = sensor.GetDueDate(sc);
dasChannel.DigitalMultiplier = sensor.ScaleMultiplier;
dasChannel.DIUnits = sensor.DIUnits;
dasChannel.DigitalMode = sensor.InputMode;
dasChannel.LinearizationFormula = new LinearizationFormula(bUseNonLinear ? sc.Records.Records[0].Poly : sc.Records.Records[sc.Records.Records.Count() - 1].Poly);
dasChannel.LinearSensorCalibration = bUseNonLinear ? sc.LinearAddedValues : string.Empty;
dasChannel.OffsetToleranceHighMilliVolts = sensor.OffsetToleranceHigh;
dasChannel.OffsetToleranceLowMilliVolts = sensor.OffsetToleranceLow;
dasChannel.RemoveOffset = sc.RemoveOffset;
if (UseACCoupling(sensor, hc))
{
dasChannel.RemoveOffset = false;
}
APILogger.DebugLog($"SortOutConfig hc={dasChannel?.HardwareChannelName ?? "N/A"}, sc={sc?.ToSerializedString() ?? "N/A"}, sensor={sensor?.SerialNumber ?? "N/A"}");
dasChannel.SensitivityMilliVoltsPerEU = GetSensitivity(sc, hc.GroupChannel, soParams.GetChannelLookup(), sensor, bUseNonLinear);
dasChannel.UnitConverision = MeasurementUnitList.GetMeasurementUnit(sc.Records.Records[0].EngineeringUnits).GetScalerConversion(sensor.DisplayUnit);
if (sc.NonLinear && bUseNonLinear && sc.IRTraccCalculationType != NonLinearStyles.Polynomial)
{
dasChannel.SensorCapacityEU = sensor.FullScaleCapacity;
}
else if (sc.NonLinear && bUseNonLinear && sc.IRTraccCalculationType == NonLinearStyles.Polynomial)
{
//why is this being done twice?
//well at least for this type, lets not do it twice.
}
else if (sensor.IsDigitalInput()) { dasChannel.SensorCapacityEU = sensor.Capacity; }
else if (sensor.IsDigitalOutput()) { dasChannel.SensorCapacityEU = sensor.FullScaleCapacity; }
else
{
dasChannel.SensorCapacityEU = sensor.Capacity *
MeasurementUnitList.GetMeasurementUnit(sc.Records.Records[0].EngineeringUnits).GetScalerConversionFrom(sensor.DisplayUnit);
}
dasChannel.SerialNumber = sensor.SerialNumber;
dasChannel.Unipolar = sensor.UniPolar;
dasChannel.ShuntIsEnabled = sensor.PerformShuntEmulation && !sensor.DiagnosticsMode
&& sensor.Bridge != SensorConstants.BridgeType.IEPE && !sensor.IsTestSpecificEmbedded && !sensor.IsTestSpecificThermo;
dasChannel.SoftwareFilterFrequency = sensor.FilterClass.Frequency;
//FB 13120 For now we will continue using the SoftwareFilterFrequency until we move all the refrences to SoftwareFilterClass.
dasChannel.SoftwareFilterClass = sensor.FilterClass;
//33415 Voltage insertion channel should be half bridge
dasChannel.TypeOfBridge = null != hc.GroupChannel && Array.Exists(hc.GroupChannel.ChannelSettings, chset => ChannelSettingBase.BRIDGE_TYPE == chset.SettingName)
? (SensorConstants.BridgeType)Enum.Parse(typeof(SensorConstants.BridgeType), hc.GroupChannel.ChannelSettings.First(chset => ChannelSettingBase.BRIDGE_TYPE == chset.SettingName).Value)
: sensor.Bridge;
dasChannel.UserValue1 = sensor.UserValue1;
dasChannel.UserValue2 = sensor.UserValue2;
dasChannel.UserValue3 = sensor.UserValue3;
dasChannel.VerifyOffset = true;
dasChannel.VoltageInsertionCheckEnabled = false;
//FB14606: Test Setup Specific-ize ZeroMethod parameters
dasChannel.ZeroAverageStartSeconds = hc.GroupChannel.ZeroMethodStart;
dasChannel.ZeroAverageStopSeconds = hc.GroupChannel.ZeroMethodEnd;
dasChannel.ZeroMethod = hc.GroupChannel.ZeroMethod;
if (dasChannel.IEPEChannel != (sensor.Bridge == SensorConstants.BridgeType.IEPE))
{
//IEPE status of channel disagrees with sensor, can we reprogram the DAS?
if (dasChannel.CanReProgram())
{
dasChannel.IEPEChannel = sensor.Bridge == SensorConstants.BridgeType.IEPE;
}
else
{
if (dasChannel.IEPEChannel)
{
ReportErrors?.Invoke(new List<string>(new[]
{
string.Format(StringResources.EditObjectSensorsControl_InvalidIEPEOnNonIEPE, dasChannel.ChannelName2, sensor.SerialNumber)
}));
}
else
{
ReportErrors?.Invoke(new List<string>(new[]
{
string.Format(StringResources.EditObjectSensorsControl_InvalidNonIEPEOnIEPE, dasChannel.ChannelName2, sensor.SerialNumber)
}));
}
//FAIL ?
}
}
dasChannel.TriggerBelowThresholdEu = null;
dasChannel.TriggerAboveThresholdEu = null;
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.NONE;
//the edittestsetupleveltrigger is a convenient wrapper and easier that the isoleveltrigger, so I use it here.
var lt = new LevelTriggerCapableChannel(hc, sensor, sc, soParams.GetGroupFromChannelId(hc.GroupChannel.Id), hc.GroupChannel);
//ReSharper disable once InconsistentNaming
var existingLT = soParams.TestTemplate.GetLevelTrigger(lt.ToISOLevelTriggerChannel());
if (null != existingLT)
{
lt.FromISOLevelTriggerChannel(existingLT);
if (h.IsTSRAIR())
{
if (existingLT.TriggerOutsideBounds)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.OutsideWindow;
dasChannel.TriggerAboveThresholdEu = lt.OutsideUpperBoundValue;
dasChannel.TriggerBelowThresholdEu = lt.OutsideLowerBoundValue;
}
}
else
{
if (existingLT.TriggerBetweenBounds)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.InsideWindow;
dasChannel.TriggerAboveThresholdEu = lt.InsideUpperBoundValue;
dasChannel.TriggerBelowThresholdEu = lt.InsideLowerBoundValue;
}
else if (existingLT.TriggerOutsideBounds)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.OutsideWindow;
dasChannel.TriggerBelowThresholdEu = lt.OutsideLowerBoundValue;
dasChannel.TriggerAboveThresholdEu = lt.OutsideUpperBoundValue;
}
else if (existingLT.GreaterThanEnabled && existingLT.LessThanEnabled)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.GreaterThan | DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.LessThan;
dasChannel.TriggerAboveThresholdEu = lt.GreaterThanValue;
dasChannel.TriggerBelowThresholdEu = lt.LessThanValue;
}
else if (existingLT.LessThanEnabled)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.LessThan;
dasChannel.TriggerBelowThresholdEu = lt.LessThanValue;
}
else if (existingLT.GreaterThanEnabled)
{
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.GreaterThan;
dasChannel.TriggerAboveThresholdEu = lt.GreaterThanValue;
}
}
}
if (h.IsTSRAIR())
{
//18057 set channel's supersampling rate
dasChannel.UnsupersampledSampleRate = soParams.TestTemplate.GetSampleRateForEmbeddedHardware(h, das.DASInfo.Modules[mod.ModuleArrayIndex].TypeOfModule);
}
return true;
}
public static void SortOutConfigCAN(IDASChannel c, int moduleChannelNumber, SortOutConfigParams soParams)
{
var canChannel = c as CANInputDASChannel;
canChannel.ModuleChannelNumber = moduleChannelNumber;
var testChannel = soParams.TestTemplate.ChannelsForGroup.FirstOrDefault().Value;
canChannel.IsFD = testChannel[0].CanIsFD;
canChannel.ArbBaseBitrate = testChannel[0].CanArbBaseBitrate;
canChannel.ArbBaseSJW = testChannel[0].CanArbBaseSJW;
canChannel.DataBitrate = testChannel[0].CanDataBitrate;
canChannel.DataSJW = testChannel[0].CanDataSJW;
canChannel.FileType = testChannel[0].CanFileType;
}
/// <summary>
/// sorts out the squib properties from the test setup and sets properties on the squibchannel
/// </summary>
/// <param name="currentChannelIdx"></param>
/// <param name="mod"></param>
/// <param name="harwarechanneltoAbsoluteDisplayOrder"></param>
/// <param name="key"></param>
/// <param name="moduleChannelNumber"></param>
/// <param name="channelLookup"></param>
/// <param name="c"></param>
/// <param name="currentTest"></param>
/// <param name="das"></param>
public static void SortOutConfigSquib(ref int currentChannelIdx, IDASModule mod,
string key, ref int moduleChannelNumber, IDASChannel c,
IDASCommunication das, SortOutConfigParams soParams)
{
currentChannelIdx++; //It's OK to increment this here because we need to handle both squib channels (Voltage and Current)
OutputSquibChannel squib2 = null;
if (currentChannelIdx < mod.Channels.Length) { squib2 = mod.Channels[currentChannelIdx] as OutputSquibChannel; }
if (null == squib2) { throw new NotSupportedException("require both VO and CU channels"); }
var outputSquibChannel = c as OutputSquibChannel;
if ( soParams.ContainsHardwareChannelDisplayOrder(key))
{
outputSquibChannel.AbsoluteDisplayOrder = soParams.GetHardwareChannelAbsoluteDisplayOrder(key);
squib2.AbsoluteDisplayOrder = outputSquibChannel.AbsoluteDisplayOrder;
}
outputSquibChannel.ModuleChannelNumber = moduleChannelNumber;
moduleChannelNumber++;
squib2.ModuleChannelNumber = moduleChannelNumber;
var hc = soParams.GetHardwareChannel(key);
var sensor = hc.Sensor;
//SensorID is the EID of the sensor, and is saved in the config so
//that if the associated DAS is connected to a different computer,
//this test-used EID can be found. This is helpful, for example,
//when the Quick build button is used to create a new Test Setup and
//the same sensor is connected to a different DAS. Prior to this
//saving in the config, only the "live" EID was known when reading the config.
outputSquibChannel.IDs = new EID[] { new EID(sensor.EID) };
sensor.ISOCode = hc.GroupChannel.IsoCode;
foreach (var se in sensor.SupportedExcitation)
{
var sc = SensorCalibration
.GetLatestCalibrationBySerialNumberAndExcitation(sensor, se);
if (null != sc) { break; }
}
outputSquibChannel.DiagnosticsMode = false;
squib2.DiagnosticsMode = false;
outputSquibChannel.EventStartTime = DateTime.Now;
squib2.EventStartTime = DateTime.Now;
var isocode = new DTS.Common.ISO.IsoCode(sensor.ISOCode)
{
PhysicalDimension = "VO"
};
outputSquibChannel.ISOCode = isocode.StringRepresentation;
isocode.PhysicalDimension = "CU";
squib2.ISOCode = isocode.StringRepresentation;
outputSquibChannel.UserCode = hc.GroupChannel.UserCode;
outputSquibChannel.UserChannelName = hc.GroupChannel.UserChannelName;
outputSquibChannel.IsoChannelName = hc.GroupChannel.IsoChannelName;
squib2.UserCode = outputSquibChannel.UserCode;
squib2.UserChannelName = outputSquibChannel.UserChannelName;
squib2.IsoChannelName = outputSquibChannel.IsoChannelName;
outputSquibChannel.BypassCurrentFilter = sensor.BypassCurrentFilter;
squib2.BypassCurrentFilter = outputSquibChannel.BypassCurrentFilter;
outputSquibChannel.BypassVoltageFilter = sensor.BypassVoltageFilter;
squib2.BypassVoltageFilter = outputSquibChannel.BypassVoltageFilter;
outputSquibChannel.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Normal;
squib2.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Normal;
outputSquibChannel.Date = DateTime.Now;
squib2.Date = DateTime.Now;
outputSquibChannel.DelayMS = sensor.SquibFireDelayMS;
squib2.DelayMS = sensor.SquibFireDelayMS;
outputSquibChannel.DiagnosticsMode = false;
squib2.DiagnosticsMode = false;
outputSquibChannel.DurationMS = sensor.SquibFireDurationMS;
squib2.DurationMS = outputSquibChannel.DurationMS;
outputSquibChannel.LimitDuration = sensor.LimitSquibFireDuration;
squib2.LimitDuration = outputSquibChannel.LimitDuration;
outputSquibChannel.MeasurementType = sensor.SquibMeasurementType;
squib2.MeasurementType = SquibMeasurementType.CURRENT;
outputSquibChannel.FireMode = sensor.SquibFireMode;
squib2.FireMode = outputSquibChannel.FireMode;
outputSquibChannel.SquibDescription = sensor.Comment;
squib2.SquibDescription = sensor.Comment;
outputSquibChannel.SquibFiredPassed = false;
squib2.SquibFiredPassed = false;
outputSquibChannel.SquibFiredValid = false;
squib2.SquibFiredPassed = outputSquibChannel.SquibFiredPassed;
outputSquibChannel.SquibToleranceHigh = sensor.SquibToleranceHigh;
squib2.SquibToleranceHigh = outputSquibChannel.SquibToleranceHigh;
outputSquibChannel.SquibToleranceLow = sensor.SquibToleranceLow;
squib2.SquibToleranceLow = outputSquibChannel.SquibToleranceLow;
outputSquibChannel.SquibOutputCurrent = sensor.SquibOutputCurrent;
squib2.SquibOutputCurrent = outputSquibChannel.SquibOutputCurrent;
outputSquibChannel.UserValue1 = sensor.UserValue1;
squib2.UserValue1 = outputSquibChannel.UserValue1;
outputSquibChannel.UserValue2 = sensor.UserValue2;
squib2.UserValue2 = outputSquibChannel.UserValue2;
outputSquibChannel.UserValue3 = sensor.UserValue3;
squib2.UserValue3 = outputSquibChannel.UserValue3;
outputSquibChannel.ChannelName2 = hc.DisplayName;
outputSquibChannel.ChannelId = hc.GroupChannel.Id.ToString();
outputSquibChannel.Sensor = hc.GroupChannel.SensorData.SerialNumber.ToString();
squib2.SerialNumber = hc.GroupChannel.SensorData.SerialNumber;
outputSquibChannel.SerialNumber = hc.GroupChannel.SensorData.SerialNumber;
outputSquibChannel.ChannelGroupName = hc.GroupChannel.GroupName;
squib2.ChannelId = hc.GroupChannel.Id.ToString() + Constants.CURRENT_SUFFIX;
squib2.Sensor = hc.GroupChannel.SensorData.SerialNumber.ToString() + Constants.CURRENT_SUFFIX;
squib2.ChannelGroupName = hc.GroupChannel.GroupName;
squib2.ChannelName2 = hc.DisplayName;
outputSquibChannel.SetupEID = sensor.EID;
squib2.SetupEID = sensor.EID;
if ( null != outputSquibChannel.IDs && outputSquibChannel.IDs.Length > 0 && null != outputSquibChannel.IDs[0]
&& outputSquibChannel.IDs[0].IsValid())
{
outputSquibChannel.DataCollectionEID = outputSquibChannel.IDs[0].ID;
squib2.DataCollectionEID = outputSquibChannel.DataCollectionEID;
}
if (squib2.SquibDescription.EndsWith(".1"))
{
var newName = squib2.SquibDescription;
newName = newName.Substring(0, newName.Length - 2) + ".2";
squib2.ChannelName2 = squib2.ChannelName2.Replace(squib2.SquibDescription, newName);
}
outputSquibChannel.HardwareChannelName = hc.GetIdSimple();
squib2.HardwareChannelName = hc.GetIdSimple();
//8747 - controls the default software filter for this channel
//by using a harcoded filter, like TDC does
if (SerializedSettings.UseLegacyTOMCFC &&
soParams.TestTemplate.DASSampleRateList[das.SerialNumber] > TestObjectHelper.TDC_LEGACY_TOM_CUTOFF_SPS)
{
outputSquibChannel.SoftwareFilterFrequency = TestObjectHelper.TDC_LEGACY_TOM_HIGH_FILTER;
squib2.SoftwareFilterFrequency = TestObjectHelper.TDC_LEGACY_TOM_HIGH_FILTER;
}
}
/// <summary>
/// sorts out TOMdigital properties from the test setup and sets the properties on the DASChannel
/// </summary>
/// <param name="c"></param>
/// <param name="key"></param>
/// <param name="harwarechanneltoAbsoluteDisplayOrder"></param>
/// <param name="moduleChannelNumber"></param>
/// <param name="channelLookup"></param>
public static void SortOutConfigTOMDigitalChannel(IDASChannel c, string key,
int moduleChannelNumber, SortOutConfigParams soParams)
{
var dOut = c as OutputTOMDigitalChannel;
if (soParams.ContainsHardwareChannelDisplayOrder(key))
{
dOut.AbsoluteDisplayOrder = soParams.GetHardwareChannelAbsoluteDisplayOrder(key);
}
dOut.ModuleChannelNumber = moduleChannelNumber;
var hc = soParams.GetHardwareChannel(key);
var sensor = hc.Sensor;
//SensorID is the EID of the sensor, and is saved in the config so
//that if the associated DAS is connected to a different computer,
//this test-used EID can be found. This is helpful, for example,
//when the Quick build button is used to create a new Test Setup and
//the same sensor is connected to a different DAS. Prior to this
//saving in the config, only the "live" EID was known when reading the config.
dOut.IDs = new EID[] { new EID(sensor.EID) };
dOut.DiagnosticsMode = false;
dOut.EventStartTime = DateTime.Now;
if (sensor.FilterClassIso == "?")
{
sensor.FilterClassIso = "P"; // "Prefiltered > CFC 1000" (Unfiltered)
}
dOut.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Normal;
dOut.DelayMS = sensor.DigitalOutputDelayMS;
dOut.DiagnosticsMode = false;
dOut.DigitalChannelDescription = sensor.SerialNumber;
dOut.LimitDuration = sensor.DigitalOutputLimitDuration;
dOut.DurationMS = dOut.LimitDuration ? sensor.DigitalOutputDurationMS : 0;
dOut.OutputMode = sensor.DigitalOutputMode;
dOut.HardwareChannelName = hc.GetIdSimple();
dOut.IsoChannelName = sensor.ISOChannelName;
}
/// <summary>
/// this is the overall sortoutconfig function that was in multiple places, I tried to encapsulate and clean it up a little
/// </summary>
/// <param name="currentTest"></param>
/// <param name="ldas"></param>
/// <param name="clearDiagnostics"></param>
/// <param name="allHardware"></param>
/// <param name="currentUser"></param>
public static void SortOutConfig(DataModel.TestTemplate currentTest,
List<IDASCommunication> ldas,
bool clearDiagnostics,
DASHardware[] allHardware,
DTS.Slice.Users.User currentUser)
{
//18413 Diagnostics has not been run error message when using trigger check before realtime.
if (clearDiagnostics) { DASHardware.MarkAllDASUnclean(ldas); }
// set the test setup das order index for any das that support it
// used in 14531 Implement TMATS support for S6A stream on boot
var currentDasIndex = 0;
foreach (var das in ldas)
{
if (das is DTS.Common.Interface.TestSetups.ITestDASOrder iTestDASOrder)
{
iTestDASOrder.DASIndex = currentDasIndex++;
}
}
var soParams = new SortOutConfigParams() { TestTemplate = currentTest };
foreach (var group in currentTest.Groups)
{
soParams.AddGroupChannels(currentTest.ChannelsForGroup[group]);
}
foreach (var group in currentTest.Groups)
{
AddAndOrderChannels(group, soParams);
}
currentTest.FilterLookup = soParams.FilterLookup;
soParams.SetAllHardware(currentTest.GetHardware());
foreach (var das in ldas)
{
if (null == das.ConfigData) { continue; }
SortOutConfigDAS(das, currentUser, soParams);
}
}
/// <summary>
/// this was the portion of sort out config that applied to DAS, I abstracted it out to it's own function
/// for readability
/// </summary>
/// <param name="dasSerialToDASHardware"></param>
/// <param name="das"></param>
/// <param name="harwarechanneltoAbsoluteDisplayOrder"></param>
/// <param name="currentUser"></param>
/// <param name="channelLookup"></param>
/// <param name="allHardware"></param>
/// <param name="currentTest"></param>
/// <param name="testObjectChannelIdToGroup"></param>
/// <param name="reportErrors"></param>
public static void SortOutConfigDAS(IDASCommunication das,
DTS.Slice.Users.User currentUser,
SortOutConfigParams soParams,
ReportErrorsDelegate reportErrors = null)
{
if (null == das) { return; }
APILogger.DebugLog($"SortOutConfigDAS {das?.SerialNumber ?? "N/A"}");
if (!soParams.ContainsHardware(das.SerialNumber))
{
//this das is not part of the test (but may have configured channels, we need to set them to not collect data)
foreach (var module in das.ConfigData.Modules)
{
foreach (var c in module.Channels)
{
DisableChannel(c);
}
}
APILogger.DebugLog($"SortOutConfigDAS das={das?.SerialNumber ?? "N/A"} - not part of test, eliminating all channels");
return;
}
var h = soParams.GetHardware(das.SerialNumber);
if (null == h)
{
APILogger.DebugLog($"SortOutConfigDAS das={das?.SerialNumber ?? "N/A"} - no get hardware");
return;
}
var moduleNumber = 0;
var absoluteDisplayMax = 0;
if (soParams.GetHardwareChannelToAbsoluteDisplayOrderCount() > 0)
{
absoluteDisplayMax = soParams.GetMaxAbsoluteDisplayOrder();
}
var modules = new IDASModule[0];
if ( null != das.ConfigData.Modules)
{
modules = das.ConfigData.Modules;
}
foreach (var mod in modules)
{
if (null == mod) { continue; }
APILogger.DebugLog($"SortOutConfigDAS das={das?.SerialNumber ?? "N/A"}, module= {mod?.ModuleArrayIndex??-1}");
//this is just to make sure the profile is intialized, the StreamOutputModule will get set during processing below
mod.StreamProfile = (UDPStreamProfile)Enum.Parse(typeof(UDPStreamProfile),
Defaults.GetUserSettingValueString(currentUser.Id, PropertyEnums.PropertyIds.DefaultUDPStreamProfile));
var moduleChannelNumber = 0;
for (var currentChannelIdx = 0; currentChannelIdx < mod.Channels.Length; currentChannelIdx++)
{
APILogger.DebugLog($"SortOutConfigDAS das={das?.SerialNumber ?? "N/A"}, module= {mod?.ModuleArrayIndex ?? -1}, channel={currentChannelIdx}");
var c = mod.Channels[currentChannelIdx];
var key = $"{h.DASId}_{c.Number}";
var hc = soParams.GetHardwareChannel(key);
if (mod.IsClock())
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Clock;
if (c is TimestampDASChannel tsdasChannel)
{
tsdasChannel.AbsoluteDisplayOrder = ++absoluteDisplayMax;
tsdasChannel.ModuleChannelNumber = moduleChannelNumber;
}
}
else if (null == hc || null == hc.Sensor)
{
APILogger.DebugLog($"SortOutConfigDAS das={das?.SerialNumber ?? "N/A"}, module= {mod?.ModuleArrayIndex ?? -1}, channel={currentChannelIdx} - no sensor on channel or no hardwarechannel");
DisableChannel(c);
}
else if (mod.IsUart())
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.UART;
if (c is UARTInputDASChannel uartChannel)
{
uartChannel.AbsoluteDisplayOrder = ++absoluteDisplayMax;
uartChannel.ModuleChannelNumber = moduleChannelNumber;
uartChannel.SerialNumber = hc.Sensor.SerialNumber;
uartChannel.HardwareChannelName = hc.GetIdSimple(soParams.AllHardware);
}
}
else if (mod.IsStreamOut())
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.StreamOut;
if (c is StreamOutputDASChannel streamOut)
{
streamOut.AbsoluteDisplayOrder = ++absoluteDisplayMax;
streamOut.ModuleChannelNumber = moduleChannelNumber;
streamOut.SerialNumber = hc.Sensor.SerialNumber;
streamOut.HardwareChannelName = hc.GetIdSimple(soParams.AllHardware);
mod.StreamProfile = hc.Sensor.StreamOutUDPProfile;
}
}
else if (mod.IsStreamIn())
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.StreamIn;
if (c is StreamInputDASChannel streamIn)
{
streamIn.AbsoluteDisplayOrder = ++absoluteDisplayMax;
streamIn.ModuleChannelNumber = moduleChannelNumber;
streamIn.SerialNumber = hc.Sensor.SerialNumber;
streamIn.HardwareChannelName = hc.GetIdSimple(soParams.AllHardware);
}
}
else
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Normal;
if (c is AnalogInputDASChannel dasChannel)
{
SortOutConfigAnalog(dasChannel, key, moduleChannelNumber,
das, h, mod, soParams, reportErrors);
//it had been contuing out but that seems wrong -
//for instance if we fail to sort out this channel because of support excitation
//you still want to go onto the next channel ...
}
else if (c is OutputSquibChannel)
{
SortOutConfigSquib(ref currentChannelIdx, mod, key, ref moduleChannelNumber,
c, das, soParams);
}
else if (c is OutputTOMDigitalChannel)
{
SortOutConfigTOMDigitalChannel(c, key, moduleChannelNumber, soParams);
}
else if (c is CANInputDASChannel)
{
SortOutConfigCAN(c, moduleChannelNumber, soParams);
}
}
moduleChannelNumber++;
}
var filterrate = Convert.ToUInt32(soParams.TestTemplate.GetAAFForHardware(h));
mod.AAFilterRateHz = filterrate;
mod.PostTriggerSeconds = soParams.TestTemplate.GetPostTriggerSeconds(das.SerialNumber);
mod.PreTriggerSeconds = soParams.TestTemplate.GetPreTriggerSeconds(das.SerialNumber);
mod.NumberOfEvents = soParams.TestTemplate.NumberOfEvents;
mod.WakeUpMotionTimeout = soParams.TestTemplate.WakeUpMotionTimeout;
mod.RecordingMode = RecordingModeExtensions.FromRecordingModes(soParams.TestTemplate.RecordingMode);
switch (soParams.TestTemplate.RecordingMode)
{
case RecordingModes.Interval:
mod.ScheduledStartTime = soParams.TestTemplate.RTCScheduleStartDateTime;
mod.RecordingInterval = soParams.TestTemplate.IntervalBetweenEventStartsMinutes;
break;
case RecordingModes.Scheduled:
mod.ScheduledStartTime = soParams.TestTemplate.RTCScheduleStartDateTime;
break;
case RecordingModes.MultipleEventCircularBufferPlusUART:
mod.RecordingMode = DFConstantsAndEnums.RecordingMode.AutoCircularBufferPlusUART;
break;
case RecordingModes.MultipleEventRecorderPlusUART:
mod.RecordingMode = DFConstantsAndEnums.RecordingMode.AutoRecorderModePlusUART;
break;
case RecordingModes.ContinuousRecorderPlusUART:
mod.RecordingMode = DFConstantsAndEnums.RecordingMode.ContinuousRecorderModePlusUART;
break;
case RecordingModes.Active:
case RecordingModes.MultipleEventActive:
mod.RecordingMode = SetActiveRecordingMode(soParams.TestTemplate);
break;
}
mod.SampleRateHz = Convert.ToUInt32(soParams.TestTemplate.GetSampleRateForHardware(h));
mod.FirmwareVersion = das.DASInfo.Modules[moduleNumber].FirmwareVersion;
mod.MaxEventStorageSpaceInBytes = das.DASInfo.Modules[moduleNumber].MaxEventStorageSpaceInBytes;
moduleNumber++;
}
das.ConfigData.TestID = soParams.TestTemplate.Name;
das.ConfigData.TestSetupUniqueId = soParams.TestTemplate.TestSetupUniqueId;
das.ConfigData.Description = soParams.TestTemplate.Description;
das.ConfigData.InstanceID = soParams.TestTemplate.TestId;
if ( das is IAlignUDPToPPSAware alignAware)
{
alignAware.AlignUDPToPPS = soParams.TestTemplate.AlignUDPToPPS;
}
}
public class SortOutConfigParams
{
private Dictionary<string, HardwareChannel> _channelLookup = new Dictionary<string, HardwareChannel>();
private readonly Dictionary<string, int> _harwarechanneltoAbsoluteDisplayOrder = new Dictionary<string, int>();
public int GetHardwareChannelToAbsoluteDisplayOrderCount() { return _harwarechanneltoAbsoluteDisplayOrder.Count; }
public bool ContainsHardwareChannelDisplayOrder(string key) { return _harwarechanneltoAbsoluteDisplayOrder.ContainsKey(key); }
public int GetHardwareChannelAbsoluteDisplayOrder(string key)
{
return _harwarechanneltoAbsoluteDisplayOrder.ContainsKey(key) ? _harwarechanneltoAbsoluteDisplayOrder[key] : -1;
}
public int GetMaxAbsoluteDisplayOrder()
{
return _harwarechanneltoAbsoluteDisplayOrder.Values.Max();
}
public Dictionary<string, double> FilterLookup { get; private set; } = new Dictionary<string, double>();
private readonly List<IGroupChannel> _channels = new List<IGroupChannel>();
public void AddGroupChannels(IGroupChannel [] channels)
{
_channels.AddRange(channels);
_channels.Sort((a, b) => a.TestSetupOrder.CompareTo(b.TestSetupOrder));
}
public void SetAbsoluteDisplayOrderFromIndex(string key, IGroupChannel ch)
{
_harwarechanneltoAbsoluteDisplayOrder[key] = _channels.IndexOf(ch);
}
public HardwareChannel GetHardwareChannel(string key)
{
return _channelLookup.ContainsKey(key) ? _channelLookup[key] : null;
}
public void SetHardwareChannel(string key, HardwareChannel channel)
{
_channelLookup[key] = channel;
}
public void SetChannelLookup(Dictionary<string, HardwareChannel> lookup)
{
_channelLookup = lookup;
}
public Dictionary<string, HardwareChannel> GetChannelLookup() { return _channelLookup; }
private readonly Dictionary<long, IGroup> _channelIdToGroup = new Dictionary<long, IGroup>();
public IGroup GetGroupFromChannelId(long id)
{
return _channelIdToGroup.ContainsKey(id) ? _channelIdToGroup[id] : null;
}
public void SetChannelIdToGroup(long id, IGroup group)
{
_channelIdToGroup[id] = group;
}
public DataModel.TestTemplate TestTemplate { get; set; } = new DataModel.TestTemplate();
public DASHardware [] AllHardware { get; private set; }
public void SetAllHardware(DASHardware[] hardware)
{
AllHardware = hardware;
_hardwareToSerialLookup.Clear();
SetHardwareLookupEntries(hardware);
}
public void SetHardwareLookupEntries(DASHardware [] hardware)
{
foreach (var h in hardware) { _hardwareToSerialLookup[h.SerialNumber] = h; }
}
private readonly Dictionary<string, DASHardware> _hardwareToSerialLookup = new Dictionary<string, DASHardware>();
public bool ContainsHardware(string serialNumber) { return _hardwareToSerialLookup.ContainsKey(serialNumber); }
public DASHardware GetHardware(string serialNumber)
{
return _hardwareToSerialLookup.ContainsKey(serialNumber) ? _hardwareToSerialLookup[serialNumber] : null;
}
}
public static void AddAndOrderChannels(IGroup group,
SortOutConfigParams soParams)
{
var hardware = soParams.TestTemplate.GetHardware();
foreach (var h in hardware)
{
foreach (var ch in h.Channels)
{
if (null == ch.Sensor) continue;
var key = $"{h.DASId}_{ch.ChannelNumber}";
soParams.SetHardwareChannel(key, ch);
try
{
if (soParams.FilterLookup.ContainsKey(key))
{
//item with same key is already added, previously spammed the log with this...
}
else
{
soParams.FilterLookup.Add(key, ch.Sensor.Filter.Frequency);
}
}
catch (Exception ex)
{
APILogger.Log("************************** " + ch.GetId() + " " + ex.Message + " **************");
}
}
}
var groupChannels = soParams.TestTemplate.ChannelsForGroup[group].ToList();
groupChannels.Sort((a, b) => a.TestSetupOrder.CompareTo(b.TestSetupOrder));
foreach (var ch in groupChannels)
{
if (ch.SensorId < 0 || ch.DASId < 0 || ch.DASChannelIndex < 0) { continue; }
var key = $"{ch.DASId}_{ch.DASChannelIndex}";
soParams.SetChannelIdToGroup(ch.Id, group);
soParams.SetAbsoluteDisplayOrderFromIndex(key, ch);
}
}
private static void DisableChannel(IDASChannel c)
{
c.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Disabled;
if (c is AnalogInputDASChannel dasChannel)
{
dasChannel.ChannelName2 = "";
dasChannel.SerialNumber = "";
dasChannel.SensorCapacityEU = 1;
dasChannel.SensorCapacity = 0;
dasChannel.SensorPolarity = "";
dasChannel.TriggerAboveThresholdEu = null;
dasChannel.TriggerBelowThresholdEu = null;
dasChannel.LevelTriggerType = DTS.Common.DAS.Concepts.DAS.Channel.LevelTriggerTypes.NONE;
}
else if (c is OutputSquibChannel outputSquibChannel)
{
outputSquibChannel.FireMode = SquibFireMode.NONE;
}
else if (c is OutputTOMDigitalChannel dOut)
{
dOut.ConfigurationMode = DFConstantsAndEnums.ConfigMode.Disabled;
dOut.OutputMode = DigitalOutputModes.NONE;
}
}
public static ExcitationVoltageOptions.ExcitationVoltageOption GetExcitationVoltageEnum(SensorCalibration sc, IGroupChannel c,
Dictionary<string, HardwareChannel> channelLookup, SensorData sd)
{
var preferredExcitation = sd.SupportedExcitation[0];
if (c.DASId < 0 || c.DASChannelIndex < 0)
{
return preferredExcitation;
}
var key = $"{c.DASId}_{c.DASChannelIndex}";
//find the appropriate excitation
var h = channelLookup[key];
foreach (var se in sd.SupportedExcitation)
{
if (!h.IsSupportedExcitation(se)) continue;
preferredExcitation = se;
break;
}
return preferredExcitation;
}
private static bool UseACCoupling(SensorData sensor, HardwareChannel channel)
{
if (null == sensor || null == channel) { return false; }
switch (sensor.Bridge)
{
case SensorConstants.BridgeType.DigitalInput:
case SensorConstants.BridgeType.IEPE:
case SensorConstants.BridgeType.RTC:
case SensorConstants.BridgeType.SQUIB:
case SensorConstants.BridgeType.StreamOut:
case SensorConstants.BridgeType.StreamIn:
case SensorConstants.BridgeType.TOMDigital:
case SensorConstants.BridgeType.UART:
case SensorConstants.BridgeType.CAN:
return false;
}
if (!sensor.ACCouplingModeEnabled) { return false; }
if (channel.Hardware.ProtocolVersion < SLICE6AIR.AC_COUPLER_ENABLE) { return false; }
if (channel.Hardware.DASTypeEnum != HardwareTypes.SLICE6_AIR) { return false; }
return true;
}
public static double GetSensitivity(SensorCalibration sc, IGroupChannel c, Dictionary<string, HardwareChannel> channelLookup, SensorData sd, bool useNonLinear)
{
var preferredExcitation = sd.SupportedExcitation[0];
if ((sd.SupportedExcitation.Length > 1) && (c.DASId >= 0 && c.DASChannelIndex >= 0))
{
//find the appropriate excitation
var key = $"{c.DASId}_{c.DASChannelIndex}";
if (channelLookup.ContainsKey(key))
{
var h = channelLookup[key];
foreach (var se in sd.SupportedExcitation)
{
if (!h.IsSupportedExcitation(se)) continue;
preferredExcitation = se;
break;
}
}
else
{
//hardware not found, just return the first excitation?
APILogger.Log(
$"warning, failed to find hardware {c.IsoChannelName}/{c.UserChannelName} - {c.DASId}:{c.DASChannelIndex}");
}
}
//nonlinear cal records are first, linear last
var record = useNonLinear ? Array.Find(sc.Records.Records, r => r.Excitation == preferredExcitation || !sc.IsProportional) : sc.Records.Records.LastOrDefault(r => r.Excitation == preferredExcitation || !sc.IsProportional);
if (null == record) { throw new NotSupportedException("no calibration record found for " + sc.SerialNumber + " for excitation " + preferredExcitation); }
//now finally, do any math we need
return record.Sensitivity;
}
public static DFConstantsAndEnums.RecordingMode SetActiveRecordingMode(DataModel.TestTemplate currentTest)
{
var result = DFConstantsAndEnums.RecordingMode.AutoActiveMode;
if (currentTest.WakeUpWithMotion)
{
result = DFConstantsAndEnums.RecordingMode.AerospaceWithMotion;
}
else if (currentTest.StartWithEvent)
{
result = DFConstantsAndEnums.RecordingMode.MultipleEventRecorderTriggerStart;
}
return result;
}
public bool CheckAAFilterRate(List<IDASCommunication> dasList)
{
var result = true;
var failureList = new List<string>();
using (var configService = new ConfigurationService())
{
var mreLocal = new ManualResetEvent(false);
configService.CheckAAFilterRate(dasList, delegate (ServiceBase.CallbackData cbd)
{
switch (cbd.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Failure:
failureList.Add(cbd.Target.SerialNumber);
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mreLocal.Set();
break;
}
}, dasList);
mreLocal.WaitOne();
}
if (failureList.Any())
{
result = false;
}
return result;
}
private void PrepareForDiagnostics(List<IDASCommunication> ldas, Dictionary<string, int> dasSampleRateList)
{
using (var diagnosticsService = new DiagnosticsService())
{
var sampleRateLookup = new Dictionary<string, double>();
var aafLookup = new Dictionary<string, float>();
foreach (var serialNumber in ldas.Where(x => dasSampleRateList.ContainsKey(x.SerialNumber)).Select(y=>y.SerialNumber))
{
var sampleRate = dasSampleRateList[serialNumber];
sampleRateLookup.Add(serialNumber, sampleRate);
aafLookup.Add(serialNumber, Convert.ToSingle(SerializedSettings.GetAAFException(SerializableAAF.DAS_TYPE.SLICE, sampleRate)));
}
var mre = new ManualResetEvent(false);
diagnosticsService.PrepareForDiagnostics(ldas,
PrePostResults.PreEventDiagnosticsResult,
sampleRateLookup,
aafLookup,
delegate (ServiceBase.CallbackData callbackData)
{
switch (callbackData.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Success:
break;
case ServiceBase.CallbackData.CallbackStatus.Progress:
break;
case ServiceBase.CallbackData.CallbackStatus.NewData:
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
callbackData.Target.DiagnosticsHasBeenRun = false;
break;
case ServiceBase.CallbackData.CallbackStatus.Canceled:
callbackData.Target.DiagnosticsHasBeenRun = false;
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
},
ldas);
var timeWaited = 0;
int MaxTime = 0;
MaxTime = Math.Max(MaxTime, SerializedSettings.Diagnostics_Slice_TimeoutSec * 1000);
while (!mre.WaitOne(100, false) && timeWaited < MaxTime)
{
timeWaited += 100;
}
if (timeWaited >= MaxTime)
{
APILogger.Log(
"Diagnostics::Exiting early due to timeout");
}
}
}
private bool DataNeverDownloaded(List<IDASCommunication> dasList)
{
int maxQueryDownloadTimeout = 0;
maxQueryDownloadTimeout = Math.Max(maxQueryDownloadTimeout,
SerializedSettings.ResolveChannels_SLICE_QueryDownloadTimeoutSec * 1000);
CheckForData(dasList, maxQueryDownloadTimeout);
var bHaveUnDownloadedData = false;
foreach (var downloadStatus in dasList.Select(x=>x.EventDownloadedStatus))
{
if (bHaveUnDownloadedData)
{
break;
}
if ((null != downloadStatus) && Array.Exists(downloadStatus, b => !b))
{
bHaveUnDownloadedData = true;
}
}
return bHaveUnDownloadedData;
}
protected void CheckForData(List<IDASCommunication> dasList, int maxQueryDownloadTimeout)
{
using (var ds = new DownloadService())
{
var mre = new ManualResetEvent(false);
var cancelEvent = new ManualResetEvent(false);
var doneEvent = new ManualResetEvent(false);
ds.QueryDownloadedStatus(dasList,
delegate (ServiceBase.CallbackData callbackData)
{
switch (callbackData.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Canceled:
case ServiceBase.CallbackData.CallbackStatus.Failure:
cancelEvent.Set();
mre.Set();
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
}, dasList);
var timeWaited = 0;
while (!cancelEvent.WaitOne(0, false) && !doneEvent.WaitOne(0, false) &&
!mre.WaitOne(100, false) && timeWaited < maxQueryDownloadTimeout)
{
timeWaited += 100;
var percent = 100D * timeWaited / maxQueryDownloadTimeout;
APILogger.Log($"{StringResources.CheckHardwareStatus_CheckingDAS} {percent:F0}%");
}
if (timeWaited >= maxQueryDownloadTimeout || cancelEvent.WaitOne(0, false) ||
doneEvent.WaitOne(0, false))
{
cancelEvent.Set();
}
}
}
private void RunDiagnostics(List<IDASCommunication> ldas, DASHardware[] hardware)
{
using (var diagnosticsService = new DiagnosticsService())
{
var mre = new ManualResetEvent(false);
var cancelEvent = new ManualResetEvent(false);
var doneEvent = new ManualResetEvent(false);
var workEventDone = new ManualResetEvent(false);
var preOrPost = PrePostResults.PreEventDiagnosticsResult;
if (ldas.Count > 0)
{
try
{
diagnosticsService.SetStatusIndicator(ldas, DiagnosticsStatusIndicatorState.Pending, delegate (ServiceBase.CallbackData callbackData)
{
switch (callbackData.Status)
{
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
},
ldas);
}
catch (Exception ex)
{
APILogger.Log(ex);
diagnosticsService.Cancel();
Thread.Sleep(100);
cancelEvent.Set();
return;
}
var totalTime = 0;
int MaxTime = 0;
MaxTime = Math.Max(MaxTime, SerializedSettings.Diagnostics_Slice_TimeoutSec * 1000);
while (totalTime < MaxTime && !cancelEvent.WaitOne(0, false) && !doneEvent.WaitOne(0, false) && !mre.WaitOne(10, false))
{
totalTime += 10;
}
if (totalTime >= MaxTime || cancelEvent.WaitOne(0, false) || doneEvent.WaitOne(0, false))
{
APILogger.Log("Diagnostics::Exiting early due to timeout or user cancel");
diagnosticsService.ForceCancel();
Thread.Sleep(100);
cancelEvent.Set();
return;
}
//keep track of what units failed and why so we can warn later
//17822 DataPRO and TDAS rack unresponsive during diagnostics
var failedUnits = new List<Tuple<IDASCommunication, string>>();
try
{
diagnosticsService.Diagnos(ldas, 0, preOrPost, delegate (ServiceBase.CallbackData callbackData)
{
switch (callbackData.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Success:
callbackData.Target.DiagnosticsHasBeenRun = true;
foreach (var h in hardware)
{
if (h.SerialNumber != callbackData.Target.SerialNumber) continue;
if (null == ((InfoResult)callbackData.Target.DASInfo).OwningDAS.BaseInput) continue;
h.MeasuredInputVoltage = Math.Round(((InfoResult)callbackData.Target.DASInfo).OwningDAS.BaseInput.InputMilliVolts / 1000D, 2);
h.MeasuredBatteryVoltage = Math.Round(((InfoResult)callbackData.Target.DASInfo).OwningDAS.BaseInput.BatteryMilliVolts / 1000D, 2);
}
break;
case ServiceBase.CallbackData.CallbackStatus.Progress:
case ServiceBase.CallbackData.CallbackStatus.NewData:
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
callbackData.Target.DiagnosticsHasBeenRun = false;
var msg = callbackData.ErrorMessage;
if (callbackData.ErrorException is TriggerShortedException)
{
msg = string.Format(StringResources.Diagnostics_ShortedTrigger);
}
failedUnits.Add(new Tuple<IDASCommunication, string>(callbackData.Target, msg));
break;
case ServiceBase.CallbackData.CallbackStatus.Canceled:
callbackData.Target.DiagnosticsHasBeenRun = false;
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
},
ldas, MaxTime);
}
catch (Exception ex)
{
APILogger.Log(ex.Message);
diagnosticsService.ForceCancel();
Thread.Sleep(100);
cancelEvent.Set();
return;
}
totalTime = 0;
while (totalTime < MaxTime && !cancelEvent.WaitOne(0, false) && !doneEvent.WaitOne(0, false) && !mre.WaitOne(10, false))
{
totalTime += 10;
}
if (totalTime >= MaxTime || cancelEvent.WaitOne(0, false) || doneEvent.WaitOne(0, false))
{
APILogger.Log("Diagnostics::Exiting early due to timeout or user cancel");
diagnosticsService.ForceCancel();
Thread.Sleep(100);
cancelEvent.Set();
return;
}
//warn if there are any failed units
if (failedUnits.Any())
{
var msgs = new List<string>();
foreach (var failedUnit in failedUnits)
{
msgs.Add(string.Format(StringResources.DiagnosticsFailed_Unit,
failedUnit.Item1.SerialNumber, failedUnit.Item2));
}
//notify consumer that work is done and it can stop waiting
workEventDone.Set();
}
}
}
}
}
}