//#define _8868_MEASUREADCAT0MV_OLDSCHOOL using System; using System.Collections.Generic; using System.Linq; using System.Text; using DTS.DASLib.Command.SLICE; using DTS.Common.Utilities; using DTS.Common.DAS.Concepts; using DTS.DASLib.Command; using DTS.Common; using DTS.Common.DASResource; using DTS.Common.Enums; using DTS.Common.ICommunication; using DTS.Common.Utilities.Logging; using DTS.Common.WINUSBConnection; using DTS.DASLib.Command.SLICE.DownloadCommands; using DTS.DASLib.Command.SLICE.RealtimeCommands; using DTS.DASLib.Utility; using DTS.Common.Classes.Sensors; using System.IO; using System.Xml; using DTS.Common.Enums.Sensors; using DTS.Common.Classes.Connection; using DTS.Common.Interface.Connection; using DTS.Common.Interface.DASFactory.Diagnostics; using DTS.Common.Interface.DASFactory.Download; using DTS.Common.Enums.DASFactory; using DTS.Common.Interface.DASFactory; using DTS.Common.Enums.Hardware; using DTS.Common.Interface.DASFactory.Config; using Prism.Ioc; using DTS.Common.Events.DASFactory; using DTS.Common.Constant.DASSpecific; using DTS.Common.Utilities.LTLogging; using DTS.DASLib.Service.FirmwareUtility; using Prism.Events; using DTS.Common.SharedResource.Strings; namespace DTS.DASLib.Service { /// /// /// slice 2 does it's downloads a little differently /// namely, it downloads pages at a time regardless of where the break in sample alignment is /// it also requires that we make requests for samples based not on the sample number, but /// the byte where the sample are. /// this is partially because of ECC block size and partially for more efficient transfer /// public class WhatToDownloadSlice2 : 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 WhatToDownloadSlice2(IDownloadRequest dr, int numberOfChannels, int pageSize) { if (dr is WhatToDownloadSlice2 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; //we add one to the end sample for consistency with SLICE1 based code? - basically //we need one more sample then the firmware is sending right now ... base.EndSample = (dr.EndSample + 1) * _numberOfChannels; 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; } } public class SLICE2_Base : Slice, IConfigurationActions, IVoltageInsertionEnabled, ITimeActions where T : IConnection, new() { /// /// indicates that voltage insertion was detected as being on /// http://manuscript.dts.local/f/cases/34284/Warn-when-VoltageInsertion-switches-are-set /// public bool VoltageInsertionEnabled { get; protected set; } public override void SetIsStreamingSupported(bool supported = false) { IsStreamingSupported = false; } public override void ReadFirstUseDate() { try { if (RunTestVariables.InRunTest && !RunTestVariables.CheckHardwareFirstUseDateInRunTest) { //skip IsFirstUseDateSupported = false; FirstUseDate = null; } else { var attr = GetUserAttribute(AttributeTypes.SliceUserAttributes.FirstUseDate); if (string.IsNullOrWhiteSpace(attr)) { IsFirstUseDateSupported = false; FirstUseDate = null; return; } if (DateTime.TryParse(attr, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var dt)) { IsFirstUseDateSupported = true; if (dt.Year == DFConstantsAndEnums.FIRST_USE_DATE_NOT_SET.Year) { FirstUseDate = null; } else { FirstUseDate = dt; } return; } IsFirstUseDateSupported = true; FirstUseDate = null; } } catch (Exception ex) { APILogger.Log(ex); IsFirstUseDateSupported = false; FirstUseDate = null; } } protected override void SetFirstUseDateAsync(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } try { var date = DFConstantsAndEnums.FIRST_USE_DATE_NOT_SET.ToString("d", System.Globalization.CultureInfo.InvariantCulture); if (null != FirstUseDate) { date = ((DateTime)FirstUseDate).ToString("d", System.Globalization.CultureInfo.InvariantCulture); } SetUserAttribute(AttributeTypes.SliceUserAttributes.FirstUseDate, date); } catch (Exception ex) { info.Error(ex.Message); return; } info.Success(); } public override HardwareTypes GetHardwareType() { if (!string.IsNullOrWhiteSpace(SerialNumber)) { if (SerialNumber.StartsWith("SPS")) { return HardwareTypes.SLICE2_SIM; } if (SerialNumber.StartsWith("SLS")) { return HardwareTypes.SLICE2_SLS; } if (SerialNumber.StartsWith("SPD")) { return HardwareTypes.SLICE2_DIM; } if (SerialNumber.StartsWith("SLD")) { return HardwareTypes.SLICE2_SLD; } if (SerialNumber.StartsWith("SLD")) { return HardwareTypes.SLICE2_SLD; } if (SerialNumber.StartsWith("SPT")) { return HardwareTypes.SLICE2_TOM; } if (SerialNumber.StartsWith("SLT")) { return HardwareTypes.SLICE2_SLT; } } return HardwareTypes.SLICE2_Base; } public override int[] GetStackChannelConfigTypes() => new int[] { 0 }; protected override void AsyncAutoDetect(object o) { if (!(o is AutoDetectServiceAsyncInfo info)) { return; } if (null == DASInfo || null == DASInfo.Modules || 0 == DASInfo.Modules.Length || null == ConfigData || 0 == ConfigData.Modules.Length) { info.Error($"no configuration on unit {SerialNumber}"); return; } if (DASInfo.Modules[0].IsProgrammable) { var channels = new List(); foreach (var module in ConfigData.Modules) { foreach (var _ in module.Channels) { channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.AUTO_DETECT); } } var cmd = new SetStackChannelTypeConfiguration(this); cmd.SetValue(channels.ToArray()); if (info.QueryConfiguration) { var query = new QueryStackSensorIDs(this); query.Exec(); var idx = 0; foreach (var m in ConfigData.Modules) { foreach (var c in m.Channels) { if (c is AnalogInputDASChannel) { switch (Convert.ToInt32(query.Types[idx])) { case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE: (c as AnalogInputDASChannel).IEPEChannel = true; break; case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE_LOW: (c as AnalogInputDASChannel).IEPEChannel = true; break; default: (c as AnalogInputDASChannel).IEPEChannel = false; break; } var id = new EID(HexEncoding.ToString(query.IDs[idx])); if (id.IsValid() && !RunTestVariables.BypassEIDRead) { c.IDs = new EID[] { id }; } } idx++; } } } } info.Success(); } protected override void AsyncClearLatches(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } var QATS = new QueryArmAndTriggerStatus(this); QATS.SyncExecute(); info.Success(); } protected override void AsyncClearTriggerOut(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } var ssi = new SetSwitchImmediate(this) { DeviceID = 0x00, Switch = (byte)Switches.BaseSwitches.TriggerOut, SwitchText = Switches.BaseSwitches.TriggerOut.ToString(), Setting = InvertTrigger ? Convert.ToByte(1) : Convert.ToByte(0) }; ssi.SyncExecute(); info.Success(); } /// /// clears trigger lines, used by AsyncClearDASTriggerLine and AsyncStartTriggerCheck /// private void ClearTriggerLine() { try { //reset the FPGA of SLICE6/SLICE2 which may have latched and will remain latched (until something resets it) //IHL will apparently NOT reset it //this should come atleast 1.5s after the SLICE6DB/ECM pulse, however there's a mandatory 1.7s wait by genericservices.starttriggercheck between //ECM/S6DB and regular units var qats = new QueryArmAndTriggerStatus(this); qats.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex); } //performance improvement, don't set polarity in systems that don't need to //[reduce # of commands] if (RunTestVariables.InRunTest && RunTestVariables.DontSetPolarityInRunTest) { //don't set } else { var ssaStart = new SetSystemAttribute(this); var ssaTrigger = new SetSystemAttribute(this); try { ssaStart.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true); ssaTrigger.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true); ssaStart.SyncExecute(); ssaTrigger.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex); } } } /// /// complete any work we need to do before starting trigger check /// protected override void AsyncPreStartTriggerCheck(object asyncInfo) { if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; } // 14459 Trigger shorted error after running check trigger twice. // if software trigger was used, it will still be set, this is to make sure it's unset // prior to starting trigger check, this is occuring before the QATS that will reset // FPGA status ClearTriggerLine(); info.Success(); } /// /// clears any trigger lines for any das /// protected override void AsyncClearDASTriggerLine(object asyncInfo) { if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; } ClearTriggerLine(); info.Success(); } protected override void AsyncStartTriggerCheck(object asyncInfo) { if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; } try { ClearTriggerLine(); var ihl = new InitializeHardwareLines(this) { LogCommands = true }; try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines)) { ihl.SyncExecute(); } else { throw new Exception("command not supported: InitHardareInputLines"); } } catch (Exception ex) { InitializeHardwareLines.Log(ex, ihl); } if (ihl.TriggerInputShorted) { info.Error("TriggerInputShorted"); return; } if (ihl.StartRecordShorted) { info.Error("StartInputShorted"); return; } info.Success(); } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } /// /// /// gets the expected excitation for a given channel /// note for SLICE2 we need to consider 2V,5V, and 10V, however 2V or 2.5V may both refer to 2V /// returns 0 if excitation could not be retrieved, otherwise excitation in mV /// /// /// /// protected override double GetExpectedExcitationMV(int moduleIndex, int channelOnModule) { if (ConfigData?.Modules == null || ConfigData.Modules.Length <= moduleIndex) { APILogger.Log("could not get expected excitaiton, no configuration data to base it on"); return 0D; } if (!(ConfigData.Modules[moduleIndex].Channels[channelOnModule] is AnalogInputDASChannel aic)) { //we never expect to get here, but if we ever do, there's no excitation to consider ... APILogger.Log("could not get expected excitation, channel is not analog"); return 0D; } var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(aic.Excitation) * 1000D; try { var qsa = new QuerySystemAttribute_Slice2Bridge(this); switch (channelOnModule) { case 0: switch (aic.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_10000mV; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_5000mV; break; default: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_2000mV; break; } break; case 1: switch (aic.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_10000mV; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_5000mV; break; default: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_2000mV; break; } break; default: switch (aic.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_10000mV; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_5000mV; break; default: qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_2000mV; break; } break; } //module 0 is the base, so we have to add one to offset this qsa.DeviceID = Convert.ToByte(1 + moduleIndex); qsa.SyncExecute(); var bridgeExcitation = Convert.ToDouble(qsa.Value);//already in mV, no conversion needed var delta = Math.Abs(excitation - bridgeExcitation); if (delta < 500) { excitation = bridgeExcitation; } } catch (Exception ex) { APILogger.Log("failed to get expected excitation: ", ex); } return excitation; } private const ushort SLICE_SHUNT_DAC_MIDSCALE = 2116; protected override void QueryInternalOffsets(ref DiagnosticsResult[] results) { } protected override void MeasureInternalOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results, bool bFinalOffset) { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffset)) { #region New internal offset measure // first count how many we need to do it on var numToMeasure = ChannelActions.Count(a => a.MeasureInternalOffset); if (numToMeasure == 0) return; try { var measureInternalOffset = new MeasureStackInternalChannelOffset(this); measureInternalOffset.NumberOfChannels = (byte)numToMeasure; measureInternalOffset.DeviceID = 0; // send to base var measureChannels = new byte[numToMeasure]; var channelCounter = 0; for (var idx = 0; idx < ChannelActions.Length; idx++) { if (ChannelActions[idx].MeasureInternalOffset) { measureChannels[channelCounter++] = (byte)ChannelActions[idx].DASChannelNumber; } } measureInternalOffset.ChannelList = measureChannels; measureInternalOffset.SyncExecute(); // calculate result channelCounter = 0; var measuredChannelBiasMv = new double[ChannelDiagnostics.Length]; for (var idx = 0; idx < ChannelActions.Length; idx++) { if (ChannelActions[idx].MeasureInternalOffset) { var result = (double)measureInternalOffset.MeasuredInternalOffset[channelCounter]; measuredChannelBiasMv[idx] = result; channelCounter++; } } for (var idx = 0; idx < results.Length; idx++) { info.NewData(new ServiceCallbackData.DiagnosticNewData() { Result = measuredChannelBiasMv[idx], DasChannelNumber = results[idx].DASChannelNumber, Action = ServiceCallbackData.DiagnosticNewData.Actions.MeasureInternalOffset }); //attempt to resolve the removed adc by using the before and after info try { if (bFinalOffset) { if (null != results[idx] && ChannelActions[idx].RemoveOffset) { results[idx].RemovedInternalOffsetADC = Convert.ToInt32((double)results[idx].MeasuredInternalOffsetMilliVolts / results[idx].ScalefactorMilliVoltsPerADC - measuredChannelBiasMv[idx] / results[idx].ScalefactorMilliVoltsPerADC); } else { results[idx].RemovedInternalOffsetADC = 0; } } else { results[idx].MeasuredInternalOffsetMilliVolts = measuredChannelBiasMv[idx]; if (results[idx].ScalefactorMilliVoltsPerADC == 0) { results[idx].ZeroMVInADC = 0; } else { results[idx].ZeroMVInADC = Convert.ToInt16(measuredChannelBiasMv[idx] / results[idx].ScalefactorMilliVoltsPerADC); } } } catch (Exception ex) { APILogger.Log( $"Error Retrieving Internal Offset on {SerialNumber} channel {results[idx].DASChannelNumber}", ex); results[idx].RemovedInternalOffsetADC = 0; results[idx].MeasuredInternalOffsetMilliVolts = 0; } } } catch (Exception ex) { APILogger.Log("Error Retrieving Internal Offset", ex); for (var idx = 0; idx < results.Length; idx++) { results[idx].RemovedInternalOffsetADC = 0; results[idx].MeasuredInternalOffsetMilliVolts = 0; } } #endregion } else if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffsetSwitches) && GEN3_REV_B == _hardwareRevision) { #region 8668 MEASUREADCAT0MV SWITCHES try { // first count how many we need to do it on var numToMeasure = ChannelActions.Count(a => a.MeasureInternalOffset); if (numToMeasure == 0) return; var existingCourseShunts = new Dictionary(); var existingFineShunts = new Dictionary(); var existingSwitchValues = new Dictionary>(); VoltageInsertionEnabled = false; //query switches and DACs for resetting later foreach (var module in DASInfo.Modules) { var qdac = new QueryDACImmediate(this); qdac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qdac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt; qdac.SyncExecute(); existingCourseShunts[module.ModuleArrayIndex] = qdac.Value; qdac = new QueryDACImmediate(this); qdac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qdac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt; qdac.SyncExecute(); existingFineShunts[module.ModuleArrayIndex] = qdac.Value; var qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID] = new Dictionary(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; } qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; } qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; } qsi = new QuerySwitchImmediate(this); qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC; qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString(); qsi.SyncExecute(); existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting; if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; } } //set switches & DACs uint fastestSPS = 0; foreach (var module in DASInfo.Modules) { fastestSPS = Math.Max(ConfigData.Modules[module.ModuleArrayIndex].SampleRateHz, fastestSPS); var ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString(); ssi.Setting = Convert.ToByte(false); ssi.SyncExecute(); var sDac = new SetDACImmediate(this); sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt; sDac.Value = SLICE_SHUNT_DAC_MIDSCALE; sDac.SyncExecute(); sDac = new SetDACImmediate(this); sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt; sDac.Value = SLICE_SHUNT_DAC_MIDSCALE; sDac.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString(); ssi.Setting = Convert.ToByte(true); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString(); ssi.Setting = Convert.ToByte(false); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString(); ssi.Setting = Convert.ToByte(true); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString(); ssi.Setting = Convert.ToByte(true); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString(); ssi.Setting = Convert.ToByte(true); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString(); ssi.Setting = Convert.ToByte(true); ssi.SyncExecute(); } //retrieve sample averages 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)(fastestSPS * avgTimeSeconds); if (measureOffset.Samples < 1) { measureOffset.Samples = 1; } measureOffset.SyncExecute(); for (var i = 0; i < measureOffset._data.Length; i++) { if (bFinalOffset) { results[i].RemovedInternalOffsetADC = measureOffset._data[i]; } else { try { results[i].MeasuredInternalOffsetMilliVolts = measureOffset._data[i] * results[i].ScalefactorMilliVoltsPerADC; results[i].ZeroMVInADC = measureOffset._data[i]; APILogger.Log("MeasuredInternalOffsetMv:", measureOffset._data[i], "*", results[i].ScalefactorMilliVoltsPerADC, "=", results[i].MeasuredInternalOffsetMilliVolts); APILogger.Log(SerialNumber, string.Format("CH{0}", results[i].DASChannelNumber), "ZeroMVInADC=", results[i].ZeroMVInADC); } catch (Exception ex) { results[i].MeasuredInternalOffsetMilliVolts = 0D; APILogger.Log(ex, "FinalOffset:", bFinalOffset); } } } //unset switches & DACs, do the opposite of everything/restore foreach (var module in DASInfo.Modules) { var ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted; ssi.Setting = existingSwitchValues[ssi.DeviceID][(byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted]; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString(); ssi.SyncExecute(); var sDac = new SetDACImmediate(this); sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt; sDac.Value = existingCourseShunts[module.ModuleArrayIndex]; sDac.SyncExecute(); sDac = new SetDACImmediate(this); sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt; sDac.Value = existingFineShunts[module.ModuleArrayIndex]; sDac.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString(); ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch]; ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString(); ssi.Setting = Convert.ToByte( existingSwitchValues[ssi.DeviceID][ ssi.Switch]); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted; ssi.Setting = existingSwitchValues[ssi.DeviceID][(byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted]; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString(); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA; ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch]; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString(); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB; ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch]; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString(); ssi.SyncExecute(); ssi = new SetSwitchImmediate(this); ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex); ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC; ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString(); ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch]; ssi.SyncExecute(); } } catch (Exception ex) { APILogger.Log(ex, "finalOffset:", bFinalOffset); foreach (var r in results) { if (bFinalOffset) { r.RemovedInternalOffsetADC = 0; } else { r.MeasuredInternalOffsetMilliVolts = 0D; } } } #endregion } else { //Command not supported for (var idx = 0; idx < results.Length; idx++) { results[idx].RemovedInternalOffsetADC = 0; results[idx].MeasuredInternalOffsetMilliVolts = 0; } } } /// /// /// Convert gain code to value based on Gen2 vs. Gen3 conversion table /// /// /// protected override double GainCodeToGainValue(ushort gainCode) { var gainValueString = _hardwareRevision == GEN3_REV_B ? ((GainCodesGen3)gainCode).ToString() : ((GainCodesGen2)gainCode).ToString(); if (!double.TryParse(gainValueString.TrimStart('G'), out var gainValue)) { gainValue = 1.0D; } return gainValue; } private SerializableDictionary _userAttributes = null; private static readonly object USER_ATTRIBUTE_LOCK = new object(); public string GetUserAttribute(AttributeTypes.SliceUserAttributes attribute) { CreateUserAttributesIfNeeded(); lock (USER_ATTRIBUTE_LOCK) { if (!_userAttributes.ContainsKey((int)attribute)) { return string.Empty; } return _userAttributes[(int)attribute]; } } public void SetUserAttribute(AttributeTypes.SliceUserAttributes attribute, string value) { CreateUserAttributesIfNeeded(); lock (USER_ATTRIBUTE_LOCK) { _userAttributes[(int)attribute] = value; var sb2 = new StringBuilder(); using (var w = XmlWriter.Create(sb2)) { _userAttributes.WriteXml(w); } try { var ca = GetConfigAttributes(this); ca.StoreXMLConfig(sb2.ToString(), ConfigAttributes.FileStore.UserAttributes); } catch (Exception ex) { APILogger.Log(ex); } } } private void CreateUserAttributesIfNeeded() { lock (USER_ATTRIBUTE_LOCK) { if (null == _userAttributes) { _userAttributes = new SerializableDictionary(); try { var ca = GetConfigAttributes(this); var xml = ca.RetrieveXMLConfig(ConfigAttributes.FileStore.UserAttributes, this); if (!string.IsNullOrEmpty(xml)) { //slice firmware tends to pad with \0, which is invalid in xml, strip them out first xml = xml.Replace("\0", string.Empty); var settings = new XmlReaderSettings(); settings.IgnoreComments = true; settings.IgnoreWhitespace = true; using (var sr = new StringReader(xml)) { var r = XmlReader.Create(sr, settings); _userAttributes.ReadXml(r); } } } catch (Exception) { } } } } /// /// slice pro/slice1.5 both don't support user attributes, so we store them in the filestore /// specifically we store a dictionary in store 4, keyed by user attribute id, with a string value /// /// public override int GetDASDisplayOrder() { var sDisplayOrder = GetUserAttribute(AttributeTypes.SliceUserAttributes.DasDisplayOrder); if (int.TryParse(sDisplayOrder, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var temp)) { return temp; } return -1; } /// /// /// slice 1.5 and slice pro don't have a user attribute store, so we use the file store instead /// we store a dictionary in there keyed by attribute number with string value /// /// public override void SetDASDisplayOrder(int order) { SetUserAttribute(AttributeTypes.SliceUserAttributes.DasDisplayOrder, order.ToString(System.Globalization.CultureInfo.InvariantCulture)); } /// /// sets the channel display order for slice 1.5/slice pro /// /// public override void SetChannelDisplayOrder(int[] order) { var _ = GetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder); var sb = new StringBuilder(); foreach (var i in order) { if (sb.Length > 0) { sb.Append(","); } sb.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture)); } SetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder, sb.ToString()); } /// /// gets the channel display order for slice 1.5/slice pro /// /// public override int[] GetChannelDisplayOrder() { var s = GetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder); var atts = new List(); if (!string.IsNullOrWhiteSpace(s)) { var tokens = s.Split(','); foreach (var token in tokens) { if (int.TryParse(token, out int iTemp)) { atts.Add(iTemp); } } } return atts.ToArray(); } internal class TimeSynchronizationAsyncInfo : SliceServiceAsyncInfo { public DateTime Time { get; set; } public TimeSynchronizationAsyncInfo(ServiceCallback callback, object userData, DateTime time) : base(callback, userData) { Time = time; } } void ITimeActions.Synchronize(ServiceCallback callback, object userData, DateTime time) { var info = new TimeSynchronizationAsyncInfo(callback, userData, time); LaunchAsyncWorker("Slice2.Synchronize", AsyncSynchronize, info); } private void AsyncSynchronize(object info) { var packet = (TimeSynchronizationAsyncInfo)info; try { var set = new SetBaseSystemTime(this); set.SystemTime = packet.Time; set.SyncExecute(); var query = new QueryBaseSystemTime(this); query.SyncExecute(); } catch (Exception ex) { packet.Error(ex.Message); return; } packet.Success(); } private DateTime _systemDateTime = DateTime.MinValue; public override DateTime SystemBaseTime => _systemDateTime; void ITimeActions.QueryTime(ServiceCallback callback, object userData) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("Slice2.QueryTime", AsyncQueryTime, info); } private void AsyncQueryTime(object info) { var packet = (SliceServiceAsyncInfo)info; try { var query = new QueryBaseSystemTime(this); query.SyncExecute(); packet.NewData(query.SystemTime); } catch (Exception ex) { packet.Error(ex.Message); return; } packet.Success(); } public override bool RequireDiagnosticRateMatchSampleRate() { return true; } private volatile bool _rangeBandwithLimited = false; public override bool RangeBandwidthLimited => _rangeBandwithLimited; private volatile bool _supportsTimeSynchronization = false; public override bool SupportsTimeSynchronization => _supportsTimeSynchronization; private static readonly double IEPE_GAIN_DIVIDER = 4.9D; //0.2040816327F, 2.0408163265F public static StaticInformation StaticDASIEPESLICE2Info = new StaticInformation(new double[] { 2400D / (1.0D / IEPE_GAIN_DIVIDER), 2400D / (2.0D / IEPE_GAIN_DIVIDER), 2400D / (5.0D / IEPE_GAIN_DIVIDER), 2400D / (10.0D / IEPE_GAIN_DIVIDER), 2400D / (20.0D / IEPE_GAIN_DIVIDER), 2400D / (50.0D / IEPE_GAIN_DIVIDER), 2400D / (100.0D / IEPE_GAIN_DIVIDER) }); /// /// this is the SPS gen 3 input range table for modified SPS /// http://fogbugz/fogbugz/default.asp?10080 /// note that we have artifically restricted this list to only provide /// the gains we wish to make available /// public static StaticInformation StaticDASRevCBridgeInfoModified = new StaticInformation(new[] { 2400D/1D, 2400D/2D, 2400D/4D, 2400D/8D, 2400D/16D, 2400D/20D, 2400D/32D, 2400D/40D, 2400D/64D, 2400D/80D, 2400D/160D, 2400D/320D, 2400D/640D, 2400D/800D, 2400D/1600D }); /// /// this is the SPS gen3 input range table for unmodified SPS /// http://fogbugz/fogbugz/default.asp?10080 /// note that we have artifically restricted this list twice, once to /// restricts gains above 1600 as not useful, and then we took out four /// "noisy" gains /// public static StaticInformation StaticDASRevCBridgeInfoNotModified = new StaticInformation(new[] { 2400D/1D, 2400D/2D, 2400D/4D, 2400D/8D, // 2400D/16D, 2400D/20D, // 2400D/32D, 2400D/40D, // 2400D/64D, 2400D/80D, 2400D/160D, 2400D/320D, // 2400D/640D, 2400D/800D, 2400D/1600D }); public static StaticInformation StaticDASRevCIEPEInfo = new StaticInformation(new double[]{ 2400D/(1D/IEPE_GAIN_DIVIDER), 2400D/(2D/IEPE_GAIN_DIVIDER), 2400D/(4D/IEPE_GAIN_DIVIDER), 2400D/(8D/IEPE_GAIN_DIVIDER), 2400D/(16D/IEPE_GAIN_DIVIDER), 2400D/(32D/IEPE_GAIN_DIVIDER), 2400D/(64D/IEPE_GAIN_DIVIDER) }); public enum GainCodesGen2 //Used by Slice PRO Gen2 { G1 = 8, G2 = 16, G4 = 17, G5 = 24, G8 = 18, G10 = 25, G16 = 19, G20 = 26, G32 = 20, G40 = 27, G50 = 48, G64 = 21, G80 = 35, G100 = 49, G128 = 22, G160 = 43, G200 = 50, G256 = 23, G320 = 37, G400 = 51, G640 = 45, G800 = 52, G1280 = 46, G1600 = 53, G2560 = 47, G3200 = 54, G6400 = 62, G12800 = 63 }; /// /// decorated with min/max/"middle" for /// http://fogbugz/fogbugz/default.asp?10080 /// public enum GainCodesGen3 //Used by Slice PRO Gen3 { [MaxInputRange(2500D)] [MinInputRange(1251.25)] [FirmwareInputRange(1875.625)] G1 = 8, [MaxInputRange(1250D)] [MinInputRange(625.625)] [FirmwareInputRange(937.8125)] G2 = 16, [MaxInputRange(625D)] [MinInputRange(156.40625)] [FirmwareInputRange(468.90625)] G4 = 24, [MaxInputRange(312.5D)] [MinInputRange(156.40625)] [FirmwareInputRange(234.45313)] G8 = 32, [GainAvailableUnmodified(false)] [MaxInputRange(156.25)] [MinInputRange(125.03125)] [FirmwareInputRange(140.64063)] G16 = 40, [MaxInputRange(125D)] [MinInputRange(78.17188)] [FirmwareInputRange(101.58594)] G20 = 17, [GainAvailableUnmodified(false)] [MaxInputRange(78.125)] [MinInputRange(62.51563)] [FirmwareInputRange(70.32031)] G32 = 48, [MaxInputRange(62.5)] [MinInputRange(39.08594)] [FirmwareInputRange(50.79297)] G40 = 25, [GainAvailableUnmodified(false)] [MaxInputRange(39.0625)] [MinInputRange(31.25781)] [FirmwareInputRange(35.16016)] G64 = 56, [MaxInputRange(31.25)] [MinInputRange(15.64063)] [FirmwareInputRange(23.44531)] G80 = 33, [MaxInputRange(15.625)] [MinInputRange(7.82031)] [FirmwareInputRange(11.72266)] G160 = 41, [MaxInputRange(7.8125)] [MinInputRange(3.91016)] [FirmwareInputRange(5.86133)] G320 = 49, [GainAvailableUnmodified(false)] [MaxInputRange(3.90625)] [MinInputRange(3.12578)] [FirmwareInputRange(3.51602)] G640 = 57, [MaxInputRange(3.125)] [MinInputRange(1.56406)] [FirmwareInputRange(2.34453)] G800 = 34, [MaxInputRange(1.5625)] [MinInputRange(.78203)] [FirmwareInputRange(1.17227)] G1600 = 42, [GainDisabled(true)] [MaxInputRange(.78125)] [MinInputRange(0.39102)] [FirmwareInputRange(0.58613)] G3200 = 50, [GainDisabled(true)] [MaxInputRange(.39063)] [MinInputRange(.15648)] [FirmwareInputRange(.27355)] G6400 = 58, [GainDisabled(true)] [MaxInputRange(.15625)] [MinInputRange(.0782)] [FirmwareInputRange(.11723)] G16000 = 43, [GainDisabled(true)] [MaxInputRange(.07813)] [MinInputRange(.03910)] [FirmwareInputRange(.05861)] G32000 = 51, [GainDisabled(true)] [MaxInputRange(.03906)] [MinInputRange(0)] [FirmwareInputRange(.01953)] G64000 = 59 }; /// /// 15462 Limit SLICE 6 AIR gains as they are not functional with IEPE accel /// algorithm requires the gains to be in order of firmware input range explicitly /// this can be used to sort enum values into order of firmware input range /// public class GainSorterSLICE6IEPE : IComparer { public int Compare(GainCodesSLICE6IEPE left, GainCodesSLICE6IEPE right) { var leftRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(left); var rightRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(right); return leftRange.CompareTo(rightRange); } } /// /// 10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs /// algorithm requires the gains to be in order of firmware input range explicitly /// this can be used to sort enum values into order of firmware input range /// public class GainSorter : IComparer { public int Compare(GainCodesGen3 left, GainCodesGen3 right) { var leftRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(left); var rightRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(right); return leftRange.CompareTo(rightRange); } } protected int _hardwareRevision = UNKNOWN_REVISION; protected int _baseType = -1; /// /// returns the nominal input ranges for SLICE2 /// this was modified for /// http://fogbugz/fogbugz/default.asp?10080 /// we now restrict the ranges by throwing out certain gains, etc /// /// /// whether the SPS is "modified" or not(http://fogbugz/fogbugz/default.asp?10080) /// public double[] GetNominalRanges(SensorConstants.BridgeType bridgeType, bool bModified) { //if we are not gen 3, do what we've always have done if (_hardwareRevision != GEN3_REV_B) return bridgeType == SensorConstants.BridgeType.IEPE ? StaticDASIEPESLICE2Info.NominalRanges : WinUSBSlice.StaticDASBridgeInfo.NominalRanges; //if we are IEPE, apparently we don't change anything if (bridgeType == SensorConstants.BridgeType.IEPE) { return StaticDASRevCIEPEInfo.NominalRanges; } //finally, for not IEPE, we either use the modified or not modified ranges return bModified ? StaticDASRevCBridgeInfoModified.NominalRanges : StaticDASRevCBridgeInfoNotModified.NominalRanges; } private ushort _featureConfigValue = 1; public override double[] GetNominalRanges(SensorConstants.BridgeType bridgeType) { if (_hardwareRevision != GEN3_REV_B) { //if we aren't gen3 we don't care whether we are modified or not //http://fogbugz/fogbugz/default.asp?10080 return GetNominalRanges(bridgeType, false); } return GetNominalRanges(bridgeType, 1 == _featureConfigValue); } /// /// checks whether the channel should perform voltage insertion or not /// currently this just checks that the input range for the channel is >= 10mV per LP /// http://manuscript.dts.local/f/cases/31824/Don-t-run-voltage-insertion-when-the-input-range-is-10mV /// http://manuscript.dts.local/f/cases/3472/Fwd-RE-SLICE-PRO-SIM-Gen3-SPS00134-Voltage-insertion-Error /// /// /// protected bool ShouldPerformVoltageInsertionCheck(IDiagnosticActions action) { try { if (!action.PerformVoltageInsertCheck) { return false; } var moduleNumber = DASInfo.MapDASChannelNumber2ModuleArrayIndex(action.DASChannelNumber); var channelNumber = DASInfo.MapDASChannelNumber2ModuleChannelNumber(action.DASChannelNumber); if (ConfigData.Modules[moduleNumber].Channels[channelNumber] is AnalogInputDASChannel aic) { if (double.IsNaN(aic.ScalefactorMilliVoltsPerADC) || 0 == aic.ScalefactorMilliVoltsPerADC) { //if any of these constraints fail treat the situation like existing behavior, run the check return true; } var mvInputRange = Math.Abs(aic.ScalefactorMilliVoltsPerADC * short.MaxValue); if (mvInputRange >= Constants.MINIMUM_VOLTAGE_INSERTION_RANGE_MV_SLICE) { return true; } } return false; } catch (Exception ex) { //if there's an exception, preserve the existing behavior, which would have been to run //the check APILogger.Log(ex); return true; } } protected override void PerformVoltageInsertionCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results) { // first count how many we need to do it on var NumToMeasure = ChannelActions.Count(a => ShouldPerformVoltageInsertionCheck(a)); if (NumToMeasure == 0) return; var baseType = QueryBaseType(); var revision = QueryHardwareRevision(); //Min Protocol for QueryVoltageInsertion if (baseType != PROBaseType || revision < 1) { // If the functionality doesn't exist, just make sure the results are set to null // and the app will do the right thing. // ClearVoltageInsertionResults(ChannelActions, results); } var queryChannelInsertResults = new QueryVoltageInsertaionResult_SLICE2(this); queryChannelInsertResults.DeviceID = 0; // send to base var insertionChannelList = new byte[NumToMeasure]; var channelCounter = 0; for (var idx = 0; idx < ChannelActions.Length; idx++) { var actions = ChannelActions[idx]; if (ShouldPerformVoltageInsertionCheck(actions)) { insertionChannelList[channelCounter] = (byte)actions.DASChannelNumber; channelCounter++; } } queryChannelInsertResults.StackChannelList = insertionChannelList; try { if (revision >= 1 && baseType == PROBaseType) { queryChannelInsertResults.SyncExecute(); channelCounter = 0; for (var idx = 0; idx < ChannelActions.Length; idx++) { var actions = ChannelActions[idx]; if (ShouldPerformVoltageInsertionCheck(actions)) { results[idx].MeasuredGain = queryChannelInsertResults.ActualGain[channelCounter]; results[idx].TargetGain = queryChannelInsertResults.ExpectedGain[channelCounter]; channelCounter++; } } } else { channelCounter = 0; ClearVoltageInsertionResults(ChannelActions, results); } } catch (Exception ex) { APILogger.Log("Failed to perform voltageinsertion check", ex); // If the functionality doesn't exist, just make sure the results are set to null // and the app will do the right thing. // ClearVoltageInsertionResults(ChannelActions, results); } } /// /// clears the measure and target gain information for any diagnostic results which should perform voltage insertion checks /// http://manuscript.dts.local/f/cases/31824/Don-t-run-voltage-insertion-when-the-input-range-is-10mV /// /// /// protected void ClearVoltageInsertionResults(IDiagnosticActions[] ChannelActions, IDiagnosticResult[] results) { for (var idx = 0; idx < ChannelActions.Length; idx++) { var actions = ChannelActions[idx]; if (ShouldPerformVoltageInsertionCheck(actions)) { results[idx].MeasuredGain = null; results[idx].TargetGain = null; } } } public override bool SupportsMultipleEvents() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents); } public override bool SupportsAutoArm() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm); } public override bool SupportsMultipleConfigurations() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleConfigurations); } public override bool SupportsLevelTrigger() { return true; } protected override bool SupportsIEPECalSignal { get { switch (DASInfo.Modules[0].TypeOfModule) { case DFConstantsAndEnums.ModuleType.ProDIM: case DFConstantsAndEnums.ModuleType.SLICEPro_TOM: return false; default: return true; } } } public override bool CheckAAF(float rate) { var configRate = 0F; try { configRate = ConfigData.Modules[0].AAFilterRateHz; } catch (Exception) { } if (configRate < 100000 && rate < 100000) { return true; } else if (configRate == 200000 && rate == 200000) { return true; } else if (configRate >= 100000 && configRate <= 199000 && rate >= 100000 && rate <= 199000) { return true; } return false; } //eventually this should be read from an attribute public int PageSize => 16384; /// /// left over data are the samples broken between requests /// since SLICE2 sends data by blocks, a complete sample for a list of channels could (and will) get broken /// over multiple calls to QueryEventData. This temporarily holds the data between calls /// the lock is not actually necessary, but is provided for sanity sake /// protected static object LeftOverDataLock = new object(); protected ushort[] _leftOverData = null; /// /// holds onto sample data between calls /// original implementation was dynamic, but since we are /// guaranteed by the block size that will never need to push twice in a row /// it's quicker and easier just to hold an array between calls /// /// public void PushLeftOverData(ushort[] data) { lock (LeftOverDataLock) { _leftOverData = data; } } /// /// retrieves held over data between calls to QueryEventData /// /// public ushort[] PopLeftOverData() { lock (LeftOverDataLock) { if (null == _leftOverData) { return new ushort[0]; } else { return _leftOverData; } } } protected virtual bool SupportsDiagnosticsMode => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticsMode) && !IsDIM() && !IsTOM(); /// /// created for 11282, this just clears the diagnostics mode for das /// this should be part of a generic services /// protected override void TurnOffDiagnosticsMode() { if (SupportsDiagnosticsMode) { try { var scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this); // This command will clear the voltage insertion on any capable device. scd.DeviceID = 0; scd.StackChannelList = new byte[0]; // Empty Set scd.SyncExecute(); // Send empty set to device } catch (Exception ex) { APILogger.Log("Failed to unset VoltageInsertion flag", ex); } } } /// /// for SLICE2 we override WhatToDownload and transparently handle things our own way from the outside /// private WhatToDownloadSlice2 _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 WhatToDownloadSlice2(value, numChannels, PageSize); } } /// /// QueryEventData also is customized for SLICE2, it needs to perform SLICE2 specific data marshalling /// /// protected override QueryEventDataBase GetQueryEventData() { return new QueryEventData_SLICE2(this, AbstractCommandBase.Default_IO_Timeout); } /// /// SLICE2 currently doesn't have a way of storing a GUID /// we temporarily fake it by storing it in a file /// SLICE2 really should hold onto the GUID ... GUID is more unique than test id ... /// and this is already functionality in SLICE1 and soft baked into the TDAS implementation /// /// protected override void SetEventGuid(Guid guid) { try { File.WriteAllText($"{SerialNumber}_GUID", guid.ToString()); APILogger.Log("SetEventGuid (", SerialNumber, "): ", guid.ToString()); } catch (Exception ex) { APILogger.Log("Failed to set guid", ex); } } /// /// overrided as SLICE2 will not hold onto GUID right now /// /// /// protected override Guid GetEventGuid(int eventNum) { try { if (File.Exists(string.Format("{0}_GUID", SerialNumber))) { var s = File.ReadAllText(string.Format("{0}_GUID", SerialNumber)); APILogger.Log("GetEventGuid: ", SerialNumber, ": ", s); return new Guid(s); } } catch (Exception ex) { APILogger.Log("Failed to get event guid", SerialNumber, ex); } return Guid.Empty; } /// /// hardcoded constants right now ... maybe these belong in attributes in the firmware! /// protected override uint MaxAAFilterRateHz => SLICE2.MaxAAFilterRateHz; protected override uint MaxSampleRateHz => 1000000; public override long MaxMemory() { if (null == DASInfo || 0 == DASInfo.NumberOfBytesPerSampleClock || null == DASInfo.MaxEventStorageSpaceInBytes) { return 0; } return (long)(DASInfo.MaxEventStorageSpaceInBytes / DASInfo.NumberOfBytesPerSampleClock); } /// /// this is a mapping of module count to max sample rate for an SPD /// http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD /// private static readonly Dictionary _SPDModulesToMaxRate = new Dictionary() { {1, 1500000 }, {2, 1200000 }, {3, 1000000 }, {4, 800000 }, {5, 700000 }, {6, 600000 } }; /// /// the default max sample rate for an SPD if we don't have a matching module count /// private const uint DEFAULT_MAX_SPS_SPD = 600000; /// /// returns the max sample rate for a SLICE DIM /// http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD /// private uint MaxSampleRateDIM() { try { if (_SPDModulesToMaxRate.ContainsKey(DASInfo.Modules.Length)) { return _SPDModulesToMaxRate[DASInfo.Modules.Length]; } } catch( Exception ex ) { APILogger.Log(ex); } return DEFAULT_MAX_SPS_SPD; } /// /// calculates the max sample rate /// these are not exact max sample rates, but convenient close enough limits /// drop 100k every module after 3 (starting at 800k) /// /// public override uint MaxSampleRate(int numberOfConfiguredChannels) { try { if (IsDIM()) { return MaxSampleRateDIM(); } } catch( Exception ex) { APILogger.Log(ex); } if (DASInfo.Modules.Length <= 3) { return MaxSampleRateHz; } else { return Convert.ToUInt32((12 - DASInfo.Modules.Length) * 100000); } } 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()) { _minSampleRate = 50; 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) { _minSampleRate = 50; } } return _minSampleRate; } public override uint MaxAAFilterRate() { return MaxAAFilterRateHz; } /// /// returns a GetRealtimeSamples class object appropriate for the DAS /// this is partially necessary as SLICE2 uses a different channel ordering than SLICE1 /// /// /// 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 GetRealtimeSamplesSLICE2(iCommunication); } /// /// indicates whether the DAS supports streaming /// 10572 implement SW side for single command streaming realtime /// (note protocol >=152 required for streaming) /// public override bool SupportsIndividualChannelRealtimeStreaming => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream); public class GetRealtimeSamplesSLICE2 : GetRealtimeSamples { public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock) : base(sock, 2000) { } public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec) : base(sock, TimeoutMillisec) { } protected override CommandReceiveAction WholePackage() { try { if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError) { // Figure out the number of samples returned var samples_returned = (response.ParameterLength - 8) / (_channels * 2); _samplesReturned = samples_returned; var data_bytes_returned = samples_returned * _channels * 2; _data = new List(_channels); // Grab the sample number if (response.ParameterLength > 0) { response.GetParameter(0, out _sampleNumber); } // Create the data arrays by channel if (samples_returned > 0) { for (var i = 0; i < _channels; i++) { _data.Add(new short[samples_returned]); } } // Grab the data var parameter = 8; for (var sample = 0; sample < samples_returned; sample++) { for (var channel = 0; channel < _channels; channel++) { response.GetParameter(parameter, out ushort val); //changed from unsigned to signed _data[channel][sample] = (short)(((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF))) + 0x8000); try { if (recorder is IDASCommunication idas && null != idas.DASInfo && null != idas.DASInfo.Modules && idas.DASInfo.Modules.Any()) { if (idas.DASInfo.Modules[0].TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM) { var ch = idas.ConfigData.Modules[ idas.DASInfo.MapDASChannelNumber2ModuleArrayIndex(channel)].Channels[ idas.DASInfo.MapDASChannelNumber2ModuleChannelNumber(channel)]; if (null == ch) { continue; } } } } catch (Exception ex) { APILogger.Log(ex); } parameter += 2; } } return CommandReceiveAction.StopReceiving; } } catch { _samplesReturned = 0; } return CommandReceiveAction.StopReceiving; } } private readonly Dictionary SLICE2_MinimumProtocols = new Dictionary(); public override void InitMinProto() { try { _baseType = QueryBaseType(); } catch (Exception) { } // SLICE 2.0 Protocol Limitations if (_baseType != (int)SLICERecorder.SLICEPRO_BaseType.SLICEPRO_DIM) { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC] = SLICE2.MIN_PROTOCOL_VER; } SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryMSP430Firmware] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents] = SLICE2.MULTIPLE_EVENTS; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm] = SLICE2.MULTIPLE_EVENTS; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashCardInfo] = SLICE2.MIN_PROTOCOL_VER; if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE || _baseType == SLICE2.SLICE1_5_BASETYPE) { //empty, just here for readability } else { //http://manuscript.dts.local/f/cases/18457/Add-Protocol-155-support-for-voltage-insertion-on-SLICE-PRO SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageInsertion] = 155; } SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetDefaultMIF] = SLICE2.MIN_PROTOCOL_VER_GEN3; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackFirmwareUpdate] = SLICE2.STACK_FIRMWARE_UPDATE; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FileData] = SLICE2.FILE_DATA; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors] = SLICE2.STACK_SENSORS; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime] = SLICE2.MIN_PROTOCOL_VER_GEN3; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PhysicalStartAddress] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.TestCommunication] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode] = SLICE2.MIN_PROTOCOL_VER; if (_baseType == SLICE2.SLICE1_5_BASETYPE || _baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE) { //empty, just here for readability } else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelTypeConfiguration] = SLICE2.MULTIPLE_EVENTS; } SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SLICE2_OneWireID] = SLICE2.SLICE2_ONE_WIRE_ID; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision] = SLICE2.MIN_PROTOCOL_VER_GEN3; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareConfiguration] = SLICE2.MULTIPLE_EVENTS; if (_baseType == SLICE2.SLICE1_5_BASETYPE || _baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE) { //empty, just here for readability } else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels] = SLICE2.STACK_SENSORS; } SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts] = SLICE2.EVENT_ARM_ATTEMPTS; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageSysAttributes] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticTwoVoltExcitation] = SLICE2.DIAGNOSTIC_TWO_VOLT_EXCITATION; if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE || _baseType == SLICE2.SLICE1_5_BASETYPE) { //empty, just here for readability } else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ExcitationLevel] = SLICE2.FILE_DATA; } if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE) { //empty, just here for readability } else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger] = SLICE2.MIN_PROTOCOL_VER; } SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel] = SLICE2.FILE_DATA; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticsMode] = SLICE2.FILE_DATA; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleSamplesRealtime] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ResetAttributeStore] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_TimeLeftInArm] = SLICE2.QUERY_ARM_AND_TRIGGER_STATUS_TIME_LEFT_IN_ARM; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffset] = SLICE2.MEASURE_INTERNAL_OFFSET; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffsetSwitches] = SLICE2.MIN_PROTOCOL_VER_GEN3; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC] = SLICE2.MIN_PROTOCOL_VER_GEN3; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond] = SLICE2.START_REC_DELAY_IN_SECONDS; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream] = SLICE2.START_REALTIME_STREAM; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport] = SLICE2.HALF_BRIDGE_SIG_PLUS_SUPPORT; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable] = SLICE2.MIN_PROTOCOL_VER; SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.GenerateEvent] = 156; MinimumProtocols = SLICE2_MinimumProtocols; } private enum SquibFireModes { DISABLED = 0, CapDischarge = 1, ConstantCurrent = 2, CapDischargeInternal = 3, ConstantCurrentInternal = 4 } private enum SquibMeasurementTypes { InitiationSignal = 0x00, SquibVoltage = 0x01 } private enum TOMDigitalOutputModes { Disabled = 0, LHT = 1, HLT = 2, CCNC = 3, CCNO = 4 } private readonly float MaxSquibDurationMS = 40; //Kids need limits, so do unlimited-duration squibs protected void SetArmDisableShortCheck() { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent)) { var ignoreShortedStart = Convert.ToByte(IgnoreShortedStart); var ssa = new SetSystemAttributeSLICE2(this, AbstractCommandBase.Default_IO_Timeout); ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.ArmDisableStartShortCheck, ignoreShortedStart, true); ssa.SyncExecute(); } if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent)) { var ignoreShortedTrigger = Convert.ToByte(IgnoreShortedTrigger); var ssa = new SetSystemAttributeSLICE2(this, AbstractCommandBase.Default_IO_Timeout); ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.ArmDisableTriggerShortCheck, ignoreShortedTrigger, true); ssa.SyncExecute(); } } private enum ModificationSettings { None = 0, UseFullGainList = 1 << 0, Excitation7_9V = 1 << 1 }; /// /// the TOM is so different it didn't make sense to reuse the old AsyncConfigure, so I created one /// specifically for TOM /// /// private void AsyncConfigureTOM(object configAsyncInfo) { var info = configAsyncInfo as SliceConfigServiceAsyncInfo; var progressValue = 0; var bReleased = true; PresetSampleRate(); SetVoltageRequirements(); SetPolarity(); try { Lock(); bReleased = false; // report progress progressValue = 5; info.Progress(progressValue); var config = GetConfigAttributes(this);// new ConfigAttributes(this); // only set attributes to default if we haven't run diagnostics yet if (!DiagnosticsHasBeenRun) { config.PurgeStaleData(this); } // store some attributes if (ConfigData.Modules.Length > 0) { config.SampleRate = ConfigData.Modules[0].SampleRateHz; //System.Diagnostics.Trace.WriteLine("Sample Rate: " + config.SampleRate); var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz; actualSampleRate = config.ActualSampleRate; config.AAFilter = ConfigData.Modules[0].AAFilterRateHz; config.TestID = ConfigData.TestID.TrimEnd(new char[] { '\0' }); config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0'); config.TestDescription = ConfigData.Description; config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate); config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate); SetArmMode(ConfigData.Modules[0].RecordingMode); // get scale factors var scaleFactors = config.GetScaleFactors(); for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIdx].Channels.Length; channelIdx++) { if (ConfigData.Modules[moduleIdx].Channels[channelIdx] is OutputSquibChannel) { //var analog = ConfigData.Modules[moduleIdx].Channels[channelIdx] as AnalogInputDASChannel; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); //analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber]; var squib = ConfigData.Modules[moduleIdx].Channels[channelIdx] as OutputSquibChannel; squib.ScaleFactorMv = scaleFactors[dasChannelNumber]; } } progressValue++; info.Progress(Math.Min(19, progressValue)); } } SetArmDisableShortCheck(); foreach (var module in ConfigData.Modules) { for (var i = 0; i < 8; i += 2) { var voltageSquib = module.Channels[i] as OutputSquibChannel; var currentSquib = module.Channels[i + 1] as OutputSquibChannel; System.Diagnostics.Trace.Assert((null != currentSquib && null != voltageSquib), "we are missing a voltage or a squib channel"); var sscc = new SetSquibChannelConfiguraion(this); sscc.SetSquibChannelID(Convert.ToByte(i / 2));//we want 0-3 for channel id if (currentSquib.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) { switch (voltageSquib.MeasurementType) { default: case SquibMeasurementType.VOLTAGE: sscc.SetSquibRecordMode(Convert.ToByte(SquibMeasurementTypes.SquibVoltage)); break; case SquibMeasurementType.INIT_SIGNAL: sscc.SetSquibRecordMode(Convert.ToByte(SquibMeasurementTypes.InitiationSignal)); break; } switch (currentSquib.FireMode) { case SquibFireMode.CAP: { if (info.DummyConfig) { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.CapDischargeInternal)); } else { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.CapDischarge)); } } break; case SquibFireMode.CONSTANT: { if (info.DummyConfig) { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.ConstantCurrentInternal)); } else { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.ConstantCurrent)); } } break; //default: sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.DISABLED)); break; } if (currentSquib.LimitDuration) { sscc.SetSquibDurationMs(Convert.ToSingle(currentSquib.DurationMS)); } else { sscc.SetSquibDurationMs(MaxSquibDurationMS); } sscc.SetSquibDelayMs(Convert.ToSingle(currentSquib.DelayMS)); sscc.SetSquibResMax(Convert.ToSingle(currentSquib.SquibToleranceHigh)); sscc.SetSquibResMin(Convert.ToSingle(currentSquib.SquibToleranceLow)); sscc.SetSquibOutputCurrentAmp(Convert.ToSingle(currentSquib.SquibOutputCurrent)); } else { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.DISABLED)); } sscc.SyncExecute(); } for (var i = 8; i < module.Channels.Length; i++) { var sscc = new SetDigitalChannelConfiguration(this); sscc.SetDigitalChannelID(Convert.ToByte(i - 8)); var dout = module.Channels[i] as OutputTOMDigitalChannel; System.Diagnostics.Trace.Assert(null != dout, "Missing a digital output channel"); if ((dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) || (dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm)) { if (!info.ConfigureDigitalOutput && info.DummyConfig) { sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled)); } else { switch (dout.OutputMode) { case DigitalOutputModes.CCNC: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.CCNC)); break; case DigitalOutputModes.CCNO: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.CCNO)); break; case DigitalOutputModes.FVHL: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.HLT)); break; case DigitalOutputModes.FVLH: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.LHT)); break; case DigitalOutputModes.NONE: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled)); break; } } } else { sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled)); } if ((dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) || ((dout.DelayMS + dout.DurationMS) <= (ConfigData.Modules[0].PostTriggerSeconds * 1000))) { //As long as the delay + duration is less than the post-trigger seconds (1 if CB, 10 if Recorder), use it even if it's just a trigger check sscc.SetDigitalDelayMs(Convert.ToSingle(dout.DelayMS)); if (dout.LimitDuration) { sscc.SetDigitalDurationMs(Convert.ToSingle(dout.DurationMS)); } else { sscc.SetDigitalDurationMs(0); } } else { // Since we're not going to let trigger check testing go longer than post-trigger seconds, just fire immediately sscc.SetDigitalDelayMs(0); sscc.SetDigitalDurationMs(Convert.ToSingle(10)); } sscc.SyncExecute(); } } progressValue = 30; info.Progress(progressValue); // now store the data var configStr = ConfigurationData.Serialize((ConfigurationData)ConfigData); config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic); ConfigurationData.SetConfiguration(this, configStr, info.EventConfig ? (int)ConfigAttributes.FileStore.Event : (int)ConfigAttributes.FileStore.Diagnostic); //dummy read to flush buffer, per Loc try { var qsa = new QuerySystemAttribute(this); qsa.Key = AttributeTypes.SystemAttributes.SerialNumber; qsa.SyncExecute(); } catch (Exception) { } progressValue = 60; info.Progress(progressValue); try { var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this); var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr)); if (!retrieved.Equals(utf8Version)) { APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr); } } catch (Exception ex) { APILogger.Log("failed to read config", ex); } //this only needs to be done in datapro, which doesn't set position 0 //we'll have to clean up the code a bit to use it if we want for sliceware. //config.StoreXMLConfig(configStr, !info.EventConfig); config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Diagnostic : ConfigAttributes.FileStore.Event); progressValue = 70; info.Progress(progressValue); //dummy read to flush buffer, per Loc try { var qsa = new QuerySystemAttribute(this); qsa.Key = AttributeTypes.SystemAttributes.SerialNumber; qsa.SyncExecute(); } catch (Exception) { } progressValue = 80; info.Progress(progressValue); try { var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this); var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr)); if (!retrieved.Equals(utf8Version)) { APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr); } } catch (Exception ex) { APILogger.Log("failed to read config", ex); } ConfigureHasBeenRun = true; if (info.DiscardDiagnostics) { DiagnosticsHasBeenRun = false; } // report success' if (!bReleased) { bReleased = true; Release(); } progressValue = 90; info.Progress(progressValue); info.Success(); } 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(); } 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); } protected bool ShouldQueryFeatureConfig() { return !(this is EthernetSlice6 || this is EthernetSlice6Air || this is EthernetSlice6AirBridge || this is EthernetSlice6DB || this is EthernetSlice6DB3 || this is WinUSBTsrAir || this is EthernetTsrAir || this is EthernetSlice6AirThermocoupler); } protected void ConfigureDiagChannels(byte[] diagnosticChannels) { if (SupportsDiagnosticsMode) { var scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this, AbstractCommandBase.Default_IO_Timeout); if (!diagnosticChannels.Any()) { // This command will clear the voltage insertion on any capable device. scd.DeviceID = 0; scd.StackChannelList = new byte[0]; // Empty Set scd.SyncExecute(); // Send empty set to device } else { // The following command will set the voltage insertion if there are diagnosticChannels scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this, AbstractCommandBase.Default_IO_Timeout); scd.DeviceID = 0; scd.StackChannelList = diagnosticChannels; // List of diagnostic channels scd.SyncExecute(); } } } protected void PresetSampleRate() { //this is a bit of a hack, but lets make sure sample rate gets set first try { if (ConfigData.Modules.Length > 0) { var config = GetConfigAttributes(this); config.SampleRate = ConfigData.Modules[0].SampleRateHz; var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz; actualSampleRate = config.ActualSampleRate; config.AAFilter = ConfigData.Modules[0].AAFilterRateHz; } } catch (Exception ex) { APILogger.Log("failed to pre-set sample rate", ex); } } private void ReconfigureIfNeeded(ref bool bModified) { if (_hardwareRevision == GEN3_REV_B) { if (!ShouldQueryFeatureConfig()) { _featureConfigValue = 1; } else { var queryFeatureConfig = new QuerySystemAttribute_Slice2Bridge(this); queryFeatureConfig.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FeatureConfig; queryFeatureConfig.DeviceID = 1; queryFeatureConfig.SyncExecute(); bModified = ((Convert.ToInt32(queryFeatureConfig.Value)) & (int)ModificationSettings.UseFullGainList) == (int)ModificationSettings.UseFullGainList; } } } protected virtual bool AdjustInputRange(AnalogInputDASChannel analog) { //if our range is not at an edge of the gains table, then //the requested range may need to be manipulated for //10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs switch (analog.TypeOfBridge) { case SensorConstants.BridgeType.FullBridge: case SensorConstants.BridgeType.HalfBridge: case SensorConstants.BridgeType.HalfBridge_SigPlus: case SensorConstants.BridgeType.QuarterBridge: return true; } return false; } protected virtual bool RequiresNon0QualificationSamples { get => false; } protected virtual void CommonRangeWork(AnalogInputDASChannel analog, bool bModified, float[] rangeArray, byte dasChannelNumber, double MvPerEU, IDASModule module ) { if (null == analog) { return; } var requestedRange = GetRequestedRange(analog, MvPerEU); APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, requestedRange={requestedRange}"); var nominalRanges = GetNominalRanges(analog.TypeOfBridge, bModified); //if our range is at one of the edges, there's nothing we can do, just take the edge if (requestedRange > nominalRanges[0]) { requestedRange = nominalRanges[0]; } else if (requestedRange < nominalRanges[nominalRanges.Length - 1]) { requestedRange = nominalRanges[nominalRanges.Length - 1]; } else { if (AdjustInputRange(analog)) { APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, AdjustingInputRange"); requestedRange = AdjustInputRangeIfNeeded(requestedRange, bModified); } } APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, AssignedRange={requestedRange}"); rangeArray[dasChannelNumber] = Convert.ToSingle(requestedRange); } protected void CommonConfigureWork(List diagnosticChannels, int[] qualificationSamples, ref bool bReleased, SliceConfigServiceAsyncInfo info, byte[] bridgeModeArray, bool[] IsACCoupledArray, ushort[] BridgeResistanceArray, ref bool bModified, float[] rangeArray, bool[] enableUpperLevelTriggerThreshold, float[] upperLevelTriggerThreshold, bool[] enableLowerLevelTriggerThreshold, float[] lowerLevelTriggerThreshold, bool[] bridgeACCouplingArray) { APILogger.DebugLog($"CommonConfigureWork - {SerialNumber} Start"); for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { APILogger.DebugLog($"CommonConfigureWork - module={moduleIdx}"); var module = ConfigData.Modules[moduleIdx]; // configure the range for this bridge for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++) { APILogger.DebugLog($"CommonConfigureWork - module={moduleIdx}, channel={channelIdx}"); var channel = module.Channels[channelIdx]; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); //diagnosticChannels[dasChannelNumber] = channel.DiagnosticsMode ? (byte)1 : (byte)0; if (channel.DiagnosticsMode) { diagnosticChannels.Add(dasChannelNumber); } if (RequiresNon0QualificationSamples) { if (0 == channel.QualificationSamples) { channel.QualificationSamples = 5; } } qualificationSamples[dasChannelNumber] = channel.QualificationSamples; if (channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Disabled && (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal || channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm)) { if (!(channel is AnalogInputDASChannel)) { if (!bReleased) { bReleased = true; Release(); } // "Configure: Slice bridge modules must only contain analog input channels" info.Error("Slice bridge modules must only contain analog input channels"); return; } var analog = channel as AnalogInputDASChannel; switch (analog.TypeOfBridge) { case SensorConstants.BridgeType.FullBridge: bridgeModeArray[dasChannelNumber] = 0; bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled; break; case SensorConstants.BridgeType.HalfBridge: bridgeModeArray[dasChannelNumber] = 1; bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled; break; case SensorConstants.BridgeType.IEPE: bridgeModeArray[dasChannelNumber] = 0; bridgeACCouplingArray[dasChannelNumber] = false; break; case SensorConstants.BridgeType.QuarterBridge: bridgeModeArray[dasChannelNumber] = 2; bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled; break; case SensorConstants.BridgeType.DigitalInput: bridgeModeArray[dasChannelNumber] = 0; analog.DigitalInputChannel = true; bridgeACCouplingArray[dasChannelNumber] = false; break; case SensorConstants.BridgeType.HalfBridge_SigPlus: bridgeModeArray[dasChannelNumber] = 3; bridgeACCouplingArray[dasChannelNumber] = false; break; case SensorConstants.BridgeType.RTC: bridgeModeArray[dasChannelNumber] = 8; bridgeACCouplingArray[dasChannelNumber] = false; break; default: bridgeModeArray[dasChannelNumber] = 0; bridgeACCouplingArray[dasChannelNumber] = false; break; } if (analog.IEPEChannel) { IsACCoupledArray[dasChannelNumber] = analog.CouplingMode == SensorConstants.CouplingModes.AC; } else { IsACCoupledArray[dasChannelNumber] = false; } if (SensorConstants.IsTSRAirLowGChannel(module.SerialNumber())) { if (analog.ACCouplingModeEnabled && analog.CouplingMode == SensorConstants.CouplingModes.AC) { IsACCoupledArray[dasChannelNumber] = true; } } switch (analog.TypeOfBridge) { case SensorConstants.BridgeType.HalfBridge: case SensorConstants.BridgeType.HalfBridge_SigPlus: BridgeResistanceArray[dasChannelNumber] = SLICE_HALFBRIDGE_RESISTANCE; break; case SensorConstants.BridgeType.RTC: BridgeResistanceArray[dasChannelNumber] = 0; break; default: BridgeResistanceArray[dasChannelNumber] = (ushort)analog.BridgeResistanceOhms; break; } APILogger.DebugLog($"CommonConfigureWork {analog?.HardwareChannelName ?? "N/A"} sensitivity={analog?.SensitivityMilliVoltsPerEU}"); if (analog.SensitivityMilliVoltsPerEU != 0) { var MvPerEu = GetMvPerEUNormalized(analog); CommonRangeWork(analog, bModified, rangeArray, dasChannelNumber, MvPerEu, module); // // push level trigger settings out to hardware // //if (enableLowerLevelTriggerThreshold[dasChannelNumber] = (null != analog.TriggerBelowThresholdEu)) if (null != analog.TriggerBelowThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) && !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0)) { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { upperLevelTriggerThreshold[dasChannelNumber] = -upperLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw ex; } } else { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw ex; } } } else { if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) && !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0) ) { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } else { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } } if (null != analog.TriggerAboveThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) && !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0)) { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { lowerLevelTriggerThreshold[dasChannelNumber] = -lowerLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw ex; } } else { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw ex; } } } else { if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) && !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0)) { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } else { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } } } else { APILogger.DebugLog($"CommonConfigureWork {analog?.HardwareChannelName ?? "N/A"} sensitivity 0, so range is 0"); rangeArray[dasChannelNumber] = 0; } } else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.StreamOut) { try { var streamOut = channel as StreamOutputDASChannel; streamOut.UDPProfileName = info.StreamProfiles[this].ToString(); streamOut.UDPTimeChannelId = info.TimeChannelIds[this]; streamOut.UDPDataChannelId = info.DataChannelIds[this]; streamOut.IRIGTimeDataPacketIntervalMs = info.IrigTDPIntervals[this]; streamOut.UDPAddress = info.Addresses[this]; streamOut.UDPTmNSConfig = info.TmNSConfigs[this]; streamOut.TMATSIntervalMs = info.TMATSIntervalMs[this]; } catch( Exception ex) { APILogger.Log("Failed to set stream output", ex); } } else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.UART) { try { var uart = channel as UARTInputDASChannel; if (info.BaudRates.ContainsKey(this)) { uart.BaudRate = info.BaudRates[this]; uart.DataBits = info.DataBits[this]; uart.StopBits = info.StopBits[this]; uart.Parity = info.Parities[this]; uart.FlowControl = info.FlowControls[this]; uart.DataFormat = info.DataFormats[this]; } } catch (Exception ex) { APILogger.Log("Failed to set UART", ex); } } else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.StreamIn) { var streamIn = channel as StreamInputDASChannel; streamIn.UDPAddress = ConfigData.UDPReceiveAddress; } else { rangeArray[dasChannelNumber] = 0; bridgeModeArray[dasChannelNumber] = 0; BridgeResistanceArray[dasChannelNumber] = 0; bridgeACCouplingArray[dasChannelNumber] = false; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; lowerLevelTriggerThreshold[dasChannelNumber] = 0; upperLevelTriggerThreshold[dasChannelNumber] = 0; } } } } private bool IsStreamingOnlyMode() { if (null == ConfigData) { return false; } if (null == ConfigData.Modules) { return false; } if (0 >= ConfigData.Modules.Length) { return false; } return ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.S6A_DeviceStreamingOnly || ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Streaming; } protected virtual void SetStreamingRealtimeSampleRate(SliceConfigServiceAsyncInfo info) { //FB14613: When arming for stream-on-reboot, set RealtimeSampleRate to the DAS' test sample rate try { //we don't need to set the realtime sample rate for record and stream recording modes, they won't use it if (IsStreamingOnlyMode() && IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate)) { var sampleRate = ConfigData.Modules[0].SampleRateHz; var ssr = new SetRealtimeSampleRate(this); ssr.SetValue(sampleRate); ssr.SyncExecute(); //FB14966 - Can't get DataPRO to program RealtimeAAFilterFrequencyHz when running a streaming slice 6 air test //Pass through Properties.SLICETurnOffAAFRealtime and use similar logic to TestTemplate.GetRealtimeAAFForHardware for the frequency var aafRate = info.TurnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(this) ? MaxAAFilterRateHz : ConfigData.Modules[0].AAFilterRateHz; var saaf = new SetArmAttribute(this); saaf.SetValue(AttributeTypes.ArmAndEventAttributes.RealtimeAAFilterFrequencyHz, aafRate, true); saaf.SyncExecute(); } } catch (Exception ex) { APILogger.Log("Problem setting realtime sample rate", ex); } } protected virtual void StoreConfigAttributes(SliceConfigServiceAsyncInfo info, float[] rangeArray, ref bool bReleased, ref int progressValue, byte[] bridgeModeArray, bool[] IsACCoupledArray, ushort[] BridgeResistanceArray, bool[] enableLowerLevelTriggerThreshold, float[] lowerLevelTriggerThreshold, bool[] enableUpperLevelTriggerThreshold, float[] upperLevelTriggerThreshold, int[] qualificationSamples, long numChannels, out ConfigAttributes config, bool[] bridgeACCouplingArray, long numUartChannels, long numStreamingChannels) { config = GetConfigAttributes(this);// new ConfigAttributes(this); // only set attributes to default if we haven't run diagnostics yet if (!DiagnosticsHasBeenRun) { config.PurgeStaleData(this); } // store some attributes if (ConfigData.Modules.Length > 0) { config.SampleRate = ConfigData.Modules[0].SampleRateHz; var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz; actualSampleRate = config.ActualSampleRate; config.AAFilter = ConfigData.Modules[0].AAFilterRateHz; config.TestID = ConfigData.TestID.TrimEnd(new char[] { '\0' }); config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0'); config.TestDescription = ConfigData.Description; config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate); config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate); SetArmMode(ConfigData.Modules[0].RecordingMode); SetStreamingRealtimeSampleRate(info); try { 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; } } try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelBridgeACCouplerEnable)) { var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelBridgeACCouplerEnable, bridgeACCouplingArray, true); set.SyncExecute(); } } catch (Exception ex) { APILogger.Log($"Failed to set ACCouplingModeEnabled, {ex.Message}"); } config.ConfigureBridge(bridgeModeArray); 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); } // get scale factors var scaleFactors = config.GetScaleFactors(); 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} analog channels", 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; } if (DFConstantsAndEnums.ModuleType.StreamIn == ConfigData.Modules[moduleIdx].ModuleType()) { continue; } for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIdx].Channels.Length; channelIdx++) { var analog = (AnalogInputDASChannel)ConfigData.Modules[moduleIdx].Channels[channelIdx]; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber]; } progressValue++; info.Progress(Math.Min(19, progressValue)); } } } /// /// abstracted this method out from a series of duplicated and very long functions /// /// /// /// /// /// /// scale factors for channels, only used in TSR AIR for some reason /// ranges for channels, only used in TSR AIR for some reason /// measured offset, only used in TSR AIR for some reason protected void RemainingConfigWork(ref int progressValue, SliceConfigServiceAsyncInfo info, List diagnosticChannels, ConfigAttributes config, ref bool bReleased, float[] scaleFactors, float[] ranges, float[] measuredOffset) { ConfigureDiagChannels(diagnosticChannels.ToArray()); progressValue = 20; info.Progress(progressValue); if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ExcitationLevel)) // add const value for proto version { // Allocate list for base excitation values. var excitationValues = new List(); for (byte moduleIndex = 0; moduleIndex < ConfigData.Modules.Length; moduleIndex++) { for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIndex].Channels.Length; channelIdx++) { var analog = ConfigData.Modules[moduleIndex].Channels[channelIdx] as AnalogInputDASChannel; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIndex, channelIdx); switch (analog.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2: excitationValues.Add(2); break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: excitationValues.Add(10); break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: excitationValues.Add(5); break; default: excitationValues.Add(0); break; } } progressValue++; info.Progress(Math.Min(29, progressValue)); } var saa = new SetArmAttribute(this, AbstractCommandBase.Default_IO_Timeout); saa.DeviceID = 0; saa.SetValue(AttributeTypes.ArmAndEventAttributes.StackExcitationLevel, excitationValues.ToArray(), true); saa.SyncExecute(); } progressValue = 30; info.Progress(progressValue); // now store the data var configStr = ConfigurationData.Serialize((ConfigurationData)ConfigData); config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic); ConfigurationData.SetConfiguration(this, configStr, info.EventConfig ? (int)ConfigAttributes.FileStore.Event : (int)ConfigAttributes.FileStore.Diagnostic); //dummy read to flush buffer, per Loc try { var qsa = new QuerySystemAttribute(this); qsa.Key = AttributeTypes.SystemAttributes.SerialNumber; qsa.SyncExecute(); } catch (Exception) { } progressValue = 60; info.Progress(progressValue); try { var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this); var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr)); if (!retrieved.Equals(utf8Version)) { APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr); } } catch (Exception ex) { APILogger.Log("failed to read config", ex); } //this only needs to be done in datapro, which doesn't set position 0 //we'll have to clean up the code a bit to use it if we want for sliceware. config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Diagnostic : ConfigAttributes.FileStore.Event); progressValue = 70; info.Progress(progressValue); //dummy read to flush buffer, per Loc try { var qsa = new QuerySystemAttribute(this); qsa.Key = AttributeTypes.SystemAttributes.SerialNumber; qsa.SyncExecute(); } catch (Exception) { } progressValue = 80; info.Progress(progressValue); try { var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this); var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr)); if (!retrieved.Equals(utf8Version)) { APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr); } } catch (Exception ex) { APILogger.Log("failed to read config", ex); } ConfigureHasBeenRun = true; ConfigureStreaming(info, scaleFactors, ranges, measuredOffset); ConfigureHasBeenRun = true; if (info.DiscardDiagnostics) { DiagnosticsHasBeenRun = false; } // report success' if (!bReleased) { bReleased = true; Release(); } progressValue = 90; info.Progress(progressValue); info.Success(); } protected virtual void ConfigureStreaming(SliceConfigServiceAsyncInfo info, float[] scaleFactors, float[] ranges, float[] measuredOffset) { } /// /// we can probably simplify and take common items (slice2+slice1) out of this function, but for now /// it's mostly a copy of SLICE1.AsyncConfigure /// /// protected override void AsyncConfigure(object configAsyncInfo) { var info = configAsyncInfo as SliceConfigServiceAsyncInfo; SetUDPAlignOnPPS(); IncrementNumberOfTimesWritten(); if (IsTOM()) { AsyncConfigureTOM(configAsyncInfo); } else { //keeps track of whether the PC file was written or not var bFileWritten = false; try { var progressValue = 0; var bReleased = true; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels)) { ReconfigureAccordingToConfig(); } PresetSampleRate(); SetVoltageRequirements(); SetPolarity(); SetArmDisableShortCheck(); try { Lock(); var bModified = false; ReconfigureIfNeeded(ref bModified); bReleased = false; // loop thru the modules (slices) and configure the channels var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels); var rangeArray = new float[numChannels]; //var IsHalfBridgeArray = new bool[numChannels]; var bridgeModeArray = new byte[numChannels]; var BridgeResistanceArray = new UInt16[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 bridgeACCouplingArray = new bool[numChannels]; CommonConfigureWork(diagnosticChannels, qualificationSamples, ref bReleased, info, bridgeModeArray, IsACCoupledArray, BridgeResistanceArray, ref bModified, rangeArray, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold, enableLowerLevelTriggerThreshold, lowerLevelTriggerThreshold, bridgeACCouplingArray); if (bReleased) { return; } // report progress progressValue = 5; info.Progress(progressValue); StoreConfigAttributes(info, rangeArray, ref bReleased, ref progressValue, bridgeModeArray, IsACCoupledArray, BridgeResistanceArray, enableLowerLevelTriggerThreshold, lowerLevelTriggerThreshold, enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold, qualificationSamples, numChannels, out var config, bridgeACCouplingArray, 0, 0); if (bReleased) { return; } RemainingConfigWork(ref progressValue, info, diagnosticChannels, config, ref bReleased, null, null, null); bFileWritten = true; } catch (CanceledException) { if (!bReleased) { bReleased = true; Release(); } info.Cancel(); } catch (Exception ex) { if (!bReleased) { bReleased = true; Release(); } APILogger.Log("exception during configuration", ex); info.Error(ex.Message, ex); } finally { if (!bReleased) { bReleased = true; Release(); } } info.Progress(100); info.Success(); } finally { //if the pc file wasn't written warn the user if (!bFileWritten) { APILogger.RaiseError(DTS.Common.Strings.Strings.FailedToWritePcConfigFile); } } } } /// /// adjusts the requested input range to avoid certain gains if needed /// we are doing this for /// 10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs /// whenever the hardware is not modified we manipulate the input range requested so we fall /// within acceptable gains /// /// /// /// protected virtual double AdjustInputRangeIfNeeded(double originalRange, bool bModified) { if (_hardwareRevision != GEN3_REV_B) return originalRange; var gains = Enum.GetValues(typeof(GainCodesGen3)).Cast().ToList(); gains.Sort(new GainSorter()); gains.Reverse(); var lastAcceptableValue = double.NaN; //assume gains are in order from 1 to 1600, so find the first gain which is too restrictive and bounce back from that //so for instance, if someone selected a range of 700mV, we would start with G1, pass, go to G2, pass, go to G4, fail, so we would use //G2. Also note, while we know the min/max theoretical value foreach gain, when we request a range from the firmware we don't use that value //we use somewhere inbetween (the FirmwareInputRangemV) to increase the chances we get the gain we want... //note there are gains greater than 1600, but they are currently explicitly disabled by issue 10080 foreach (var gain in gains) { if (GainDisabledAttribute.IsGainDisabled(gain)) { continue; } if (!bModified && !GainAvailableUnmodifiedAttribute.IsGainAvailableToUnmodified(gain)) { continue; } var maxValue = MaxInputRangeAttribute.GetMaxInputRangemV(gain); if (originalRange < maxValue) { lastAcceptableValue = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(gain); } else { break; } } return !double.IsNaN(lastAcceptableValue) ? lastAcceptableValue : originalRange; } protected override ConfigAttributes GetConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication com) { return new SLICE2ConfigAttributes(com); } /// /// SLICE2 config attributes, mostly inherits from SLICE.ConfigAttributes with some functionality removed /// protected class SLICE2ConfigAttributes : ConfigAttributes { /// /// I'm not aware of a purgeStaleData function in SLICE2 yet /// this may be unnecessary if the firmware is intelligent enough, and maybe this is legacy /// for now I just hollow it out /// /// public override void PurgeStaleData(IDASCommunication das) { } public SLICE2ConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication _com) : base(_com) { } #region CONFIG_XML private static readonly uint XML_HEADER_LENGTH = 8; private static readonly byte[] XML_STORE_MAGIC_BYTES = new byte[] { 0xAA, 0x55, 0xA5, 0x5A }; private readonly object _storeXMLConfigLock = new object(); /// /// SLICE2 stores the xml in flash using SetFileData. We compress the XML string, write the header, /// then write data 400 bytes at a time. For the header, we first write XML_STORE_MAGIC_BYTES followed /// by the length of the compressed XML in bytes. /// /// /// /// public override void StoreXMLConfig(string data, FileStore fileStore) { try { lock (_storeXMLConfigLock) { if (string.IsNullOrEmpty(data)) { return; } // since this might contain multi byte characters, we need to convert it to a byte array first // Compress Data var d = GetBytes(fileStore, data); var crc = new Utilities.Crc32(); var crcBefore = crc.Get(d); var ByteArrayData = GetCompressedBytes(d); var crcAfter = crc.Get(ByteArrayData); APILogger.Log($"StoreXMLConfig {com.SerialNumber} crc uncompressed: {crcBefore} crc compressed: {crcAfter}"); var fileid = (ushort)fileStore;//EventConfig ? Convert.ToUInt16(1) : Convert.ToUInt16(0); //Store Header - magic bytes and data length //Store Magic bytes var sfd = new SetFileData(com, 600000); sfd.StartByteCount = 0; sfd.FileID = fileid; sfd.Data = XML_STORE_MAGIC_BYTES; sfd.SyncExecute(); //Store Header - data length sfd.StartByteCount = (XML_HEADER_LENGTH / 2); sfd.FileID = fileid; sfd.Data = BitConverter.GetBytes((uint)ByteArrayData.Length); sfd.SyncExecute(); //Store Data for (uint i = 0; i < ByteArrayData.Length; i += (uint)sfd.MaximumFileStreamBytes) { long array_size = sfd.MaximumFileStreamBytes; if ((i + sfd.MaximumFileStreamBytes) > ByteArrayData.Length) { array_size = ByteArrayData.Length - i; } var dataToSend = new byte[array_size]; Array.Copy(ByteArrayData, i, dataToSend, 0, array_size); sfd.Data = dataToSend; sfd.FileID = fileid; sfd.StartByteCount = i + XML_HEADER_LENGTH; sfd.SyncExecute(); } var fileName = Path.Combine(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), Constants.DAS_CONFIGS), string.Format("{0}.xml", com.SerialNumber)); if ((FileStore.Diagnostic == fileStore) || (FileStore.Event == fileStore)) { StoreXmlConfigPC(data, Constants.DAS_CONFIGS, fileName); } } } catch (Exception ex) { //log the error and rethrow it, this preserve the existing behavior at the time I added the handler APILogger.Log($"SLICE2ConfigAttributes.StoreXMLConfig", ex); throw ex; } } private uint GetDeviceCRC(IDASCommunication das, bool eventConfig) { uint crc = 0; string s = string.Empty; if (das is EthernetSlice2 ethernet) { s = ethernet.GetUserAttribute(eventConfig ? AttributeTypes.SliceUserAttributes.EventConfigCRC : AttributeTypes.SliceUserAttributes.ConfigCRC); } else if (das is CDCUSBSlice usbSLICE) { s = usbSLICE.GetUserAttribute(eventConfig ? AttributeTypes.SliceUserAttributes.EventConfigCRC : AttributeTypes.SliceUserAttributes.ConfigCRC); } if (!string.IsNullOrWhiteSpace(s)) { if (uint.TryParse(s, out var i)) { return i; } } return crc; } /// /// We retrieve the xml from SLICE 2 flash using QueryFileData. We read the header, then read data 400 /// bytes at a time. For the header, we first read XML_STORE_MAGIC_BYTES followed by the length of the /// compressed XML in bytes. We then unzip the data and return the XML string. /// /// public override string RetrieveXMLConfig(FileStore fileStore, IDASCommunication das) { lock (_storeXMLConfigLock) { var WholeStr = ""; try { var allBytes = new List(); allBytes.Clear(); //Get Header - magic bytes and data length //Get Magic bytes var qfd = new QueryFileData(com, 600000); var fileid = (ushort)fileStore; qfd.FileID = fileid; qfd.StartByteCount = 0; qfd.EndByteCount = (XML_HEADER_LENGTH / 2) - 1; qfd.SyncExecute(); if (qfd.Data.Length < 4) { APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig <4 bytes returning empty"); return ""; } if (qfd.Data[0] == 0xFF && qfd.Data[1] == 0xFF && qfd.Data[2] == 0xFF && qfd.Data[3] == 0xFF) { APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig first 4 bytes wrong, returning empty string"); return ""; } if (BitConverter.ToUInt32(qfd.Data, 0) != BitConverter.ToUInt32(XML_STORE_MAGIC_BYTES, 0)) { APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig Magic bytes not found"); throw new InvalidDataException($"{com.SerialNumber} Magic bytes not found"); } //Get data length qfd.FileID = fileid; qfd.StartByteCount = (XML_HEADER_LENGTH / 2); //account for magic bytes qfd.EndByteCount = XML_HEADER_LENGTH - 1; qfd.SyncExecute(); var ByteArrayDataLength = BitConverter.ToUInt32(qfd.Data, 0); if (Math.Pow(2, 21) < ByteArrayDataLength) { APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig data length is greater than store size"); throw new InvalidDataException($"data length is greater than store size {com.SerialNumber}"); } //Get Data for (uint i = 0; i < ByteArrayDataLength; i += (uint)qfd.MaximumFileStreamBytes) { uint array_size = (uint)qfd.MaximumFileStreamBytes; if ((i + qfd.MaximumFileStreamBytes) > ByteArrayDataLength) { array_size = ByteArrayDataLength - i; } qfd.StartByteCount = (XML_HEADER_LENGTH) + i; qfd.EndByteCount = (XML_HEADER_LENGTH - 1) + (i + array_size); qfd.FileID = fileid; qfd.SyncExecute(); APILogger.Log($"{com.SerialNumber} Read {i} of {ByteArrayDataLength} CRC: {CalculateCrc32(new List(qfd.Data)).ToString()}"); allBytes.InsertRange((int)i, qfd.Data); } if (allBytes.Count < 1) { APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig all bytes count < 1"); // "Slice.RetrieveAttributes: Attributes are empty" throw new Exception("Attributes are empty"); } APILogger.Log($"{com.SerialNumber} read {allBytes.Count} bytes in total"); var crc = new Utilities.Crc32(); //DecideToFailOrNot(fileStore, ref allBytes); WholeStr = GetDecompressedString(allBytes.ToArray(), fileStore); } catch (Exception ex) { APILogger.Log($"Failed to retrieve xml config - {fileStore} on {com.SerialNumber}. Error: {ex}"); if (FileStore.Event == fileStore) { //event file store FIRST falls back to diagnostics //which will fall back onto pc if it fails WholeStr = RetrieveXMLConfig(FileStore.Diagnostic, das); } else if (FileStore.Diagnostic == fileStore) { //diagnostics falls back to pc WholeStr = RetrieveXmlConfigPC(); } } try { APILogger.ConfLog(com.SerialNumber, "XML Config\n", Common.Utils.Utils.PrettyPrint(WholeStr)); } catch (Exception) { } return WholeStr; } } private static UInt32 CalculateCrc32(List data) { if (data.Count % 2 > 0) { data.Add(0x0); } ushort crc = 0xFFFF; byte[] b = data.ToArray(); for (int i = 0; i < b.Length; i += 2) crc = Common.Utils.Utils.Math_DoCRC16Step(BitConverter.ToUInt16(b, i), crc); return crc; } #endregion /// /// Retrieve the XML string that was split and stored in the Arm attributes /// note that SLICE2 is apparently not going to store separate event configs, /// so we just grab the same one off the pc again /// [this is a bad idea as the event could be from earlier and the system config could have been /// changed after the use ran the test, and then reconfigured but wants to download the old test] /// /// The event number to get it from /// /// The combined XML string public override string RetrieveEventXMLConfig(int eventNum, QueryDownloadProgress progress, IDASCommunication das) { lock (_storeXMLConfigLock) { var s = string.Empty; try { s = RetrieveXMLConfig(FileStore.Event, das); } catch (Exception ex) { APILogger.Log("failed to retrieve event xml config for event ", eventNum, ex); } //fall back to whatever is on the live config if (string.IsNullOrEmpty(s)) { APILogger.Log("failed to retrieve event xml config (it was empty) for event ", eventNum); s = RetrieveXMLConfig(FileStore.Diagnostic, das); } return s; } } } protected override void AsyncReadyForArming(object asyncInfo) { //Until DataPRO supports StartRecDelayInSecond, set to 0 if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond)) { var ssaS6 = new SetSystemAttributeSLICE6(this); ssaS6.SetValue(AttributeTypes.SystemAttributesSLICE6.StartRecDelayInSecond, Convert.ToUInt16(0), true); ssaS6.SyncExecute(); } base.AsyncReadyForArming(asyncInfo); } protected override void AsyncArmNow(object asyncInfo) { base.AsyncArmNow(asyncInfo); } private bool IsDIM() { if (SerialNumber.StartsWith("SPD") || SerialNumber.StartsWith("SLD")) { return true; } else { return false; } } private DASModule MakeConfigModuleFromInfoModuleTOM(InfoResult.Module infoModule) { var configModule = new DASModule(infoModule.ModuleArrayIndex, this); var channels = new List(); for (var i = 0; i < 8; i++) { var c = new OutputSquibChannel(configModule, i); c.SupportedSquibFireModes = new SquibFireMode[] { SquibFireMode.CAP, SquibFireMode.CONSTANT, SquibFireMode.NONE }; if (0 != i % 2) { c.MeasurementType = SquibMeasurementType.CURRENT; } c.ModuleChannelNumber = i; channels.Add(c); } for (var i = 8; i < 16; i++) { var c = new OutputTOMDigitalChannel(configModule, i); channels.Add(c); } configModule.Channels = channels.ToArray(); return configModule; } protected override void PerformSensorIdCheck(ref Dictionary sensorIds) { try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors)) { var qsi = new QueryStackSensorIDs(this); qsi.SyncExecute(); foreach (var module in ConfigData.Modules) { foreach (var channel in module.Channels) { var id = ""; if (channel is OutputSquibChannel) { //we just expect 4 ids, the id of this channel is one of those 4 id = HexEncoding.ToString(qsi.IDs[channel.ModuleChannelNumber / 2]); } else if (channel is AnalogInputDASChannel) { var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); id = HexEncoding.ToString(qsi.IDs[dasChannelNumber]); } if (!string.IsNullOrWhiteSpace(id)) { sensorIds[channel.Number] = new[] { id }; } } } } else { PerformSensorIdCheck_SingleChannelMethod(ref sensorIds); } } catch (Exception ex) { APILogger.Log(ex); } finally { try { var prepForDC = new PrepareForDataCollection(this); prepForDC.SyncExecute(); } catch (Exception ex) { throw new Exception($"Unable to turn on excitation: {ex.Message}"); } } } protected override DASModule MakeConfigModuleFromInfoModule(InfoResult.Module infoModule) { if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEPro_TOM) { return MakeConfigModuleFromInfoModuleTOM(infoModule); } var configModule = new DASModule(infoModule.ModuleArrayIndex, this); configModule.Channels = new AnalogInputDASChannel[infoModule.NumberOfChannels]; for (var i = 0; i < infoModule.NumberOfChannels; i++) { var channel = new AnalogInputDASChannel(configModule, i); switch (infoModule.TypeOfModule) { case DFConstantsAndEnums.ModuleType.SliceBridge2High: case DFConstantsAndEnums.ModuleType.SliceBridge2Low: channel.IEPEChannel = false; channel.DigitalInputChannel = false; break; case DFConstantsAndEnums.ModuleType.SliceIEPE2High: case DFConstantsAndEnums.ModuleType.SliceIEPE2Low: channel.IEPEChannel = true; channel.DigitalInputChannel = false; break; case DFConstantsAndEnums.ModuleType.ProDIM: channel.TypeOfBridge = SensorConstants.BridgeType.DigitalInput; channel.IEPEChannel = false; channel.DigitalInputChannel = true; break; } QueryHardwareRevision(); //int hardwareRevision = QueryHardwareRevision(); var baseType = QueryBaseType(); if (_hardwareRevision == GEN3_REV_B && baseType == PROBaseType) { channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5, ExcitationVoltageOptions.ExcitationVoltageOption.Volt10, ExcitationVoltageOptions.ExcitationVoltageOption.Volt2}; } else if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticTwoVoltExcitation))//loc thinks this is where cal started for 2 volt { channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] {ExcitationVoltageOptions.ExcitationVoltageOption.Volt2, ExcitationVoltageOptions.ExcitationVoltageOption.Volt5}; } else { channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5 }; } if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM) { channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.DigitalInput }; } else if (_hardwareRevision == GEN3_REV_B && baseType == PROBaseType) { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport)) { channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.HalfBridge_SigPlus, SensorConstants.BridgeType.IEPE, SensorConstants.BridgeType.QuarterBridge }; } else { channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE, SensorConstants.BridgeType.QuarterBridge }; } } else if (infoModule.IsProgrammable) { channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE}; } else { if (channel.IEPEChannel) { channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.IEPE }; } else { channel.SupportedBridges = new SensorConstants.BridgeType[] {SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge}; } } configModule.Channels[i] = channel; } return configModule; } protected override bool CheckAutoDetectSupport() { if (null == DASInfo) { return false; } if (null == DASInfo.Modules) { return false; } if (0 == DASInfo.Modules.Length) { return false; } return DASInfo.Modules[0].IsProgrammable; } protected bool _haveInited = false; /// /// returns whether the unit supports 2V or not /// right now this is only used in one spot (SLICE2 AsyncQueryConfiguration) /// so it may not be true for purposes outside of this function without /// additional work /// /// protected virtual bool GetSupports2Volt() { return true; } protected override void AsyncQueryTestSetup(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceQueryTestSetupAsyncInfo; var config = GetConfigAttributes(this); //Temp: This is just to use RetrieveEventXMLConfig for now var testSetupString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.TestSetup, this); config.StoreXmlConfigPC(testSetupString, Constants.DAS_TEST_SETUPS, config.TestSetupUniqueId); info.Success(); } /// /// Updates the configuration on the unit using a given input file /// 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s) /// protected override void AsyncUpdateConfigurationFromFile(object asyncInfo) { var info = asyncInfo as SliceServiceAsyncInfo; if (!(info.functionData is string filePath)) { info.Error($"AsyncUpdateConfigurationFromFile: {SerialNumber} - Empty file path"); return; } if (!File.Exists(filePath)) { info.Error($"SyncUpdateConfigurationFromFile: {SerialNumber} - invalid file path: {filePath}"); return; } try { IncrementNumberOfTimesCorrected(); var fileData = File.ReadAllText(filePath); ConfigData = ConfigurationData.DeserializeFromString(fileData); var config = GetConfigAttributes(this); config.StoreXMLConfig(fileData, Slice.ConfigAttributes.FileStore.Event); config.StoreXMLConfig(fileData, Slice.ConfigAttributes.FileStore.Diagnostic); QueryConfigGetScaleFactors(info); } catch (Exception ex) { APILogger.Log(ex); info.Error(ex.Message, ex); return; } info.Success(); } //for testing this is the percentage diagnostics file store should fail private const int DIAGNOSTICS_FAIL_PERCENT = 20; //for testing this is the percentage event file store should fail private const int EVENT_FAIL_PERCENT = 50; //for testing this is how many bytes to mess with when a file store fails private const int NUM_BYTES_TO_MESS_WITH = 5; //mess with some bytes private static void DecideToFailOrNot(ConfigAttributes.FileStore fileStore, ref List allBytes) { if (allBytes.Count < NUM_BYTES_TO_MESS_WITH) { return; } var r = new Random(DateTime.Now.Millisecond); var failPercent = 0; if (fileStore == ConfigAttributes.FileStore.Diagnostic) { failPercent = DIAGNOSTICS_FAIL_PERCENT; } else if (fileStore == ConfigAttributes.FileStore.Event) { failPercent = EVENT_FAIL_PERCENT; } failPercent = 100 - failPercent; if (r.Next(0, 100) > failPercent) { //we failed, mess up some bytes! var badBytes = new byte[NUM_BYTES_TO_MESS_WITH]; r.NextBytes(badBytes); //pick a spot for (var i = 0; i < NUM_BYTES_TO_MESS_WITH; i++) { var index = r.Next(0, allBytes.Count - 1); allBytes[index] = badBytes[i]; } } } /// /// This function was previously part of QueryConfiguration /// I abstracted it out so that UpdateConfiguration could call it /// it's needed to initialize some data structions that are part of the /// configuration /// 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s) /// private void QueryConfigGetScaleFactors(SliceServiceAsyncInfo info, bool bReadDeviceData = true) { var config = GetConfigAttributes(this); try { var scaleFactors = new float[0]; try { //15949 S6A when streaming does a whole bunch of unnecessary queries //only get this attribute while not streaming (to avoid failing) if (!GetIsStreaming()) { // scaleFactors = GetScaleFactors(); } } catch (Exception ex) { APILogger.Log(ex); } #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 if (null == scaleFactors) { scaleFactors = new float[0]; } info.Progress(80); // set the OwningDAS and OwningModule since it was skipped during serialization if (null != ConfigData.Modules) { foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; // 17873 outside of download, ask device for "arm" sample rate, otherwise ask for first event's var fSampleRate = bReadDeviceData ? config.ActualSampleRate : config.GetEventSamplerate(0); if (float.IsNaN(fSampleRate)) { fSampleRate = 10000F; APILogger.Log("Warning invalid actual sample rate detected, defaulting back to 10k sps"); } //per CPB, use the realtime sample rate in this case //14549 Unhandled exception when using hardware discovery with SLICE6 AIR if (float.IsInfinity(fSampleRate)) { QueryArmAttribute qaa = new QueryArmAttribute(this); qaa.Key = AttributeTypes.ArmAndEventAttributes.RealtimeSampleRate; qaa.SyncExecute(); fSampleRate = Convert.ToSingle(qaa.Value); APILogger.Log( $"Warning invalid actual sample rate detected (infinity), default back to realtime rate, {fSampleRate}"); } module.ActualSampleRateHz = Convert.ToUInt32(fSampleRate); 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.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber]; } //double EuPerMv = GetEuPerMv(analog); } } } } } 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(string.Format("Connection with {0} failed", SerialNumber)); 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 } } /// /// queries the BaseFirmwareBuildID system attribute for the purpose of getting the string into the log /// 26821 Add query of BaseFirmwareBuildID for SLICE PRO/ 1.5/ S6/S6A/PowerPRO/S6DB/S6DB3/ TSR AIR for Logs /// All SLICE2 derivatives appear to support this (including S6DB) /// protected virtual void QueryBaseFirmwareBuildId() { try { var query = new QuerySystemAttributeSLICE2(this); query.Key = AttributeTypes.SystemAttributesSLICE2.BaseFirmwareBuildID; query.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex); } } /// /// retrieves the number of times configuration has been written, increments the count, then stores the value /// protected void IncrementNumberOfTimesWritten() { var num = 0L; try { var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesWritten); if (long.TryParse(sValue, out long temp)) { num = temp; } } catch (Exception ex) { APILogger.Log(ex); } num++; try { SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesWritten, num.ToString()); APILogger.Log($"Setting NumberTimesWritten to {num}"); } catch (Exception ex) { APILogger.Log(ex); } } /// /// retrieves the number of times the configuration has been retrieved, increments, and stores /// protected void IncrementNumberOfTimesCorrected() { var num = 0L; try { var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesCorrected); if (long.TryParse(sValue, out long temp)) { num = temp; } } catch (Exception ex) { APILogger.Log(ex); } num++; try { SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesCorrected, num.ToString()); APILogger.Log($"Setting NumberTimesCorrected to {num}"); } catch (Exception ex) { APILogger.Log(ex); } } /// /// retrieves the number of times configuration was detected invalid, increments, and stores the count /// protected void IncrementNumberOfFailedReads() { var numberOfFailures = 0L; try { var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesDetected); if (long.TryParse(sValue, out long temp)) { numberOfFailures = temp; } } catch (Exception ex) { APILogger.Log(ex); } numberOfFailures++; try { SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesDetected, numberOfFailures.ToString()); APILogger.Log($"Setting NumberTimesDetected to {numberOfFailures}"); } catch (Exception ex) { APILogger.Log(ex); } } /// /// basically a copy of SLICE1 ... may be able to eliminate and use the base version. /// /// protected override void AsyncQueryConfiguration(object configAsyncInfo) { try { if (!_haveInited) { InitMinProto(); _haveInited = true; } if (GetIsStreaming()) { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting this attribute if streaming, it will fail } else { QueryBaseFirmwareBuildId(); if (!ShouldQueryFeatureConfig()) { _featureConfigValue = 1; } else { //hardware gen 3 we have to tell whether we are modified or not //http://fogbugz/fogbugz/default.asp?10080 var qsa = new QuerySystemAttribute_Slice2Bridge_GEN3(this); qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2_GEN3.FeatureConfig; //this is a bridge attribute, we are making a small assumption that //all the modules have this bit set... qsa.DeviceID = 1; qsa.SyncExecute(); if (qsa.Value is float f) { //this would throw an invalid cast, so avoid it } else { _featureConfigValue = (ushort)qsa.Value; } } } } catch (Exception) { } var info = configAsyncInfo as SliceServiceQueryConfigAsyncInfo; try { if (this is IDASReconfigure) { (this as IDASReconfigure).GetMaxModuleCount(); } } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception) { } var hardwareRev = -1; var baseType = -1; //15949 S6A when streaming does a whole bunch of unnecessary queries //only get this attribute if not streaming (otherwise it will fail) if (!GetIsStreaming()) { try { hardwareRev = QueryHardwareRevision(); baseType = QueryBaseType(); _rangeBandwithLimited = hardwareRev < 1; } catch (Exception ex) { APILogger.Log("failed to query hardware revision and type", ex); } } info.Progress(10); if (hardwareRev >= 1) { _supportsTimeSynchronization = true; try { var qbst = new QueryBaseSystemTime(this); qbst.SyncExecute(); _systemDateTime = qbst.SystemTime; } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception ex) { APILogger.Log("failed to query system time", ex); } } info.Progress(15); var dasDisplayOrder = -1; var displayOrder = new int[] { -1 }; try { dasDisplayOrder = GetDASDisplayOrder(); displayOrder = GetChannelDisplayOrder(); } catch (Exception) { } try { // pickup the channel data var config = GetConfigAttributes(this); info.Progress(40); try { var StoredInfoString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.Diagnostic, this); ConfigData = ConfigurationData.DeserializeFromString(StoredInfoString); var blank = string.IsNullOrWhiteSpace(StoredInfoString); var failedValidation = false; if (!VerifyConfig()) { failedValidation = true; ConfigData = null; } if (ConfigData == null || ConfigData.Modules == null || ConfigData.Modules.Length == 0) { //note that not all users may be using microsoft prism and event structures... //so put this in a try in case they aren't (f/w test utility) // 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s) IncrementNumberOfFailedReads(); try { var eventAggregator = ContainerLocator.Container.Resolve(); eventAggregator.GetEvent().Publish(new DASConfigurationArg(this, blank, failedValidation)); } catch (Exception ex) { APILogger.Log(ex); } APILogger.LogString("QueryConfiguration: ConfigData or Modules empty, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } else if (DASInfo == null || DASInfo.Modules == null || !ModuleLengthsAreOK(info.DifferentModuleCountsAreOK)) { APILogger.LogString("QueryConfiguration: DASInfo empty or Modules not expected length, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception ex) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(ex); if (ex.Message.Contains("ReceiveFailed")) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } //for now we supress this order by requests from support var error = string.Format(Strings.InvalidConfig, SerialNumber); ConfigData = MakeDefaultConfigFromInfo(); } info.Progress(50); try { if (null != ConfigData.Modules) { foreach (var m in ConfigData.Modules) { foreach (var c in m.Channels) { if (c is AnalogInputDASChannel aic) { if (_hardwareRevision == GEN3_REV_B && _baseType == PROBaseType) { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport)) { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE, SensorConstants.BridgeType.QuarterBridge, SensorConstants.BridgeType.HalfBridge_SigPlus }; } else { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE, SensorConstants.BridgeType.QuarterBridge }; } aic.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt10, ExcitationVoltageOptions.ExcitationVoltageOption.Volt2, ExcitationVoltageOptions.ExcitationVoltageOption.Volt5}; } else if (DASInfo.Modules[0].IsProgrammable && IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels)) { if (GetSupports2Volt()) { aic.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5, ExcitationVoltageOptions.ExcitationVoltageOption.Volt2 }; } else { aic.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5 }; } if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport)) { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE, SensorConstants.BridgeType.HalfBridge_SigPlus }; } else { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE }; } } if (aic.DigitalInputChannel) { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.DigitalInput }; } } } } } } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception) { } info.Progress(60); try { var idx = 0; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) && DASInfo.Modules.Any() && DASInfo.Modules[0].IsProgrammable) { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors) && info.ReadIds) { var query = new QueryStackSensorIDs(this); query.SyncExecute(); if (null != ConfigData.Modules) { foreach (var m in ConfigData.Modules) { foreach (var c in m.Channels) { if (c is AnalogInputDASChannel aic) { var id = HexEncoding.ToString(query.IDs[idx]); var eid = new EID(id); if (eid.IsValid() && !RunTestVariables.BypassEIDRead) { c.IDs = new EID[] { eid }; } else { c.IDs = new EID[0]; } switch (Convert.ToInt32(query.Types[idx])) { case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE: aic.IEPEChannel = true; break; case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE_LOW: aic.IEPEChannel = true; break; default: aic.IEPEChannel = false; break; } } idx++; } } } } } } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception ex) { APILogger.Log("Failed to get channel types", ex); } // get scale factors info.Progress(70); if (null != ConfigData) { ConfigData.DisplayOrder = displayOrder; ConfigData.DasDisplayOrder = dasDisplayOrder; } try { QueryConfigGetScaleFactors(info, info.DeviceScaleFactors); } catch (Exception ex) { APILogger.Log(ex); } try { // 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 if (!(IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) && DASInfo.Modules[0].IsProgrammable)) { 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); if (channel is OutputSquibChannel) { dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber / 2); } //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting ids while streaming, it will fail if (!GetIsStreaming()) { channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, 0); } } //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting ids while streaming, it will fail if (!GetIsStreaming()) { IEID[] ids = null; //performance improvement, don't check (and re-check 3 times) for a battery id on channel 255 //update - SLICE2 and beyond don't have battery ids, don't check ... if (null != ids && 0 != ids.Length) { module.OwningDAS.DASInfo.BatteryID = ids[0]; module.OwningDAS.SetDASInfo(); } } } } } } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception) { } info.Progress(90); try { RetrieveEventGuids(); } catch (Exception ex) { APILogger.Log("problem getting event guids", ex); } try { DASInfo.MaxEventStorageSpaceInBytes = ReadSampleStorageSizeInBytes(); } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception ex) { APILogger.Log("problem getting sample storage size in bytes", ex); } info.Progress(95); try { GetAutoArmStatus(); } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (Exception ex) { APILogger.Log("problem getting autoarm status", ex); } 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(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } /// /// This function returns True when the number of DAS modules is equal to the /// number of Config modules or when it is acceptable if they differ. /// /// private bool ModuleLengthsAreOK(bool differentModuleCountsAreOK) { var result = DASInfo.Modules.Length == ConfigData.Modules.Length; if (!result && differentModuleCountsAreOK) { try { // 28263 The config from a SLICE6Air that has a config that's not built from DataPRO 4.0 or later will not have a 3rd module for // the UART channel and not have a 4th module for the Stream Output channel, so ignore the difference in the number of modules // only if this call is for the purposes of reading an old config for a Quick Build. if (_baseType == (int)SLICERecorder.SLICEPRO_BaseType.SLICE6AIR) { //Pre-DataPRO 4.0, the config only had two 3-channel modules, but as of 4.0 //additional modules exist for a UART channel, and a Stream Output channel. //Since differentModuleCountsAreOK is True, we can add the new modules. var newModules = new IDASModule[DASInfo.Modules.Length]; //Copy all of the existing module(s) from ConfigData var index = 0; foreach (var oldModule in ConfigData.Modules) { newModules[index] = oldModule; index++; } //Add the new module(s) from DASInfo for (var i = index; i < newModules.Length; i++) { newModules[i] = MakeConfigModuleFromInfoModule((InfoResult.Module)DASInfo.Modules[i]); } //Replace the modules in the config ConfigData.Modules = newModules; result = true; } } catch (Exception ex) { APILogger.Log($"Exception in ModuleLengthsAreOK: {ex.Message}"); result = false; } } return result; } protected override void AsyncEnterLowPowerMode(object asyncInfo) { var info = (SliceServiceAsyncInfo)asyncInfo; try { //clear any results we have, they will be invalid after going to low power //16164 User can proceed past diag. without running on all channels ClearChannelDiagnosticsResults(); if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode)) { var low = new SetStackLowPowerMode(this); low.SyncExecute(); } else { if (DASInfo != null && DASInfo.Modules != null) { // DeviceID is the module index + 1 for (var i = 0; i < DASInfo.Modules.Length; i++) { try { DisablePowerSupply(DASInfo.Modules[i].TypeOfModule, (byte)(i + 1)); } catch (Exception ex) { APILogger.Log("problem disabling power supply", ex); } } } } info.Success(); } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } protected override void RetrieveEventGuids() { try { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting event guids while streaming, it will fail if (GetIsStreaming()) { SetEventGuids(null); SetEventFaultFlags(null); SetEventArmAttemps(null); ((IDownload)this)?.SetExtendedFaultFlags(null); return; } var EventCount = new QueryTotalEventCount(this, AbstractCommandBase.Default_IO_Timeout); EventCount.SyncExecute(); if (EventCount.Count < 1) { // no events SetEventGuids(null); SetEventFaultFlags(null); ((IDownload)this)?.SetExtendedFaultFlags(null); SetEventArmAttemps(null); return; } var guids = new Guid[EventCount.Count]; var downloadedStatus = new bool[EventCount.Count]; var query2 = new QueryEventAttribute(this, AbstractCommandBase.Default_IO_Timeout); var faultFlags = new ushort[EventCount.Count]; var extendedFaultFlags = new List(); query2.Key = AttributeTypes.ArmAndEventAttributes.FaultFlags; for (ushort eventIdx = 0; eventIdx < EventCount.Count; eventIdx++) { query2.EventNumber = eventIdx; guids[eventIdx] = GetEventGuid(eventIdx); extendedFaultFlags.Add(GetExtendedFaultFlags(eventIdx)); faultFlags[eventIdx] = 0; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags)) { try { query2.SyncExecute(); faultFlags[eventIdx] = (ushort)query2.Value; } catch (Exception ex) { APILogger.Log("could not get fault flags", ex); } } try { var ca = GetConfigAttributes(this);//new Slice.ConfigAttributes(this); downloadedStatus[eventIdx] = ca.EventHasBeenDownloaded(eventIdx, out uint iflag); } catch (Exception) { } } SetEventGuids(guids); SetEventFaultFlags(faultFlags); ((IDownload)this)?.SetExtendedFaultFlags(extendedFaultFlags.ToArray()); SetEventDownloadStatus(downloadedStatus); } catch (CommandException ce) { if (ce.Error != CommandErrorReason.InvalidMode) { APILogger.LogString("DASFactory.RetriveEventGuids: "); APILogger.LogException(ce); } } catch (Exception ex) { APILogger.LogString("DASFactory.RetriveEventGuids: "); APILogger.LogException(ex); } } protected override void AsyncUpdateIDs(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceAsyncInfo; if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) || !DASInfo.Modules[0].IsProgrammable) { // Not Programmable base.AsyncUpdateIDs(configAsyncInfo); } else { try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors)) { var query = new QueryStackSensorIDs(this); query.SyncExecute(); var idx = 0; foreach (var m in ConfigData.Modules) { foreach (var c in m.Channels) { if (c is AnalogInputDASChannel analog) { var eid = new EID(HexEncoding.ToString(query.IDs[idx])); if (eid.IsValid() && !RunTestVariables.BypassEIDRead) { c.IDs = new EID[] { eid }; } else { c.IDs = new EID[0]; } switch (Convert.ToInt32(query.Types[idx])) { case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE: analog.IEPEChannel = true; break; default: analog.IEPEChannel = false; break; } } idx++; } } } info.Success(); } catch (Exception ex) { APILogger.Log("Failed to update ids - ", ex); info.Error("Failed to update IDS", ex); } } } /// /// Retrieve the info to fill in the ConfigData property /// /// The delegate to report to /// User supplied data that we pass along void IConfigurationActions.QueryConfiguration(ServiceCallback callback, object userData, uint crc, string strConfig, bool bReadIds, bool bDeviceScaleFactors, bool differentModuleCountsAreOK) { var info = new SliceServiceQueryConfigAsyncInfo(callback, userData, crc, strConfig, bReadIds, bDeviceScaleFactors, differentModuleCountsAreOK); LaunchAsyncWorker("Slice2.QueryConfiguration", AsyncQueryConfiguration, info); } protected const int UNKNOWN_REVISION = -1; protected const int GEN2_REV_A = 0; protected const int GEN3_REV_B = 1; protected int QueryHardwareRevision() { if (_hardwareRevision != UNKNOWN_REVISION) { return _hardwareRevision; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision)) { return UNKNOWN_REVISION; } else { if (RunTestVariables.InRunTest && this is EthernetSlice6) { _hardwareRevision = RunTestVariables.AssumedBaseHardwareRevisionSLICE6; return _hardwareRevision; } try { var qsa = new QuerySystemAttributeSLICE2(this); qsa.Key = AttributeTypes.SystemAttributesSLICE2.BaseHardwareRevision; qsa.SyncExecute(); _hardwareRevision = Convert.ToInt32(qsa.Value ?? -1); QueryHardwareRevisionPost(); return Convert.ToInt32(qsa.Value ?? -1); } catch (Exception ex) { APILogger.Log("failed to get hardware revision", ex); } return UNKNOWN_REVISION; } } protected virtual void QueryHardwareRevisionPost() { } protected const int UNKNOWNBaseType = -1; protected const int PROBaseType = 0; protected const int NANOBaseType = 1; protected int QueryBaseType() { //this shouldn't change if we've already gotten it once ... if (_baseType != UNKNOWNBaseType) { return _baseType; } if (RunTestVariables.InRunTest && this is EthernetSlice6 && RunTestVariables.AssumeBaseTypeSLICE6 > 0) { _baseType = RunTestVariables.AssumeBaseTypeSLICE6; return _baseType; } try { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting this attribute if streaming, it will fail if (GetIsStreaming()) { return UNKNOWNBaseType; } var qsa = new QuerySystemAttributeSLICE2(this); qsa.Key = AttributeTypes.SystemAttributesSLICE2.BaseType; qsa.SyncExecute(); _baseType = Convert.ToInt32(qsa.Value); return _baseType; } catch (Exception ex) { APILogger.Log("failed to get base type", ex); } return UNKNOWNBaseType; } #region IClockSyncActions /// /// AsyncInfo for IClockSyncActions that derive from here (S6, S6A, S6DB, etc) /// internal class SetClockSyncConfigPacket { public SliceServiceAsyncInfo Info { get; set; } public SetClockSyncConfigPacket(SliceServiceAsyncInfo info) { Info = info; } } internal class GetClockSyncStatusPacket { public SliceServiceAsyncInfo Info { get; set; } public GetClockSyncStatusPacket(SliceServiceAsyncInfo info) { Info = info; } } protected virtual void AsyncSetClockSyncConfig(object asyncInfo) { if (!(asyncInfo is SetClockSyncConfigPacket packet)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetClockSyncConfig)) { packet.Info.Error("Set Clock Config not supported"); return; } var info = packet.Info; var bLocked = false; try { Lock(); bLocked = true; try { var ssaClockSyncProfile = new SetSystemAttributeSLICE2(this, SetSystemAttribute.Default_IO_Timeout); ssaClockSyncProfile.SetValue(AttributeTypes.SystemAttributesSLICE2.ClockSyncProfile, packet.Info.functionData, true); ssaClockSyncProfile.SyncExecute(); //no exception, so update the current profile DASClockSyncProfile = (ClockSyncProfile)packet.Info.functionData; } catch (Exception ex) { APILogger.Log("Problem setting clock sync profile", ex); APILogger.RaiseError(string.Format(StringResources.UnableToSetClockSync, ex.Message, SerialNumber)); } 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("SetClockSyncConfig error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { info.Success(); APILogger.Log("SetClockSyncConfig error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } protected virtual void AsyncGetClockSyncStatus(object asyncInfo) { if (!(asyncInfo is GetClockSyncStatusPacket packet)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPSyncStatus)) { packet.Info.Error("Get PTP sync status not supported"); return; } var info = packet.Info; var bLocked = false; //SLICE6/S6DB only has PTP Sync try { Lock(); bLocked = true; try { var qsaClockSyncProfile = new QuerySystemAttributeSLICE2(this, QuerySystemAttribute.Default_IO_Timeout); qsaClockSyncProfile.Key = AttributeTypes.SystemAttributesSLICE2.ClockSyncProfile; qsaClockSyncProfile.SyncExecute(); DASClockSyncProfile = (ClockSyncProfile)qsaClockSyncProfile.Value; } catch (Exception ex) { APILogger.Log("Problem getting clock sync profile", ex); } try { var status = new Ptp1588GetSyncStatus(this); status.SyncExecute(); if (null == DASClockSyncStatus) { DASClockSyncStatus = new Dictionary(); List sources = Enum.GetValues(typeof(InputClockSource)).Cast().ToList(); for (int i = 1; i < sources.Count; i++) { // init all sources not synced DASClockSyncStatus.Add(sources[i], false); } } DASClockSyncStatus[InputClockSource.PTP] = Ptp1588Commands.PtpSyncStatus.Synced == status.SyncStatus; if (DASClockSyncStatus[InputClockSource.PTP]) { DoRTCInUTCCheck(); } } catch (Exception ex) { APILogger.Log("Problem getting PTP sync status", 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("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(); } } } internal class SetPTPDomainIDPacket { public SliceServiceAsyncInfo Info { get; set; } public SetPTPDomainIDPacket(SliceServiceAsyncInfo info) { Info = info; } } internal class GetPTPDomainIDPacket { public SliceServiceAsyncInfo Info { get; set; } public GetPTPDomainIDPacket(SliceServiceAsyncInfo info) { Info = info; } } protected virtual void AsyncSetPTPDomainID(object asyncInfo) { if (!(asyncInfo is SetPTPDomainIDPacket packet)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID)) { packet.Info.Error("Set PTP Domain ID not supported"); return; } var info = packet.Info; var bLocked = false; try { Lock(); bLocked = true; try { var ssaPTPDomainID = new SetSystemAttributeSLICE2(this, SetSystemAttribute.Default_IO_Timeout); ssaPTPDomainID.SetValue(AttributeTypes.SystemAttributesSLICE2.PTP_DomainNumber, packet.Info.functionData, true); ssaPTPDomainID.SyncExecute(); } catch (Exception ex) { APILogger.Log("Problem setting ptp domain id", 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("SetPTPDomainID error - has failed ", straightFailures, " times, giving up", ce); info.Error(ce.Message, ce); } else { info.Success(); APILogger.Log("SetPTPDomainID error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } protected virtual void AsyncGetPTPDomainID(object asyncInfo) { if (!(asyncInfo is GetPTPDomainIDPacket packet)) { return; } if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID)) { packet.Info.Error("Get PTP domain ID not supported"); return; } var info = packet.Info; var bLocked = false; try { Lock(); bLocked = true; try { var qsaPTPDomainID = new QuerySystemAttributeSLICE2(this, QuerySystemAttribute.Default_IO_Timeout); qsaPTPDomainID.Key = AttributeTypes.SystemAttributesSLICE2.PTP_DomainNumber; qsaPTPDomainID.SyncExecute(); PTPDomainID = (byte)qsaPTPDomainID.Value; } catch (Exception ex) { APILogger.Log("Problem getting ptp domain id", 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("GetPTPDomainID 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.PTPDomainID = 0; } info.Success(); APILogger.Log("GetPTPDomainID error", ce); } } catch (Exception ex) { if (bLocked) { bLocked = false; Release(); } info.Error(ex.Message, ex); } finally { if (bLocked) { Release(); } } } #endregion } /// /// SLICE2 is basically a SLICE + some overrides for now /// so, we extend slice and override specific functionality as required for SLICE2 /// /// public class SLICE2 : SLICE2_Base, IDASReconfigure where T : IConnection, new() { /// /// return values to QueryHardwareRevision /// private enum SLICEPRO_Generation { SLICEPRO_GEN2 = 0, SLICEPRO_GEN3 = 1 } /// /// the different revisions of slice pro Gen III /// determined by querying Hardware_Revision /// private enum SLICEPro_GenIIIRevisions { MAX = 2, //200KHz fixed filter HI = 3, //100KHz fixed filter AUTO = 4 //4KHz fixed filter } /// /// gets the number of samples the hardware filter shifted collected data by /// /// /// /// /// public override ulong GetPhaseShiftSamples(uint ModuleIndex, double ActualSampleRate, uint HardwareAAF, ulong originalT0) { /*int baseHardwareRevision = QueryHardwareRevision(); int BaseType = QueryBaseType(); SLICEPRO_Generation gen = (SLICEPRO_Generation)baseHardwareRevision; SLICEPro_BaseTypes type = (SLICEPro_BaseTypes)BaseType; switch (type) { case SLICEPro_BaseTypes.DIM: return GetPhaseShiftSamplesDIM(ActualSampleRate, HardwareAAF, originalT0); case SLICEPro_BaseTypes.TOM: return GetPhaseShiftSamplesTOM(ActualSampleRate, HardwareAAF, originalT0); case SLICEPro_BaseTypes.SIM: { switch (gen) { case SLICEPRO_Generation.SLICEPRO_GEN2: { QueryHardwareConfiguration qhc = new QueryHardwareConfiguration(this, Convert.ToInt32(ModuleIndex)); qhc.SyncExecute(); switch (qhc.HardwareType) { case QueryHardwareConfiguration.Types.IEPE_Configurable: case QueryHardwareConfiguration.Types.Bridge_Configurable: case QueryHardwareConfiguration.Types.Bridge: case QueryHardwareConfiguration.Types.IEPE: return GetPhaseShiftSamplesGen2HBW(ActualSampleRate, HardwareAAF, originalT0); case QueryHardwareConfiguration.Types.Bridge_Configurable_Low: case QueryHardwareConfiguration.Types.IEPE_Configurable_Low: return GetPhaseShiftSamplesGen2LBW(ActualSampleRate, HardwareAAF, originalT0); } }break; case SLICEPRO_Generation.SLICEPRO_GEN3: { QuerySystemAttribute_Slice2Bridge_GEN3 qsa = new QuerySystemAttribute_Slice2Bridge_GEN3(this); qsa.DeviceID = Convert.ToByte(ModuleIndex); qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2_GEN3.Hardware_Revision; qsa.SyncExecute(); int value = Convert.ToInt32(qsa.Value); SLICEPro_GenIIIRevisions rev = (SLICEPro_GenIIIRevisions)value; switch (rev) { case SLICEPro_GenIIIRevisions.AUTO: return GetPhaseShiftSamplesGen3Auto(ActualSampleRate, HardwareAAF, originalT0); case SLICEPro_GenIIIRevisions.HI: return GetPhaseShiftSamplesGen3Hi(ActualSampleRate, HardwareAAF, originalT0); case SLICEPro_GenIIIRevisions.MAX: return GetPhaseShiftSamplesGen3Max(ActualSampleRate, HardwareAAF, originalT0); } }break; } }break; } //note, we should never get here...*/ return 0; } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesGen3Max(double actualSampleRate, uint hardwareAAF, ulong originalT0) { var microSeconds = 0D; if (hardwareAAF < 50000) { microSeconds = 4.8D + 556D / hardwareAAF; } else if (hardwareAAF < 200000) { microSeconds = 4.8D + 556D / 45000D; } else { microSeconds = 4.8; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3MAX"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesGen3Hi(double actualSampleRate, uint hardwareAAF, ulong originalT0) { var microSeconds = 0D; if (hardwareAAF < 50000) { microSeconds = 9D + 556D / hardwareAAF; } else if (hardwareAAF < 100000) { microSeconds = 9D + 556D / 45000D; } else { microSeconds = 9D; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3HI"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesGen3Auto(double actualSampleRate, uint hardwareAAF, ulong originalT0) { var microSeconds = 0D; if (hardwareAAF < 4000) { microSeconds = 225D + 556D / hardwareAAF; } else { microSeconds = 225D; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3AUTO"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesGen2LBW(double actualSampleRate, uint hardwareAAF, ulong originalT0) { var microSeconds = 0D; if (hardwareAAF < 50000) { microSeconds = 8D + 556D / hardwareAAF; } else if (hardwareAAF < 100000) { microSeconds = 8D; } else if (hardwareAAF < 200000) { microSeconds = 4D; } else { microSeconds = 4; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen2LBW"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesGen2HBW(double actualSampleRate, uint hardwareAAF, ulong originalT0) { var microSeconds = 0D; if (hardwareAAF < 50000) { microSeconds = 4D + 556D / hardwareAAF; } else if (hardwareAAF < 100000) { microSeconds = 4D + 556D / 45000; } else if (hardwareAAF < 200000) { microSeconds = 4D; } else { microSeconds = 2; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen2HBW"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesDIM(double actualSampleRate, uint HardwareAAF, ulong originalT0) { return GetPhaseShiftSamples(.35D, actualSampleRate, originalT0, "Gen3DIM"); } /// /// computes the number of samples hardware AAF shifted collected data /// see /// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate /// /// /// /// private ulong GetPhaseShiftSamplesTOM(double actualSampleRate, uint HardwareAAF, ulong originalT0) { var microSeconds = 0D; if (HardwareAAF < 50000) { microSeconds = 9D + 556D / HardwareAAF; } else if (HardwareAAF < 100000) { microSeconds = 9D + 556D / 45000D; } else { microSeconds = 9; } return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3TOM"); } void IDASReconfigure.SetMaxModuleCount(int count) { try { //turn off power supplies first if (DASInfo != null && DASInfo.Modules != null && DASInfo.Modules.Length > 0) { DisablePowerSupply(DASInfo.Modules[0].TypeOfModule, 1); } var ssa = new SetSystemAttributeSLICE2(this); ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.MaxSLICEEnable, (byte)count, true); ssa.SyncExecute(); } catch (Exception ex) { APILogger.Log("Failed to set max module count", SerialNumber, ex); } } private int _maxModuleCount = -1; /// /// gets the physical max number of modules. /// /// int IDASReconfigure.GetMaxModuleCount() { if (-1 == _maxModuleCount) { try { var q = new QuerySystemAttributeSLICE2(this); q.Key = AttributeTypes.SystemAttributesSLICE2.SLICECountOnStack; q.SyncExecute(); _maxModuleCount = Convert.ToInt32(q.Value); return Convert.ToInt32(q.Value); } catch (Exception ex) { APILogger.Log("Failed to get max module count", SerialNumber, ex); } return DASInfo.Modules.Length; } else { return _maxModuleCount; } } } /// /// 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_SLICE2 : 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_SLICE2(DTS.Common.Interface.DASFactory.ICommunication sock) : base(sock) { LogCommands = false; } public QueryEventData_SLICE2(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec) : base(sock, TimeoutMillisec) { LogCommands = false; } private ulong GetRequestedStartSport() { if (recorder is SLICE2) { var slice2_Ethernet = recorder as SLICE2; return (slice2_Ethernet.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport; } else if (recorder is SLICE2) { var slice2_USB = recorder as SLICE2; return (slice2_USB.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport; } else { throw new NotSupportedException("GetRequestedStartSport not supported for " + recorder.ConnectString); } } private void PushLeftOverData(ushort[] daters) { if (recorder is SLICE2 slice2_USB) { slice2_USB.PushLeftOverData(daters); } else if (recorder is SLICE2 slice2_Ethernet){ slice2_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==" + CommandPacketBase.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 = GetRequestedStartSport(); 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)); //performance improvement, do less calculations over iterations var product = samplesProcessed * ChannelsDownloaded; var leftover = new ushort[_data.Length - product]; for (var i = 0; i < leftover.Length; i++) { leftover[i] = _data[i + product]; } 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); var samplesProcessed = Convert.ToInt32(Math.Truncate(((double)_data.Length - offset) / ChannelsDownloaded)); var leftover = new ushort[_data.Length - offset - (samplesProcessed * ChannelsDownloaded)]; //performance improvement, do less calculations over iterations offset += samplesProcessed * ChannelsDownloaded; for (var i = 0; i < leftover.Length; i++) { leftover[i] = _data[i + offset]; } PushLeftOverData(leftover); } return UserCallback(cbReport); } private ushort[] PopLeftOverData() { if (recorder is SLICE2 slice2_USB) { return slice2_USB.PopLeftOverData(); } else if (recorder is SLICE2 slice2_Ethernet) { return slice2_Ethernet.PopLeftOverData(); } else if (recorder is SLICE6 slice6_Ethernet) { return slice6_Ethernet.PopLeftOverData(); } else if (recorder is SLICE6AIR slice6air_Ethernet) { return slice6air_Ethernet.PopLeftOverData(); } else if (recorder is SLICE6DB slice6db_Ethernet) { return slice6db_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) < GetRequestedStartSport())//(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 (GetRequestedStartSport() > FirstSample) //slice2.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport > FirstSample) { offset = Convert.ToInt32(GetRequestedStartSport() - 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)); var rv = new short[completeSamples]; short currentVal = 0; offset += channel; //performance improvement, skip unnecessary stuff ... for (var i = 0; i < completeSamples; i++) { val = _data[i * ChannelsDownloaded + offset]; currentVal = (short)((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)) + 0x8000); rv[i] = currentVal; } signedADC = rv; } // 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]; } } public List GetRawIndexedChannelData(ref List leftOver, ref ulong carryoverStartPoint) { carryoverStartPoint += Convert.ToUInt64(_data.Length); var data = new List(leftOver); data.AddRange(_data); var samplesPresent = Convert.ToInt32(data.Count / ChannelsDownloaded); var output = new List(); for (var i = 0; i < 8; i++) { output.Add(new ushort[samplesPresent]); for (var iSample = 0; iSample < samplesPresent; iSample++) { output[i][iSample] = data[iSample * ChannelsDownloaded + i]; } } data.RemoveRange(0, samplesPresent * ChannelsDownloaded); leftOver.Clear(); leftOver.AddRange(data); return output; } } }