using System; using DTS.Common.Enums.Sensors; using DTS.Common.Interface.Connection; using DTS.Common.Enums.DASFactory; using DTS.Common.Interface.DASFactory; using System.Collections.Generic; using System.Linq; using System.Text; using DTS.Common; using DTS.Common.Enums; using DTS.Common.Utilities.Logging; using DTS.DASLib.Command.SLICE; using DTS.DASLib.Command.SLICE.DownloadCommands; using DTS.Common.Enums.Hardware; using DTS.Common.ICommunication; using DTS.Common.WINUSBConnection; using DTS.DASLib.Command; using DTS.DASLib.Command.SLICE.RealtimeCommands; using DTS.Common.Interface.DASFactory.Download; using DTS.Common.DASResource; using DTS.Common.DAS.Concepts; using DTS.Common.Constant.DASSpecific; using DTS.Common.Interface.DASFactory.Config; using DTS.Common.Classes.Connection; using DTS.Common.Interface.DASFactory.Diagnostics; using Prism.Ioc; using DTS.Common.Events; using DTS.Common.Classes.TMAT; using DTS.DASLib.Service.Classes; using DTS.Common.Interface.TestSetups; using DTS.DASLib.Service.Interfaces; using System.IO.Ports; using System.Threading; using Prism.Events; namespace DTS.DASLib.Service { public class TSRAIR : TSRAIR_Base, IDASReconfigure where T : IConnection, new() { public TSRAIR() { MinimumValidBatteryVoltage = 2.5D; } /// /// sets the maximum module count for unit /// this is not done through a service, so there's no protection from other commands /// executing at the same time /// void IDASReconfigure.SetMaxModuleCount(int count) { SetMaxModuleCount(count, false); _maxModuleCount = count; } private int _maxModuleCount = -1; /// /// gets the physical max number of modules. /// this value is cached /// /// int IDASReconfigure.GetMaxModuleCount() { _maxModuleCount = GetMaxModuleCount(_maxModuleCount); return _maxModuleCount; } } public class TSRAIR_Base : SLICE6_Base, ITestDASOrder, IArmActions, IUARTDownload, IUARTDownloadActions where T : IConnection, new() { private double _BMI_EU_LIMIT = -1; /// /// don't check level triggers between the levels provided by BMI sensors for TSR AIR. /// 36807 Already level triggered error preventing arm with system of TSR AIR DAS /// /// /// protected override bool ShouldCheckLevelTrigger(double limitEU) { if (_BMI_EU_LIMIT < 0) { try { var query = new QueryArmAttribute(this) { Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerLessThanLimit }; query.SyncExecute(); _BMI_EU_LIMIT = ((float[])query.Value)[3]; if (_BMI_EU_LIMIT <= 0) { _BMI_EU_LIMIT = 5; } } catch (Exception ex) { _BMI_EU_LIMIT = 5; APILogger.Log(ex); } } if (_BMI_EU_LIMIT > 0) { if (limitEU >= (-1 * _BMI_EU_LIMIT) && limitEU <= _BMI_EU_LIMIT) { return false; } } return base.ShouldCheckLevelTrigger(limitEU); } public override int NumberOfChannels() { if (null == ConfigData) { return 0; } if (null == ConfigData.Modules || 0 == ConfigData.Modules.Length) { return 0; } var count = 0; var streamingModuleFound = false; foreach (var module in ConfigData.Modules) { if (module.IsStreamOut()) { streamingModuleFound = true; //when someone configured this unit streaming was supported, it's now not //don't count the module if (!IsStreamingSupported) { continue; } } count += module.NumberOfChannels(); } //check if someone configured this unit when streaming was NOT supported and now it is if (IsStreamingSupported && !streamingModuleFound) { count++; } return count; } public override void SetIsStreamingSupported(bool supported = false) { if (supported) { //If called with True, the TSR AIR has firmware that is too old to disable streaming IsStreamingSupported = true; return; } var AttrQueryTSRAir = new QuerySystemAttribute_BridgeTSRAir(this); AttrQueryTSRAir.DeviceID = 1; // 1-7 will work AttrQueryTSRAir.Key = AttributeTypes.SystemAttributes_BridgeTSRAir.DISABLE_STREAMING_FEATURE; AttrQueryTSRAir.SyncExecute(); if (string.IsNullOrWhiteSpace(AttrQueryTSRAir.Value.ToString())) { IsStreamingSupported = false; return; } IsStreamingSupported = AttrQueryTSRAir.Value.ToString() == "0"; } /// /// per EF, don't measure the final offset and calculate AutoZeroDeviation on TSRAir yet /// /// /// /// protected override void MeasureFinalOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results) { // verify the sample rate var srQuery = new QueryArmAttribute(this); srQuery.Key = AttributeTypes.ArmAndEventAttributes.DiagnosticsSampleRateHz; srQuery.SyncExecute(); var samplerate = (double)(uint)srQuery.Value; if (samplerate == 0) { throw new Exception("MeasureFinalOffset: Samplerate is 0"); } // get the average offset var measureOffset = new RetrieveSampleAverage(this); measureOffset.DeviceID = 0; // send to base var avgTimeSeconds = 1.0 / 60.0 * 2.0; // 2 * 60Hz cycles measureOffset.Samples = (ushort)(samplerate * avgTimeSeconds); if (measureOffset.Samples < 1) { measureOffset.Samples = 1; } measureOffset.SyncExecute(); // store the values in arm attributes var setSR = new SetArmAttribute(this); setSR.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetADC, measureOffset._data, true); setSR.SyncExecute(); // set the calibration result for (var idx = 0; idx < results.Length; idx++) { results[idx].FinalOffsetADC = measureOffset.GetChannelData(results[idx].DASChannelNumber); if (ChannelActions[idx].RemoveOffset) { results[idx].AutoZeroPercentDeviation = 0; } else { results[idx].AutoZeroPercentDeviation = null; } info.NewData(new ServiceCallbackData.DiagnosticNewData() { Result = results[idx].FinalOffsetADC, DasChannelNumber = results[idx].DASChannelNumber, Action = ServiceCallbackData.DiagnosticNewData.Actions.FinalOffset }); //attempt to resolve the removed adc by using the before and after info try { results[idx].RemovedOffsetADC = 0; } catch (Exception) { } } } protected override bool SupportsDiagnosticsFactoryExcitation => false; #region api protocol support protected readonly Dictionary TSRAIR_MinimumProtocols = new Dictionary(); protected override bool SupportsIEPECalSignal => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IEPE); private const int UDP_REALTIME_STREAM = 19; public override void InitMinProto() { TSRAIR_MinimumProtocols.Clear(); // TSRAIR Protocol Limitations TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetDefaultMIF] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FileData] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors] = TSRAIR.STACK_SENSORS; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.TestCommunication] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareConfiguration] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageSysAttributes] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleSamplesRealtime] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseCalibrationDate] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ResetAttributeStore] = TSRAIR.MIN_PROTOCOL_VER; //TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC] = TSRAIR.VOLTAGE_INSERTION; //TODO: REMOVE THIS HACK when implemented or declared won't be TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageInsertion] = TSRAIR.VOLTAGE_INSERTION; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPTimestamp] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPSyncStatus] = TSRAIR.MIN_PROTOCOL_VER; //TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond] = TSRAIR.START_REC_DELAY_IN_SECONDS; //TODO: REMOVE THIS HACK when implemented or declared won't be //TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream] = TSRAIR.START_REALTIME_STREAM;//TODO: REMOVE THIS HACK when streaming is implemented TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryStackActualSampleRateImmediate] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetClockSyncConfig] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryClockSyncStatus] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MaxEventsPossible] = 18; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AdcClockSource] = 18; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SliceBusEnabled] = 18; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream] = UDP_REALTIME_STREAM; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines] = TSRAIR.MIN_PROTOCOL_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC] = TSRAIR.MIN_PROTOCOL_VER; //http://manuscript.dts.local/f/cases/29760/Implement-ACCoupleEnable-for-TSR-AIR TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelBridgeACCouplerEnable] = 22; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DisableStreamingFeature] = TSRAIR.DISABLE_STREAMING_FEATURE; // 30430 Add Support for IRIGB, 1PPS, IRIGB + 1PPS Time Sync features for TSR AIR TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.GetUARTSettings] = TSRAIR.IRIG_GPS_PPSIN_SYNC; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetUARTSettings] = TSRAIR.IRIG_GPS_PPSIN_SYNC; // 30513 PTP Domain ID TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID] = TSRAIR.MIN_PROTOCOL_VER; //not currently supported TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryUARTDownload] = TSRAIR.PROTOCOL_VERSION_CIRCULAR_UART; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ExtendedFaultIds] = TSRAIR.EXTENDED_FAULTS_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ADCSamplesPerPacket] = TSRAIR.ADC_SAMPLES_PER_PACKET_VER; TSRAIR_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ScheduledEventCount] = TSRAIR.PROTOCOL_VERSION_SCHEDULED_EVENTCOUNT; MinimumProtocols = TSRAIR_MinimumProtocols; } protected enum TSR_AIR_HW_REVISION : int { REV_A = 0, //Eth - COTS REV_B = 1, //Eth - "Kitchen Sink" COTS DIR = 2, //USB - USAF SBIR DKR = 3, //USB - NASA SBIR } public override HardwareTypes GetHardwareType() { //we shouldn't get here. this is overridden in both USB and Ethernet for return HardwareTypes.UNDEFINED; } public override int[] GetStackChannelConfigTypes() => new int[] { 0 }; #endregion #region config attributes protected override ConfigAttributes GetConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication com) { return new TSRAIRConfigAttributes(com); } /// /// SLICE6 config attributes, mostly inherits from SLICE.ConfigAttributes with some functionality removed /// protected class TSRAIRConfigAttributes : SLICE6ConfigAttributes { public override void ConfigureCoupling(bool[] IsACCoupledArray) { var set = new SetArmAttribute(com, QueryArmAttribute.Default_IO_Timeout); set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelACCouplerEnable, IsACCoupledArray, true); set.SyncExecute(); } //not supported in TSRAIR public override void ConfigureBridge(byte[] bridgeModeArray) { } //not supported in TSRAIR public override void ConfigureBridgeResistance(ushort[] BridgeResistanceArray) { } //not supported in TSRAIR public TSRAIRConfigAttributes(ICommunication _com) : base(_com) { } } #endregion #region display order public override int GetDASDisplayOrder() { return -1; } public override void SetDASDisplayOrder(int order) { } public override int[] GetChannelDisplayOrder() { return new[] { -1 }; } public override void SetChannelDisplayOrder(int[] order) { } #endregion #region event info, data /// /// QueryEventData is customized for TSRAIR, it needs to perform TSRAIR specific data marshalling /// /// protected override QueryEventDataBase GetQueryEventData() { return new QueryEventData_TSRAIR(this, QueryEventData_TSRAIR.Default_IO_Timeout); } #endregion #region get overrides //use SLICE2 implementation for minimum SR. That's the minimum super-sampling rate private uint _minSampleRate = uint.MinValue; public override uint MinSampleRate() { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting the min sample rate while streaming, it will fail if (GetIsStreaming() || GetIsInArm()) { _minSampleRate = 10000; return _minSampleRate; } if (uint.MinValue == _minSampleRate) { try { var qsa = new QuerySystemAttributeSLICE2(this); qsa.Key = AttributeTypes.SystemAttributesSLICE2.MinimumSampleRate; qsa.SyncExecute(); _minSampleRate = Convert.ToUInt16(qsa.Value); } catch (Exception) { return 10000; } } return _minSampleRate; } //use SLICE2 pattern for min for maximum SR . That's the maximum super-sampling rate private uint _maxSampleRate = uint.MaxValue; public override uint MaxSampleRate(int numberOfConfiguredChannels) { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting the min sample rate while streaming, it will fail if (GetIsStreaming() || GetIsInArm()) { _maxSampleRate = 10000; return _maxSampleRate; } if (uint.MaxValue == _maxSampleRate) { try { var qsa = new QuerySystemAttributeSLICE2(this); qsa.Key = AttributeTypes.SystemAttributesSLICE2.MaximumSampleRate; qsa.SyncExecute(); _maxSampleRate = Convert.ToUInt16(qsa.Value); } catch (Exception) { _maxSampleRate = 20000; } } return _maxSampleRate; } public override uint MaxAAFilterRate() { return MaxAAFilterRateHz; } protected override uint GetEventTotalChannels(int eventNum) { try { var eventTC = new QueryEventAttribute(this); eventTC.EventNumber = (ushort)eventNum; eventTC.Key = AttributeTypes.ArmAndEventAttributes.TotalChannels; eventTC.SyncExecute(); var value = Convert.ToUInt32(eventTC.Value); return value; } catch (Exception ex) { APILogger.Log("failed to get event total channels: ", ex); } return (uint)MaxModules * 3; } #endregion #region other overrides //FB 25526 /// /// the order of this DAS among multiple das /// public int DASIndex { get; set; } = -1; public override bool IsTSRAIR() { return true; } public override bool IsSlice6Air() { return false; } public override bool IsScheduleEventCountSupported() { return ProtocolVersion >= TSRAIR.PROTOCOL_VERSION_SCHEDULED_EVENTCOUNT; } protected override DASModule MakeConfigModuleFromInfoModule(InfoResult.Module infoModule) { //per LP we can make this simpler, S6A always supports 1/2,Full,IEPE var configModule = new DASModule(infoModule.ModuleArrayIndex, this); configModule.Channels = new DASChannel[infoModule.NumberOfChannels]; for (var i = 0; i < infoModule.NumberOfChannels; i++) { if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.EmbeddedClockSecondsAndMarker || infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.EmbeddedClockNanosAndPad) { var channel = new TimestampDASChannel(configModule, i); configModule.Channels[i] = channel; } else if (DFConstantsAndEnums.ModuleType.UART == configModule.ModuleType()) { var uartChannel = new UARTInputDASChannel(configModule, i); configModule.Channels[i] = uartChannel; } else if (DFConstantsAndEnums.ModuleType.StreamOut == configModule.ModuleType()) { var streamOutChannel = new StreamOutputDASChannel(configModule, i); configModule.Channels[i] = streamOutChannel; } else { var channel = new AnalogInputDASChannel(configModule, i) { SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge } }; configModule.Channels[i] = channel; } } return configModule; } /// /// returns a GetRealtimeSamples class object appropriate for the DAS /// tsr-air has different adc math so here we are /// /// /// whether realtime is polling for samples or not /// protected override IGetRealtimeSamples GetRealtimeSamplesClass(DTS.Common.Interface.DASFactory.ICommunication iCommunication, bool bPolling = false) { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream) && !bPolling) { return new RealtimeStreamingNextSamples(iCommunication); } return new GetRealtimeSamplesTSRAIR(iCommunication); } protected override float GetLevelTriggerThreshold(AnalogInputDASChannel analog, IDiagnosticResult diagnostics, double thresholdeu, double MvPerEu) { //I have modified this to be modeled after DataScaler and how it gets EU, it's a bit different than before ... var dataZeroLevelADC = diagnostics.GetExpectedDataZeroLevelADC(analog.ZeroMethod); var initialOffsetmVInADC = 0D; //var io = new InitialOffset(); //io.FromDbSerializeString(analog.InitialOffset); //if (io.Form == InitialOffsetTypes.EUAtMV) //{ // initialOffsetmVInADC = io.MV / diagnostics.ScalefactorMilliVoltsPerADC; //} MvPerEu = analog.SensitivityMilliVoltsPerEU; var adcToEUScalingFactor = (diagnostics.ScalefactorMilliVoltsPerADC / (analog.IsProportionalToExcitation ? analog.SensitivityMilliVoltsPerEU * analog.FactoryExcitationVolts : MvPerEu)) * (analog.AtCapacity ? analog.CapacityOutputIsBasedOn : 1.0) * (analog.IsInverted ? -1.0 : 1.0); var negativeSensitivity = analog.SensitivityMilliVoltsPerEU < 0; var negativePolarity = analog.IsInverted; var eu = 0D; var adc = 0D; var mV = 0D; //16272 3.1 LevelTrigger issues //there must be some double negative occurring in this case that doesn't occur in others? if (negativeSensitivity && !negativePolarity) { eu = -1D * thresholdeu - analog.InitialEU - dataZeroLevelADC * (double)adcToEUScalingFactor + initialOffsetmVInADC * (double)adcToEUScalingFactor; adc = eu / (double)adcToEUScalingFactor; mV = adc * diagnostics.ScalefactorMilliVoltsPerADC; } else { eu = thresholdeu - analog.InitialEU + dataZeroLevelADC * (double)adcToEUScalingFactor + initialOffsetmVInADC * (double)adcToEUScalingFactor; adc = eu / (double)adcToEUScalingFactor; mV = adc * diagnostics.ScalefactorMilliVoltsPerADC; } var sb = new StringBuilder(); sb.AppendFormat("{0} {1} {2}:{3}\r\n", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), SerialNumber, analog.Number); sb.AppendFormat("DataZeroLevelADC={0}", dataZeroLevelADC); sb.AppendFormat("ScaleFactormVPerADC={0}\r\n", diagnostics.ScalefactorMilliVoltsPerADC); sb.AppendFormat("MvPerEU={0}\r\n", MvPerEu); sb.AppendFormat("adcToEUScalingFactor = {0}\r\n", adcToEUScalingFactor); sb.AppendFormat("eu={0}\r\n", eu); sb.AppendFormat("ZeroMVINADC={0}", diagnostics.ZeroMVInADC); sb.AppendFormat("adc={0}\r\n", adc); sb.AppendFormat("mv={0}\r\n", mV); //////////LevelTriggerLogging.LevelTriggerLog(sb.ToString()); //return Convert.ToSingle(mV); return Convert.ToSingle(eu); } protected override ushort? GetMaxEvents() { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MaxEventsPossible)) { try { QuerySystemAttribute qsa = new QuerySystemAttribute(this, 1000); qsa.DeviceID = 0; qsa.Key = AttributeTypes.SystemAttributes.MaxEventsPossible; qsa.SyncExecute(); return ((ushort[])qsa.Value)[1]; } catch (Exception ex) { APILogger.Log("Exception getting max number of events", ex); } } return null; } /// /// sets the max number of events to record /// see http://dtsinfo/dtswiki/index.php/TSR_AIR_Production_Attribute_Settings /// and http://manuscript.dts.local/f/cases/30431/Check-trigger-runs-more-times-than-necessary /// /// protected override void SetMaxEvents(int numEvents) { try { var ssa3 = new SetSystemAttribute(this); ssa3.DeviceID = 0; ssa3.SetValue(AttributeTypes.SystemAttributes.MaxEventsPossible, new[] { (ushort)0, (ushort)numEvents }, true); ssa3.SyncExecute(); } catch (Exception ex) { APILogger.Log("Exception setting max number of events", ex); } } protected override void CommonRangeWork(AnalogInputDASChannel analog, bool bModified, float[] rangeArray, byte dasChannelNumber, double MvPerEU, IDASModule module) { //29759 Allow Adjustable Channel Ranges in Parameters for TSR AIR if (SensorConstants.IsTSRAirLowGChannel(module.SerialNumber())) { rangeArray[dasChannelNumber] = SensorConstants.LowG64; } else if (SensorConstants.IsTSRAirHighGChannel(module.SerialNumber())) { rangeArray[dasChannelNumber] = (float)SensorConstants.DefaultRangeHiG; } else if (SensorConstants.IsTSRAirARSChannel(module.SerialNumber())) { rangeArray[dasChannelNumber] = SensorConstants.ARS2000; } else { rangeArray[dasChannelNumber] = 0; } } /// /// writes a TMATs file to pc and TSR AIR /// private void WriteTmtFile(string serialNumber, IInfoResult dasInfo, IConfigurationData configData, IDiagnosticResult[] channelDiagnostics, ushort? dataChannelId, ushort? timeChannelId, int dasIndex, float[] scaleFactors, float[] ranges, float[] measuredOffsets) { var lines = new List(); var outMod = Array.Find(ConfigData.Modules, m => m.ModuleType() == DFConstantsAndEnums.ModuleType.StreamOut); GetTMATTemplates(outMod, out var dasTemplate, out var chTemplate); lines.AddRange(WriteDASPortion(dasTemplate, configData, serialNumber, dataChannelId, timeChannelId, null, dasIndex)); //now for modules we need to subtract any UART or stream modules, which are each 1 channel modules var modules = ConfigData.Modules.Where(x => x.NumberOfChannels() > 1); var channelIndex = 0; foreach (var module in modules) { foreach (var ch in module.Channels) { var chPortion = WriteChannelPortion(chTemplate, channelIndex, ranges, scaleFactors, measuredOffsets); if (null != chPortion) { lines.AddRange(chPortion); } channelIndex++; } } TmtFile.WriteTMTFile(this, lines.ToArray(), ((IConfiguration)this).TestDirectory); } /// /// returns all the strings for TMATS file for a given channel /// private string [] WriteChannelPortion(string chTemplatePath, int channelIndex, float [] ranges, float [] scaleFactors, float [] measuredOffset) { var tmtFile = new TmtSingleFile(chTemplatePath); var keys = Enum.GetValues(typeof(TMTChannelKeysEx)).Cast().ToArray(); 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(); DataScaler scaler = aidc?.GetDataScaler()??null; if (null == scaler) { scaler = new DataScaler(); scaler.SetScaleFactorMv(.1D); scaler.ProportionalToExcitation = false; if (channelIndex < scaleFactors.Length) { scaleFactors[channelIndex] = .1F; } } 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 offsetEU = null == aidc?"0":TmtFile.GetOffsetEUString(aidc, scaler, measuredOffset, channelIndex, diagnosticResult); foreach (var key in keys) { TmtSingleFile.UpdateChannelField(key, tmtFile, channelIndex, ranges, minEU, maxEU, aidc?.EngineeringUnits ?? GetEngineeringUnits(channelIndex), scaleFactors, scaler.GetAdcToEuScalingFactor(), aidc?.ChannelName2 ?? GetChannelName(channelIndex), offsetEU, true); } return tmtFile.GetAllLines(); } private static string GetEngineeringUnits(int chIdx) { switch (chIdx) { case 12: return "s"; case 13: return "s"; case 14: return "marker"; case 15: return "s"; case 16: return "ns"; case 17: return "trigger"; default: return "---"; } } private static string GetChannelName(int chIdx) { switch(chIdx) { case 12: return "SEC_L"; case 13: return "SEC_H"; case 14: return "MARKER"; case 15: return "NSEC_L"; case 16: return "NSEC_H"; case 17: return "TRIGGER"; default: return "---"; } } /// /// returns all lines for the DAS portion of the TMATS file /// private string [] WriteDASPortion(string dasTemplatePath, IConfigurationData configData, string serialNumber, ushort? dataChannelId, ushort? timeChannelId, ushort? uartChannelId, int dasIndex) { var tmtFile = new TmtSingleFile(dasTemplatePath); var globalKeys = Enum.GetValues(typeof(TMTGlobalKeys)).Cast().ToArray(); var modules = configData.Modules.Where(x => x.NumberOfChannels() > 1); var channelCount = modules.Sum(x => x.NumberOfChannels()); var bitsPerFrame = 32 + 16 * channelCount; foreach( var key in globalKeys) { TmtSingleFile.UpdateGlobalField(this, key, tmtFile, configData, serialNumber, timeChannelId, dataChannelId, uartChannelId, dasIndex, bitsPerFrame); } return tmtFile.GetAllLines(); } /// /// retrieves path to DAS template file, and channel template file /// private void GetTMATTemplates(IDASModule module, out string dasTemplate, out string chTemplate) { switch(module.StreamProfile) { case UDPStreamProfile.CH10_ANALOG: case UDPStreamProfile.CH10_ANALOG_2HDR: { dasTemplate = "TMTTemplates/TSRAIRTMTTemplate_ANALOG_DAS.TMT"; chTemplate = "TMTTemplates/TSRAIRTMTTemplate_ANALOG_CHANNEL.TMT"; } break; default: { dasTemplate = "TMTTemplates/TSRAIRTMTTemplate_PCM_DAS.tmt"; chTemplate = "TMTTemplates/TSRAIRTMTTemplate_PCM_CHANNEL.tmt"; } break; } } /// /// 19: Support UDPRealtimeStream Protocols /// protected override int MIN_PROTOCOL_TMATS_INTERVAL => 19; protected override void ConfigureTMATS(SliceConfigServiceAsyncInfo info, float[] scaleFactors, float[] ranges, float[] measuredOffset) { //FB 25526 Implement TMATS support for TSR AIR stream on boot if (ShouldWriteStreamInfo() && null != ChannelDiagnosticsResults && ChannelDiagnosticsResults.Any()) { try { ushort? dataChannelId = null; ushort? timeChannelId = null; ushort? uartChannelId = null; if (info.DataChannelIds.ContainsKey(this)) { dataChannelId = info.DataChannelIds[this]; } if (info.TimeChannelIds.ContainsKey(this)) { timeChannelId = info.TimeChannelIds[this]; } //FB 43761 //FB 30035 Decide the context based on profile type WriteTmtFile(SerialNumber, DASInfo, ConfigData, ChannelDiagnosticsResults, dataChannelId, timeChannelId, DASIndex, scaleFactors, ranges, measuredOffset); } catch (Exception ex) { var eventAggregator = ContainerLocator.Container.Resolve(); var msg = $"Failed to write TMATs file for: {SerialNumber}\r\n{ex.Message}"; eventAggregator.GetEvent().Publish(new PageErrorArg(new[]{msg}, null)); throw new Exception(ex.Message, ex); } SetTMATSInterval(info.TMATSIntervalMs); } } /// /// tsr air only uses ch 3-5 for level trigger, so remap anything on 0-2 to 3-5 if set /// /// /// /// /// private void CheckLevelTrigger(int index1, int index2, bool [] enable, float [] dValue) { if (enable[index1]) { enable[index1] = false; enable[index2] = true; dValue[index2] = dValue[index1]; dValue[index1] = 0; } } private void RemapLevelTriggers(bool []enableUpperLevelTriggerThreshold, float [] upperLevelTriggerThreshold) { CheckLevelTrigger(0, 3, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold); CheckLevelTrigger(1, 4, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold); CheckLevelTrigger(2, 5, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold); } private void AdjustMultipleEventRecordingModeIfNeeded() { if (null == ConfigData) { return; } foreach (var mod in ConfigData.Modules) { if (mod.RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART) { mod.RecordingMode = DFConstantsAndEnums.RecordingMode.AutoCircularBufferPlusUART; } if (mod.RecordingMode == DFConstantsAndEnums.RecordingMode.RecorderModePlusUART) { mod.RecordingMode = DFConstantsAndEnums.RecordingMode.AutoRecorderModePlusUART; } } } /// /// tsr-air won't have as many things to sort out /// it's mostly a shortening of SLICE1.AsyncConfigure /// /// protected override void AsyncConfigure(object configAsyncInfo) { var info = configAsyncInfo as SliceConfigServiceAsyncInfo; SetUDPAlignOnPPS(); if ((info.StreamADCPerPacket != null) && info.StreamADCPerPacket.ContainsKey(this)) { SetADCSamplesPerPacket(info.StreamADCPerPacket[this]); } AdjustMultipleEventRecordingModeIfNeeded(); IncrementNumberOfTimesWritten(); //12638 DAS does not record data in recorder mode during calibration ~ 40% of time. //for SLICE6 we call reseteventlist here, prior to configuring and NOT before arming ResetEventListPriorToConfigure(); int progressValue = 0; bool bReleased = true; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels)) { ReconfigureAccordingToConfig(); } PresetSampleRate(); // these should be the voltage requirements for TSRAIR InputLowVoltage = 4f; InputHighVoltage = 35f; BatteryLowVoltage = 2f; BatteryHighVoltage = 4.5f; SetVoltageRequirements(); SetPolarity(); SetArmDisableShortCheck(); try { Lock(); bReleased = false; // loop thru the modules (slices) and configure the channels var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels); var numUartChannels = DASInfo.Modules.Sum(mod => DFConstantsAndEnums.ModuleType.UART == mod.TypeOfModule ? mod.NumberOfChannels : 0); var numStreamingChannels = DASInfo.Modules.Sum(mod => DFConstantsAndEnums.ModuleType.StreamOut == mod.TypeOfModule ? mod.NumberOfChannels : 0); var rangeArray = new float[numChannels]; var maxEventsPossible = new UInt16[2]; var activityThresholdInmGInactivityTimeoutInSec = new UInt16[2]; var bridgeModeArray = new byte[numChannels]; var BridgeResistanceArray = new ushort[numChannels]; var IsACCoupledArray = new bool[numChannels]; // level trigger values var enableLowerLevelTriggerThreshold = new bool[numChannels]; var enableUpperLevelTriggerThreshold = new bool[numChannels]; var lowerLevelTriggerThreshold = new float[numChannels]; var upperLevelTriggerThreshold = new float[numChannels]; var qualificationSamples = new int[numChannels]; var diagnosticChannels = new List(); var bModified = false; var bridgeACCouplingArray = new bool[numChannels]; CommonConfigureWork(diagnosticChannels, qualificationSamples, ref bReleased, info, bridgeModeArray, IsACCoupledArray, BridgeResistanceArray, ref bModified, rangeArray, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold, enableLowerLevelTriggerThreshold, lowerLevelTriggerThreshold, bridgeACCouplingArray); //http://manuscript.dts.local/f/cases/30428/Implement-Low-G-Triggering-for-TSR-AIR //level triggers are always 3-5 for TSR AIR, so remap if needed RemapLevelTriggers(enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold); if (bReleased) { return; } // report progress progressValue = 5; info.Progress(progressValue); var config = GetConfigAttributes(this); // only set attributes to default if we haven't run diagnostics yet if (!DiagnosticsHasBeenRun) { config.PurgeStaleData(this); } //FB 25526 make these arrays so we can populate them later float[] scaleFactors = null; float[] ranges = null; float[] measuredOffset = null; // store some attributes if (ConfigData.Modules.Length > 0) { config.SampleRate = ConfigData.Modules[0].SampleRateHz; var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz; config.AAFilter = ConfigData.Modules[0].AAFilterRateHz; config.TestID = ConfigData.TestID.TrimEnd('\0'); config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0'); config.InstanceID = ConfigData.InstanceID?.TrimEnd('\0'); config.TestDescription = ConfigData.Description; config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate); config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate); //Set MaxEventsPossible try { var setMaxEventsPossible = new SetSystemAttribute(this); setMaxEventsPossible.DeviceID = 0; var numberOfEvents = Convert.ToUInt16(ConfigData.Modules[0].NumberOfEvents); maxEventsPossible[0] = 0; maxEventsPossible[1] = numberOfEvents; setMaxEventsPossible.SetValue(AttributeTypes.SystemAttributes.MaxEventsPossible, maxEventsPossible, true); setMaxEventsPossible.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex.Message); var errorString = "Error attempting to set MaxEventsPossible to "; foreach (var element in maxEventsPossible) { errorString += element; errorString += ", "; } errorString = errorString.Remove(errorString.Length - 2, 2); APILogger.Log(errorString); if (!bReleased) { bReleased = true; Release(); } info.Error(string.Format(errorString)); return; } //Set WakeUpMotionTimeout (Motion detect inactivity (sec)/ActivityThresholdInmGInactivityTimeoutInSec) try { var setWakeUpMotionTimeout = new SetArmAttribute(this); var inactivityTimeoutInSec = Convert.ToUInt16(ConfigData.Modules[0].WakeUpMotionTimeout); activityThresholdInmGInactivityTimeoutInSec[0] = 350; activityThresholdInmGInactivityTimeoutInSec[1] = inactivityTimeoutInSec; setWakeUpMotionTimeout.SetValue(AttributeTypes.ArmAndEventAttributes.ActivityThresholdInmGInactivityTimeoutInSec, activityThresholdInmGInactivityTimeoutInSec, true); setWakeUpMotionTimeout.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex.Message); var errorString = "Error attempting to set ActivityThresholdInmGInactivityTimeoutInSec to "; foreach (var element in activityThresholdInmGInactivityTimeoutInSec) { errorString += element; errorString += ", "; } errorString = errorString.Remove(errorString.Length - 2, 2); APILogger.Log(errorString); if (!bReleased) { bReleased = true; Release(); } info.Error(string.Format(errorString)); return; } if (ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Scheduled || ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Interval) { config.ScheduledStartTime = ConfigData.Modules[0].ScheduledStartTime; config.RecordingInterval = ConfigData.Modules[0].RecordingInterval; } SetArmMode(ConfigData.Modules[0].RecordingMode); try { //29759 Allow Adjustable Channel Ranges in Parameters for TSR AIR config.ConfigureRange(rangeArray); progressValue = 10; info.Progress(progressValue); } catch (Exception ex) { var errorString = "Error attempting to set range to "; foreach (var range in rangeArray) { errorString += range; errorString += ", "; } errorString = errorString.Remove(errorString.Length - 2, 2); if (ex.Message.Contains("StatusSetupRequestedSensorRangeTooSmall")) { APILogger.Log("Ignoring: " + errorString, ex); } else { APILogger.Log("Aborting: errorString", ex); if (!bReleased) { bReleased = true; Release(); } info.Error(string.Format(errorString)); return; } } config.ConfigureBridge(bridgeModeArray); if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelBridgeACCouplerEnable)) { config.ConfigureCoupling(IsACCoupledArray); } config.ConfigureBridgeResistance(BridgeResistanceArray); // configure level trigger enable/threshold values try { ConfigureLevelTriggers(enableLowerLevelTriggerThreshold, enableUpperLevelTriggerThreshold, lowerLevelTriggerThreshold, upperLevelTriggerThreshold, qualificationSamples); } catch (Exception ex) { APILogger.Log("Problem setting level triggers", ex); } //FB 25526 query and populate these threee arrays for TSR AIR to be used in TMT template for TSR AIR // query scale factors scaleFactors = config.GetScaleFactors(); // query ranges ranges = config.GetRanges(); //query offsets measuredOffset = config.GetMeasuredOffset(); if (numChannels > scaleFactors.Length + numUartChannels + numStreamingChannels) { if (!bReleased) { bReleased = true; Release(); } // "ConfigurationService.Configure: Firmware returned {0} channels but stack contains {1}" info.Error(string.Format("Firmware returned {0} channels but stack contains {1}", scaleFactors.Length, numChannels - numUartChannels - numStreamingChannels)); return; } for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { if (DFConstantsAndEnums.ModuleType.UART == ConfigData.Modules[moduleIdx].ModuleType()) continue; //only configuring the analogs if (DFConstantsAndEnums.ModuleType.StreamOut == ConfigData.Modules[moduleIdx].ModuleType()) { continue; } for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIdx].Channels.Length; channelIdx++) { var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); if (ConfigData.Modules[moduleIdx].Channels[channelIdx] is AnalogInputDASChannel analog) { analog.ScalefactorMilliVoltsPerADC = -1; analog.ScalefactorEngineeringUnitsPerADC = scaleFactors[dasChannelNumber]; } } progressValue++; info.Progress(Math.Min(19, progressValue)); } } progressValue = 20; info.Progress(progressValue); SetStreamingRealtimeSampleRate(info); RemainingConfigWork(ref progressValue, info, diagnosticChannels, config, ref bReleased, scaleFactors, ranges, measuredOffset); } catch (CanceledException) { if (!bReleased) { bReleased = true; Release(); } info.Cancel(); } catch (Exception ex) { if (!bReleased) { bReleased = true; Release(); } info.Error(ex.Message, ex); } finally { if (!bReleased) { bReleased = true; Release(); } } info.Progress(100); info.Success(); } private const double CHARGING_VOLTAGE = 8D; public override string ConvertInputVoltage2BatteryCharging(double inputVoltage) { //per CPB in 31780 TSR-Air-Charging-Status-incorrect-in-DataPro-when-using-the-5V-to-9V-charger //anything over 8V is charging ... return inputVoltage >= CHARGING_VOLTAGE ? Resources.Charging : Resources.NotCharging; } protected override void ConfigureLevelTriggers(bool[] enableLowerTrigger, bool[] enableUpperTrigger, float[] lowerTriggerThreshold, float[] upperTriggerThreshold, int[] qualificationSamples) { try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger)) { //TSR AIRs only need the following two attributes to be set var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerChannelGreaterThanEnabled, enableUpperTrigger, true); set.SyncExecute(); set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerGreaterThanLimit, upperTriggerThreshold, true); set.SyncExecute(); SetLevelTriggerQualificationSamples(qualificationSamples); } } catch (Exception ex) { throw new ApplicationException("encountered problem configuring level triggers", ex); } } protected virtual void AsyncQueryConfiguration(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceAsyncInfo; var displayOrder = new int[] { -1 }; var dasDisplayOrder = -1; try { dasDisplayOrder = GetDASDisplayOrder(); } catch (Exception ex) { APILogger.Log("Failed to query user attributes", ex); } info.Progress(10); try { displayOrder = GetChannelDisplayOrder(); } catch (Exception ex) { APILogger.Log("Failed to query user attributes", ex); } info.Progress(20); try { // pickup the channel data var config = GetConfigAttributes(this);//new ConfigAttributes(this); try { var StoredInfoString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.Diagnostic, this); ConfigData = ConfigurationData.DeserializeFromString(StoredInfoString); info.Progress(50); if (!VerifyConfig()) { ConfigData = null; } if (ConfigData == null || ConfigData.Modules == null || ConfigData.Modules.Length == 0) { APILogger.LogString("QueryConfiguration: ConfigData or Modules empty, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } else if (DASInfo == null || DASInfo.Modules == null || DASInfo.Modules.Length != ConfigData.Modules.Length) { APILogger.LogString("QueryConfiguration: DASInfo or Modules empty, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } else { for (var i = 0; i < ConfigData.Modules.Length && i < DASInfo.Modules.Length; i++) { var bIEPE = false; switch (DASInfo.Modules[i].TypeOfModule) { case DFConstantsAndEnums.ModuleType.SLICEIEPE: bIEPE = true; break; } foreach (var channel in ConfigData.Modules[i].Channels) { if (bIEPE) { ((AnalogInputDASChannel)channel).SupportedBridges = new[] { SensorConstants.BridgeType.IEPE }; } else { ((AnalogInputDASChannel)channel).SupportedBridges = new[]{ SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge}; } } } } info.Progress(60); } catch (CanceledException) { // cancel must be propagated throw; } catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error($"Connection with {SerialNumber} failed"); return; } catch (Exception ex) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(ex); if (ex.Message.Contains("ReceiveFailed")) { info.Error($"Connection with {SerialNumber} failed"); return; } // something happend that prevents us from getting the stored config, make a default ConfigData = MakeDefaultConfigFromInfo(); } if (null != ConfigData) { ConfigData.DisplayOrder = displayOrder; ConfigData.DasDisplayOrder = dasDisplayOrder; } // get scale factors try { var scaleFactors = GetScaleFactors(); #if LEVEL_TRIGGER_DEFINED // These are currently unutilized since we have no application for the offset-adjusted // level trigger values outside of the Slice firmware. // // var levelTriggers = GetLevelTriggers( ); #endif info.Progress(70); // set the OwningDAS and OwningModule since it was skipped during serialization foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; module.SampleRateHz = Convert.ToUInt32(config.ActualSampleRate); module.OwningDAS = this; foreach (var iDASChannel in module.Channels) { var channel = (DASChannel)iDASChannel; channel.OwningModule = module; // pick up the scalefactor for this channel if (channel is AnalogInputDASChannel analog) { var dasChannelNumber = analog.Number; if (dasChannelNumber < scaleFactors.Length) { analog.ScalefactorEngineeringUnitsPerADC = scaleFactors[dasChannelNumber]; analog.ScalefactorMilliVoltsPerADC = -1; } } } } info.Progress(80); } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (NotConnectedException) { info.Error($"Connection with {SerialNumber} failed"); return; } catch (CanceledException) { // cancel must be propagated throw; } catch (Exception ex) { APILogger.LogString("QueryConfiguration: GetScaleFactors threw exception"); APILogger.LogException(ex); // we don't need no stinking scale factors } // get sensor ID's if (ConfigData.Modules != null) { foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; module.OwningDAS = this; // in case we bailed out above foreach (var iDASchannel in module.Channels) { var channel = (DASChannel)iDASchannel; channel.OwningModule = module; // in case bailed out above var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, 0); } IEID[] ids = null; //performance improvement, skip getting battery ids if you don't need too if (ShouldCheckForBatteryIds()) { ids = EIDReader.RetriveEIDs(this, 255, 0); } else { ids = null; } if (null != ids && 0 != ids.Length) { module.OwningDAS.DASInfo.BatteryID = ids[0]; module.OwningDAS.SetDASInfo(); } } } info.Progress(90); RetrieveEventGuids(); DASInfo.MaxEventStorageSpaceInBytes = ReadSampleStorageSizeInBytes(); info.Progress(95); GetAutoArmStatus(); info.Success(); } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (NotConnectedException) { info.Error($"Connection with {SerialNumber} failed"); return; } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } protected override void AsyncPrepareForArmNow(object asyncInfo) { base.AsyncPrepareForArmNow(asyncInfo); if (!(asyncInfo is ArmPacket info)) { return; } } /// /// returns true if the device is known to be streaming /// does not query device, just returns a flag if it has been set /// public override bool GetIsStreaming() { if (null == DASArmStatus) { return false; } //18852 Cannot use Stop streaming / (Dis)Auto Arm button if one or more DAS is idle //can't rely on just having received invalid mode, QATS will still have a status of realtime //when streaming, so we'll use either for now. return DASArmStatus.ReceivedInvalidModeDuringSetup || DASArmStatus.IsInRealtime; } private DownloadReport.UARTEventInfo GetUARTEventInfo(ushort eventNumber, bool dataPresent = false, bool dataDownloaded = false, ulong totalByteCount = 0, ulong triggerByteCount = 0, ulong startTimeStamp = 0, ulong endTimestamp = 0, uint buadRate = 0) { var uartEventInfo = new DownloadReport.UARTEventInfo() { EventNumber = eventNumber, DataPresent = dataPresent, DataDownloaded = dataDownloaded, TotalByteCount = totalByteCount, TriggerByteCount = triggerByteCount, StartTimestamp = startTimeStamp, EndTimestamp = endTimestamp, BaudRate = buadRate }; return uartEventInfo; } protected override uint[] GetExtendedFaultFlags(int eventIndex) { if ( !IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ExtendedFaultIds)) { return new uint[] { 0, 0, 0, 0 }; } return base.GetExtendedFaultFlags(eventIndex); } protected override void AsyncQueryDownload(object asyncInfo) { var info = asyncInfo as QueryDownloadAsyncInfo; // since we don't know exact how many XML attributes, modules or channels that's in the // configuration, we guess on the high end. // (44 per stored config + 4 + 3 per module + 3 per channel) * number of events // equals 44 + 4 + 30 + 90 = 168 per event const int StepsPerEvent = 168; try { if (info.EventIndex < 0) { RetrieveDASBootCount(); } else { TurnOffDiagnosticsMode(); } var qsa = new QuerySystemAttribute(this); qsa.Key = AttributeTypes.SystemAttributes.TotalEventsStored; qsa.SyncExecute(); ushort eventCount = (ushort)qsa.Value; var config = GetConfigAttributes(this);//new ConfigAttributes(this); // 6 * modules + 44 var progress = new QueryDownloadProgress(info, StepsPerEvent * eventCount, 1); // we'll also freshen up the cached event GUIDs // NOT IF WE ARE QUERYING A SPECIFIC GUID var eventGuids = new Guid[eventCount]; var faultFlags = new ushort[eventCount]; var faultFlagsEx = new ushort[eventCount]; var armAttempts = new byte[eventCount]; var extendedFaultIds = new List(); var dlReport = new DownloadReport(); if (this is IUARTDownload && 0 > info.EventIndex) { dlReport.UARTEvents = new DownloadReport.UARTEventInfo[eventCount]; for (var eventIdx = 0; eventIdx < eventCount; eventIdx++) { if (info.EventIndex >= 0 && eventIdx != info.EventIndex) { continue; } extendedFaultIds.Add(GetExtendedFaultFlags(eventIdx)); if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryUARTDownload)) { var uartInfo = GetUARTEventInfo((ushort)eventIdx); if (0 > info.EventIndex) { dlReport.UARTEvents[eventIdx] = uartInfo; } else { EventInfo.UARTEvents[eventIdx] = uartInfo; } continue; } else { QueryUartEventInfo(eventIdx, info, dlReport); } } } if (0 > info.EventIndex) { dlReport.Events = new DownloadReport.EventInfo[eventCount]; } // Retrieved the stored configuration //this is not a new xml for each event, lets just get it once! var storedConfigStr = config.RetrieveEventXMLConfig(0, progress, this); // I/O * n // convert it from XML to object var eventInfo = new DownloadReport.EventInfo(); var eventDesc = eventInfo.Description = config.GetEventDescription(0); // I/O var testId = config.GetEventID(0, this).TrimEnd(new char[] { '\0' }); ; // I/O var eventGuid = GetEventGuid(0); //scale factors for TSR AIR shouldn't change between events ... var factors = config.GetEventScaleFactors(0); var actualSampleRate = config.GetEventSamplerate(0); var stackActualSampleRate = config.GetEventStackSamplerate(0); var aafilter = config.GetEventAAFilter(0); var numberOfChannels = GetEventTotalChannels(0); // I/O for (var eventIdx = 0; eventIdx < eventCount; eventIdx++) { var storedConfig = ConfigurationData.DeserializeFromString(storedConfigStr); if (info.EventIndex >= 0 && eventIdx != info.EventIndex) { continue; } GetEventTimeStampInfo(eventIdx, out var startRecordTimestampSec, out var triggerTimestampSec, out var startRecordTimestampNanoSec, out var triggerTimestampNanoSec, out var pTPMasterSync); // the object to store it all in eventInfo = new DownloadReport.EventInfo(); eventInfo.Description = eventDesc; // get the event level values try { if (null != ConfigData) { eventInfo.Description = ConfigData.Description; } } catch (Exception) { } eventInfo.TestID = testId; eventInfo.EventNumber = eventIdx; eventInfo.TestGUID = eventGuid; try { eventInfo.FaultFlags = config.GetEventFaultFlags(eventIdx); eventInfo.FaultFlagsEx = config.GetEventFaultFlagsEx(eventIdx); } catch (Exception ex) { APILogger.Log("could not get fault flags", ex); } try { eventInfo.ArmAttempts = config.GetEventArmAttempts(eventIdx); } catch (Exception ex) { APILogger.Log("could not get arm attempts", ex); } eventGuids[eventIdx] = eventInfo.TestGUID; faultFlags[eventIdx] = eventInfo.FaultFlags; faultFlagsEx[eventIdx] = eventInfo.FaultFlagsEx; armAttempts[eventIdx] = eventInfo.ArmAttempts; eventInfo.HasBeenDownloaded = config.EventHasBeenDownloaded(eventIdx, out uint flag); // I/O // figure out how many modules we have var numberOfModules = config.CalculateNumberOfModules(storedConfig, numberOfChannels, IsTOM()); var numberOfStreamOuts = config.CalculateNumberOfStreamOuts(storedConfig); var numberOfUARTs = config.CalculateNumberOfUARTs(storedConfig); // make sure we have the right number of modules if (numberOfModules + numberOfStreamOuts + numberOfUARTs != storedConfig.Modules.Length) { // "Slice.QueryDownload: The information in the recorder is corrupt" info.Error(Strings.Slice_QueryDownload_Err1); //SS: need to add code here to try to extract enough info return; } eventInfo.Modules = new DASModule[numberOfModules]; var numberOfSamples = config.GetEventTotalSamples(eventIdx); // I/O var triggerSampleNumber = config.GetEventTriggerSampleNumber(eventIdx); // I/O var eventStartRecordSampleNumber = config.GetEventStartRecordSampleNumber(eventIdx); // I/O var eventStartTime = config.GetEventStartTime(eventIdx); // I/O var levelTriggerOffsetCorrection = config.GetEventLevelTriggerT0AdjustmentSamples(eventIdx); var levelTriggerSeen = config.GetEventLevelTriggerSeen(eventIdx); // 17873 download should only use event attributes rather than arm attributes var currentChannel = 0; //toyota boshoku -flat data issue zendesk 5702? //basically make sure we are using the event attribute ScaleFactorMvADC and not what is in the //xml for (var moduleIdx = 0; moduleIdx < numberOfModules; moduleIdx++) { // take the module from the stored config var module = (DASModule)storedConfig.Modules[moduleIdx]; module.SampleRateHz = Convert.ToUInt32(actualSampleRate); module.ActualSampleRateHz = Convert.ToUInt32(actualSampleRate); module.EmbeddedSampleRateHz = stackActualSampleRate; /// /// Amongst other things, the level trigger offset correction, if it exists, is getting applied /// to the module trigger sample numbers below. There shouldn't be any danger of this value being /// erroneously applied to outside "tweaking" since the only outside tweak should only ever happen /// if there was no trigger. Still I'm a little concerned that this value will still be invisibly /// applied to user-overridden trigger sample numbers. We could conceivably guard against this /// situation with an "original trigger sample number" concept, which we could then check against /// and not re-apply things like level trigger offset correction if the current value doesn't /// match it, implying it has been manually overridden. /// // now update the module with dynamic data module.OwningDAS = this; module.NumberOfSamples = numberOfSamples; module.TriggerSampleNumbers = new ulong[1]; // only one so far var phaseShift = GetPhaseShiftSamples(Convert.ToUInt32(1 + module.ModuleArrayIndex), Convert.ToDouble(actualSampleRate), Convert.ToUInt32(aafilter), triggerSampleNumber); module.TriggerSampleNumbers[0] = triggerSampleNumber + phaseShift; eventInfo.WasTriggered = module.TriggerSampleNumbers[0] > 0; module.StartRecordSampleNumber = 0; if (module.RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBuffer || module.RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART || module.RecordingMode == DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode || module.RecordingMode == DFConstantsAndEnums.RecordingMode.a16_CircularBufferAndStreamSubSampleMode || module.RecordingMode == DFConstantsAndEnums.RecordingMode.RAMActive || module.RecordingMode == DFConstantsAndEnums.RecordingMode.MultipleEventRAMActive) { ulong preTriggerSamples = Convert.ToUInt64(System.Math.Abs(module.PreTriggerSeconds * module.SampleRateHz + 1D)); if (preTriggerSamples < module.TriggerSampleNumbers[0]) { module.StartRecordSampleNumber = module.TriggerSampleNumbers[0] - preTriggerSamples; } } else if (module.RecordingMode == DFConstantsAndEnums.RecordingMode.AutoActiveMode || module.RecordingMode == DFConstantsAndEnums.RecordingMode.AerospaceWithMotion) { ulong preTriggerSamples = Convert.ToUInt64(System.Math.Abs(module.PreTriggerSeconds * module.SampleRateHz + 1D)); if (preTriggerSamples < module.TriggerSampleNumbers[0]) { module.StartRecordSampleNumber = eventStartRecordSampleNumber; } } #region Get_PTP_Event_Timestamp module.StartRecordTimestampSec = startRecordTimestampSec; module.TriggerTimestampSec = triggerTimestampSec; module.StartRecordTimestampNanoSec = startRecordTimestampNanoSec; module.TriggerTimestampNanoSec = triggerTimestampNanoSec; module.PTPMasterSync = pTPMasterSync; #endregion #region Slice6TiltSensor module.TiltSensorAxisXDegreesPre = double.NaN; module.TiltSensorAxisYDegreesPre = double.NaN; module.TiltSensorAxisZDegreesPre = double.NaN; module.TiltSensorAxisXDegreesPost = double.NaN; module.TiltSensorAxisYDegreesPost = double.NaN; module.TiltSensorAxisZDegreesPost = double.NaN; #endregion // update the channel info for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++) { var channel = (DASChannel)module.Channels[channelIdx]; channel.OwningModule = module; channel.EventStartTime = eventStartTime; if (channel is AnalogInputDASChannel analog) { analog.ScalefactorEngineeringUnitsPerADC = factors[currentChannel]; } // CGO // This is a hacked change for now to support 00G8 and prior firmware // in the 1.04 release of SLICEWare. This and the other CGO commented // block of code need more general cleanup before 1.05. if (null == levelTriggerOffsetCorrection) { channel.LevelTriggerT0AdjustmentSamples = 0; } else if (currentChannel < levelTriggerOffsetCorrection.Length) { channel.LevelTriggerT0AdjustmentSamples = levelTriggerOffsetCorrection[currentChannel]; } else { channel.LevelTriggerT0AdjustmentSamples = 0; } if (null == levelTriggerSeen) { channel.LevelTriggerSeen = false; } else if (currentChannel < levelTriggerSeen.Length) { channel.LevelTriggerSeen = levelTriggerSeen[currentChannel]; } else { channel.LevelTriggerSeen = false; } currentChannel++; } // store it in the object eventInfo.Modules[moduleIdx] = module; } if (0 > info.EventIndex) { // store it in the object dlReport.Events[eventIdx] = eventInfo; } else { EventInfo.Events[eventIdx] = eventInfo; } info.Progress(100 * eventIdx / eventCount); } // now assigned the data to the public property if (0 > info.EventIndex) { SetEventInfo(dlReport); } SetEventFaultFlags(faultFlags); ((IDownload)this)?.SetExtendedFaultFlags(extendedFaultIds.ToArray()); SetEventGuids(eventGuids); SetEventArmAttemps(armAttempts); info.Success(); } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } private void QueryUartEventInfo(int eventIdx, QueryDownloadAsyncInfo info, DownloadReport dlReport) { var uartInfo = new QueryUARTEventInfo(this) { EventNumber = (ushort)eventIdx }; try { uartInfo.SyncExecute(); } catch (Exception ex) { //http://manuscript.dts.local/f/cases/43316/Software-Should-Show-Clear-Error-Instead-of-Misleading-Message-When-Firmware-Bug-Blocks-Data-Download APILogger.Log(ex); } // the object to store it all in var uartEventInfo = GetUARTEventInfo(uartInfo.EventNumber, uartInfo.DataPresent, uartInfo.DataDownloaded, uartInfo.TotalByteCount, uartInfo.TriggerByteCount, uartInfo.StartTimestamp, uartInfo.EndTimestamp, uartInfo.BaudRate); if (0 > info.EventIndex) { // store it in the object dlReport.UARTEvents[eventIdx] = uartEventInfo; } else { EventInfo.UARTEvents[eventIdx] = uartEventInfo; } } protected override void DownloadEventStartCmd(SliceDownloadState state) { //TODO: REMOVE THIS HACK or keep it depending on how FW finishes implementing try { if (state.SamplesDownloaded >= state.Request.EndSample - state.Request.StartSample + 1) return; state.DownloadCommand = GetQueryEventData(); state.DownloadCommand.LogCommands = false; state.DownloadCommand.EventNumber = state.Request.EventNumber; state.DownloadCommand.Channel = state.Request.DASChannelNumber; if (state.Request.DASChannelNumber == DownloadRequest.ALL_CHANNELS) { state.DownloadCommand.ChannelsDownloaded = IsTOM() ? 9 : EventInfo.Events[state.Request.EventNumber].Modules.Sum(module => module.NumberOfChannels()); } state.DownloadCommand.LastSample = state.Request.EndSample; // Ask for the next batch of samples state.DownloadCommand.FirstSample = state.Request.StartSample + state.SamplesDownloaded; state.DownloadCommand.Execute(DownloadEventCallback, state); } catch (CanceledException) { state.Cancel(); } catch (Exception ex) { state.Error(ex.Message, ex); } } /// /// TSR AIR doesn't AutoArm /// /// /// /// /// /// /// /// /// /// void DTS.DASLib.Service.IArmActions.AutoArmNow(ServiceCallback callback, object userData, Guid eventGuid, int armNowTimeout, bool testingMode, uint diagnosticsDelayMs, int maxNumberEvents, bool repeatEnable, bool preserveDiagnostics) { var info = new AutoArmPacket(callback, userData, eventGuid, armNowTimeout, testingMode, diagnosticsDelayMs, maxNumberEvents, repeatEnable, preserveDiagnostics); info.Success(); } /// /// for TSR AIR we override WhatToDownload and transparently handle things our own way from the outside /// private WhatToDownloadTSRAIR _whatToDownload; public override IDownloadRequest WhatToDownload { get => _whatToDownload; set { _leftOverData = new ushort[0]; var numChannels = NumberOfDownloadChannels(); if (IsTOM()) { //number of channels is a lie! don't trust it! numChannels = 9; } _whatToDownload = new WhatToDownloadTSRAIR(value, numChannels, PageSize); } } protected override void AsyncTrigger(object asyncInfo) { var info = asyncInfo as SliceServiceAsyncInfo; try { var setSwitch = new SetSwitchImmediate(this); setSwitch.Switch = (byte)Switches.BaseSwitches.TSRAIR_TriggerOut; setSwitch.Setting = 1; setSwitch.DeviceID = 0; setSwitch.Port = (byte)Switches.SystemSwitches_425B.GPIO__MCU_EVENT; setSwitch.SyncExecute(); info?.Success(); } catch (CanceledException) { info?.Cancel(); } catch (Exception ex) { info?.Error(ex.Message, ex); } } protected override void AsyncClearTriggerOut(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } var ssi = new SetSwitchImmediate(this) { DeviceID = 0x00, Switch = (byte)Switches.BaseSwitches.TSRAIR_TriggerOut, Port = (byte)Switches.SystemSwitches_425B.GPIO__MCU_EVENT, Setting = InvertTrigger ? Convert.ToByte(1) : Convert.ToByte(0) }; ssi.SyncExecute(); info.Success(); } #endregion #region IClockSyncActions protected override void AsyncGetClockSyncStatus(object asyncInfo) { //TSRAIR has a command for both profile and status fetch if (!(asyncInfo is GetClockSyncStatusPacket packet)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryClockSyncStatus)) { packet.Info.Error("Get clock sync status not supported"); return; } var info = packet.Info; var bLocked = false; try { Lock(); bLocked = true; var status = new QueryClockSyncStatus(this); status.SyncExecute(); DASClockSyncProfile = status.ClockSyncProfile; DASClockSyncStatus = status.InputSyncStatus; DoRTCInUTCCheck(); bLocked = false; Release(); info.Success(); } catch (CanceledException) { if (bLocked) { bLocked = false; Release(); } info.Cancel(); } catch (CommandException ce) { if (bLocked) { Release(); bLocked = false; } straightFailures++; if (straightFailures > PERMITTED_FAILURES) { APILogger.Log("GetClockSyncStatus error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { if (info.userData is DTS.DASLib.Service.ServiceBase.CallbackData cbData) { cbData.Target.DASClockSyncStatus = null; } info.Success(); APILogger.Log("GetClockSyncStatus error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } #endregion #region IUARTDownload public uint BaudRate { get; private set; } public uint DataBits { get; private set; } public StopBits StopBits { get; private set; } public Parity Parity { get; private set; } public Handshake FlowControl { get; private set; } public UartDataFormat DataFormat { get; private set; } public IUARTDownloadRequest WhatUARTToDownload { get; set; } public void SetWhatUARTToDownload(IUARTDownloadRequest request, bool bSetInDb = true) { UARTDownloadRequest.SetWhatToDownload(this, request, bSetInDb); } #endregion #region IUARTDownloadActions public void UARTDownload(ServiceCallback callback, object userData) { var state = new SliceUARTDownloadState(callback, userData, WhatUARTToDownload); if (null == WhatUARTToDownload) { APILogger.Log("UART download - what to download is null"); state.Success(); return; } APILogger.Log("Starting UART Download ", WhatUARTToDownload.StartTimestamp, WhatUARTToDownload.EndTimestamp); ulong totalDownloaded = 0, needed = WhatUARTToDownload.TotalByteCount; try { while (totalDownloaded < needed) { var getData = new QueryUARTEventData(this) { EventNumber = WhatUARTToDownload.EventNumber, LogCommands = false, RequestOffsetByteCount = totalDownloaded, RequestByteCount = needed - totalDownloaded < uint.MaxValue ? (uint)(needed - totalDownloaded) : uint.MaxValue }; getData.SyncExecute(); if (null == getData.PayloadData) { Thread.Sleep(10); continue; } state.NewData(new ServiceCallbackData.UARTNewData { DataOffset = getData.RequestOffsetByteCount, UARTData = getData.PayloadData }); var ratio = 100D * totalDownloaded / needed; if (ratio < 0) { ratio = 0; } else if (ratio > 100) { ratio = 100D; } state.Progress(Convert.ToInt32(ratio)); totalDownloaded += getData.PayloadByteCount; } state.Success(); } catch (CanceledException) { state.Cancel(); } catch (Exception e) { APILogger.LogException(e); state.Error(e.Message, e); } } /// /// Retrieve UART info about available events to download /// /// The function to call with information /// Whatever you want to pass along void IUARTDownloadActions.QueryUARTDownload(ServiceCallback callback, object userData, int eventIndex, TDASServiceSetupInfo setupInfo) { var info = new QueryDownloadAsyncInfo(callback, userData, eventIndex); LaunchAsyncWorker("Slice.QueryUARTDownload", AsyncQueryUARTDownload, info); } protected virtual void AsyncQueryUARTDownload(object asyncInfo) { if (!(asyncInfo is QueryDownloadAsyncInfo info)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryUARTDownload)) { info.Error("Query UART download is not supported"); return; } var bLocked = false; try { Lock(); bLocked = true; var status = new QueryUARTEventInfo(this) { EventNumber = (ushort)info.EventIndex }; status.SyncExecute(); bLocked = false; Release(); info.Success(); } catch (CanceledException) { if (bLocked) { bLocked = false; Release(); } info.Cancel(); } catch (CommandException ce) { if (bLocked) { Release(); bLocked = false; } straightFailures++; if (straightFailures > PERMITTED_FAILURES) { APILogger.Log("QueryUARTDownload error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { if (info.userData is DTS.DASLib.Service.ServiceBase.CallbackData cbData) { //cbData.Target.DASClockSyncStatus = null; } info.Success(); APILogger.Log("QueryUARTDownload error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } internal class SetUARTSettingsAsyncInfo : SliceServiceAsyncInfo { public uint BaudRate { get; set; } public uint DataBits { get; set; } public uint StopBits { get; set; } public uint Parity { get; set; } public uint FlowControl { get; set; } public SetUARTSettingsAsyncInfo(ServiceCallback callback, object userData, uint baudRate, uint dataBits, uint stopBits, uint parity, uint flowControl) : base(callback, userData) { BaudRate = baudRate; DataBits = dataBits; StopBits = stopBits; Parity = parity; FlowControl = flowControl; } } void IUARTDownloadActions.GetUARTSettings(ServiceCallback callback, object userData) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("Slice.GetUARTSettings", AsyncGetUARTSettings, info); } protected virtual void AsyncGetUARTSettings(object asyncInfo) { if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.GetUARTSettings)) { info.Error("Get UART settings is not supported"); return; } var bLocked = false; try { Lock(); bLocked = true; try { var qsaUARTSettings = new QuerySystemAttributeSLICE2(this, QuerySystemAttribute.Default_IO_Timeout); qsaUARTSettings.Key = AttributeTypes.SystemAttributesSLICE2.S6A_GpsCanUARTSettings; qsaUARTSettings.SyncExecute(); //we made it, set results var uartSettings = (uint[])qsaUARTSettings.Value; BaudRate = uartSettings[0]; DataBits = uartSettings[1]; StopBits = (StopBits)uartSettings[2]; Parity = (Parity)uartSettings[3]; FlowControl = (Handshake)uartSettings[4]; } catch (Exception ex) { APILogger.Log("Problem getting UART settings", ex); } bLocked = false; Release(); info.Success(); } catch (CanceledException) { if (bLocked) { bLocked = false; Release(); } info.Cancel(); } catch (CommandException ce) { if (bLocked) { Release(); bLocked = false; } straightFailures++; if (straightFailures > PERMITTED_FAILURES) { APILogger.Log("GetUARTSettings error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { info.Success(); APILogger.Log("GetUARTSettings error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } void IUARTDownloadActions.SetUARTSettings(ServiceCallback callback, object userData, uint baudRate, uint dataBits, uint stopBits, uint parity, uint flowControl) { var info = new SetUARTSettingsAsyncInfo(callback, userData, baudRate, dataBits, stopBits, parity, flowControl); LaunchAsyncWorker("Slice.SetUARTSettings", AsyncSetUARTSettings, info); } protected virtual void AsyncSetUARTSettings(object asyncInfo) { if (!(asyncInfo is SetUARTSettingsAsyncInfo info)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetUARTSettings)) { info.Error("Set UART settings is not supported"); return; } var bLocked = false; try { Lock(); bLocked = true; var value = new uint[] { info.BaudRate, info.DataBits, info.StopBits, info.Parity, info.FlowControl }; try { var ssaUARTSettings = new SetSystemAttributeSLICE2(this, SetSystemAttribute.Default_IO_Timeout); ssaUARTSettings.SetValue(AttributeTypes.SystemAttributesSLICE2.S6A_GpsCanUARTSettings, value, true); ssaUARTSettings.SyncExecute(); } catch (Exception ex) { APILogger.Log("Problem setting UART settings", ex); } bLocked = false; Release(); info.Success(); } catch (CanceledException) { if (bLocked) { bLocked = false; Release(); } info.Cancel(); } catch (CommandException ce) { if (bLocked) { Release(); bLocked = false; } straightFailures++; if (straightFailures > PERMITTED_FAILURES) { APILogger.Log("SetUARTSettings error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { info.Success(); APILogger.Log("SetUARTSettings error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } #endregion } #region QueryEventData_TSRAIR /// /// this meaty class handles skipping parts of the download not needed (start of page to desired start sample) /// [also note we'll need to do the same thing with the end sample too if we want to use the ECC properly, /// but ECC isn't even implemented yet ...] /// public class QueryEventData_TSRAIR : QueryEventDataBase { public override UInt64 FirstSample { get => base.FirstSample; set => base.FirstSample = value; } public override UInt64 LastSample { get => base.LastSample; set => base.LastSample = value; } public QueryEventData_TSRAIR(DTS.Common.Interface.DASFactory.ICommunication sock) : base(sock) { LogCommands = false; } public QueryEventData_TSRAIR(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec) : base(sock, TimeoutMillisec) { LogCommands = false; } private ulong GetRequestedStartSpot() { if (recorder is TSRAIR tsr_air_usb) { return ((WhatToDownloadTSRAIR)tsr_air_usb.WhatToDownload).RequestedStartSport; } else if (recorder is TSRAIR tsr_air_ethernet) { return ((WhatToDownloadTSRAIR)tsr_air_ethernet.WhatToDownload).RequestedStartSport; } else { throw new NotSupportedException("GetRequestedStartSport not supported for " + recorder.ConnectString); } } private void PushLeftOverData(ushort[] daters) { if (recorder is TSRAIR tsr_air_usb) { tsr_air_usb.PushLeftOverData(daters); } else if (recorder is TSRAIR tsr_air_ethernet) { tsr_air_ethernet.PushLeftOverData(daters); } } protected override CommandReceiveAction WholePackagePost() { // now send the data to the user var stat = CommandStatus.Success; if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError) { var s = (int)response.Status; APILogger.LogString("QueryEventData.WholePackagePost: reporting failure, status==" + CommandPacket.StatusLabels[s] + " (0x" + s.ToString("X") + ")"); stat = CommandStatus.Failure; } var cbReport = new QueryEventDataReport(stat, UserCallbackData); cbReport.Data = new short[_channelsDownloaded][]; for (var i = 0; i < _channelsDownloaded; i++) GetChannelData(i, out cbReport.Data[i]); //we have processed some data, but there may be some left over (since data isn't channel sample aligned ...) //figure out what we used and what's left over //now we have two situations, one, we have already skimmed beyond all the data we need //or two, we are somewhere in between, we need to skip a few samples var requestedStartSpot = GetRequestedStartSpot(); if ((FirstSample + (ulong)_data.Length) < requestedStartSpot) { //push no data, we don't want it! } else if (FirstSample > requestedStartSpot) {//we want everything in here ... var samplesProcessed = Convert.ToInt32(Math.Truncate((double)_data.Length / ChannelsDownloaded)); var offset = samplesProcessed * ChannelsDownloaded; var leftover = new ushort[_data.Length - offset]; for (var i = 0; i < leftover.Length; i++) { leftover[i] = _data[i + offset]; } PushLeftOverData(leftover); } else { //we need to calculate samples only from the start of the data we are interested in var offset = Convert.ToInt32(requestedStartSpot - FirstSample); int samplesProcessed = Convert.ToInt32(Math.Truncate(((double)_data.Length - offset) / ChannelsDownloaded)); var product = samplesProcessed * ChannelsDownloaded; var leftover = new ushort[_data.Length - offset - product]; offset += product; for (var i = 0; i < leftover.Length; i++) { leftover[i] = _data[i + offset]; } PushLeftOverData(leftover); } return UserCallback(cbReport); } private ushort[] PopLeftOverData() { if (recorder is TSRAIR tsr_air_usb) { return tsr_air_usb.PopLeftOverData(); } else if (recorder is TSRAIR tsr_air_ethernet) { return tsr_air_ethernet.PopLeftOverData(); } else { throw new NotImplementedException(); } } protected override CommandReceiveAction WholePackage() { if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError) { return CommandReceiveAction.StopReceiving; } //we are going to process the data shortly, but before we do we'll need to //pre-pend any left over data we have to the new incoming data //since we already count the samples downloaded for samples in the left over stuff //we don't need to recount it, just the new incoming samples _samplesDownloaded = (ulong)(response.Parameter.Length) / 2; var leftover = PopLeftOverData(); _data = new ushort[_samplesDownloaded + (ulong)leftover.Length]; leftover.CopyTo(_data, 0); for (var i = 0; (ulong)i < _samplesDownloaded; i++) { response.GetParameter(2 * i, out _data[i + leftover.Length]); } return CommandReceiveAction.StopReceiving; } public override void GetChannelData(int channel, out short[] signedADC) { if (channel < 0 || channel > _channelsDownloaded) { throw new ApplicationException("QueryEventData.GetChannelData: Data requested on a channel that wasn't downloaded."); } //first short circuit if we know we are still completely skipping data if (((ulong)_data.Length + FirstSample) < GetRequestedStartSpot())//(slice2.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport) { signedADC = new short[0];//nothing to see here (we are completely before the start of our requested data) return; } //now we have two situations, one, we have already skimmed beyond all the data we need //or two, we are somewhere in between, we need to skip a few samples var offset = 0; if (GetRequestedStartSpot() > FirstSample) { offset = Convert.ToInt32(GetRequestedStartSpot() - FirstSample); } // Data order for a 9 channel stack // 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 etc. ushort val; var completeSamples = Convert.ToInt32(Math.Truncate((double)(_data.Length - offset) / ChannelsDownloaded)); //performance improvements, use simpler structures and do less calculations over the iterations var rv = new short[completeSamples]; offset += channel; for (var i = 0; i < completeSamples; i++) { val = _data[i * ChannelsDownloaded + offset]; rv[i] = (short)(((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)); //+ 0x8000); no offset add for tsr air } signedADC = rv.ToArray(); } // this function isn't used by SLICEWare, but it is used by the FirmwareTestUtility // SW uses the GetChannelData above public override void GetRawIndexedData(int index, out ushort[] data) { data = new ushort[_samplesDownloaded]; for (var i = 0; i < data.Length; i++) { data[i] = _data[i + index]; } } } #endregion public class WhatToDownloadTSRAIR : DownloadRequest { /// /// this will hold the number of channels in the download, this is needed /// to calculate the sample alignment and request/response sample numbers. /// the SLICE2 firmware uses samples counts irrespective of channels, so they must be realigned post /// download /// private readonly ulong _numberOfChannels = 0; public ulong RequestedStartSport { get; } public WhatToDownloadTSRAIR(IDownloadRequest dr, int numberOfChannels, int pageSize) { if (dr is WhatToDownloadTSRAIR dr2) { _numberOfChannels = (ulong)numberOfChannels; DASChannelNumber = dr.DASChannelNumber; base.EndSample = dr.EndSample; EventNumber = dr.EventNumber; SamplesToSkip = dr.SamplesToSkip; base.StartSample = dr.StartSample; RequestedStartSport = dr2.RequestedStartSport; StartRecordTimestampSec = dr.StartRecordTimestampSec; TriggerTimestampSec = dr.TriggerTimestampSec; } else { _numberOfChannels = (ulong)numberOfChannels; if (null == dr) { return; } DASChannelNumber = dr.DASChannelNumber; base.EndSample = 0 == dr.EndSample ? 0 : (dr.EndSample + 1) * _numberOfChannels - 1; EventNumber = dr.EventNumber; SamplesToSkip = dr.SamplesToSkip; //find the start of the page the start sample is on var nearestdiv = Math.Truncate(dr.StartSample * (double)numberOfChannels / pageSize); base.StartSample = Convert.ToUInt64(nearestdiv * pageSize); RequestedStartSport = dr.StartSample * (ulong)numberOfChannels; StartRecordTimestampSec = dr.StartRecordTimestampSec; TriggerTimestampSec = dr.TriggerTimestampSec; } } /// /// /// if EndSample gets set by a generic service, it is not expecting sample counts to be irrespective of channels /// so we convert it to be irrespective here /// however, we want to return what we consider the endsample so looping code doesn't give up until we really are done /// with all the samples we want /// public override ulong EndSample { get => base.EndSample; set => base.EndSample = (value + 1) * _numberOfChannels; } /// /// /// adjusting the start sample is a little more complicated than adjusting the endsample, we need to start /// downloading from the start of the page that the start sample we want is on /// public override ulong StartSample { get => base.StartSample; set => base.StartSample = value; } } }