Files
DP44/DataPRO/IService/Classes/SLICEService/SLICE Service.Download.cs

1345 lines
62 KiB
C#
Raw Normal View History

2026-04-17 14:55:32 -04:00
using DTS.Common.DAS.Concepts;
using DTS.Common.DASResource;
using DTS.Common.Enums.DASFactory;
using DTS.Common.ICommunication;
using DTS.Common.Interface.Connection;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DASFactory.Download;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Command;
using DTS.DASLib.Command.SLICE;
using DTS.DASLib.Command.SLICE.DownloadCommands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
namespace DTS.DASLib.Service
{
public partial class Slice<T> : Communication<T>,
IDASCommunication,
IConfigurationActions,
IDiagnosticsActions,
ITriggerCheckActions,
IRealTimeActions,
IArmActions,
IDownloadActions where T : IConnection, new()
{
public virtual DateTime SystemBaseTime => DateTime.MinValue;
public virtual bool SupportsTimeSynchronization => false;
public virtual bool RangeBandwidthLimited => false;
public virtual bool RequireDiagnosticRateMatchSampleRate() { return false; }
#region Downloading
public class SliceSetEventInfoAsync : SliceServiceAsyncInfo
{
public string Id { get; set; }
public Guid Guid { get; set; }
public int EventIndex { get; set; }
public ulong StartRecordSample { get; set; }
public ulong TotalSamples { get; set; }
public ulong[] TriggerSamples { get; set; }
public uint EventHasDownloaded { get; set; }
public SliceSetEventInfoAsync(ServiceCallback callback,
object userData,
int eventIndex,
ulong startRecordSample,
ulong totalSamples,
ulong[] triggerSamples,
Guid guid,
string id,
uint eventHasDownloaded)
: base(callback, userData)
{
EventIndex = eventIndex;
StartRecordSample = startRecordSample;
TriggerSamples = triggerSamples;
TotalSamples = totalSamples;
Id = id;
Guid = guid;
EventHasDownloaded = eventHasDownloaded;
}
}
/// <inheritdoc cref="IDownloadActions" />
void IDownloadActions.CorrectT0s(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.CorretT0s", new WaitCallback(AsyncCorrectT0s), info);
}
/// <summary>
/// scans data to find where railed data starts and attempts to identify T0 in the dataset
/// </summary>
/// <param name="asyncInfo"></param>
protected virtual void AsyncCorrectT0s(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
try
{
//scan through for a channel that is configured, then look for a T0 and start of railed data
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
var module = ConfigData.Modules[moduleIdx];
for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++)
{
var channel = module.Channels[channelIdx];
if (!channel.IsConfigured()) { continue; }
if (!FindRailAndT0(channel.Number, out var railIdx, out var t0Idx, info))
{
//failed to find railed data (maybe it was railed from the start)
//try again on the next channel ...
continue;
}
//we found a T0 and start of railed data, set T0 and total samples recorded attributes
info.NewData(DFConstantsAndEnums.T0CorrectionStatus.SettingAttributes);
SetT0AndTotalSamples(t0Idx, railIdx);
info.Success();
return;
}
}
info.Error($"T0 correction not available");
return;
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
/// <summary>
/// sets the TriggerSampleNumber and TotalSamplesRecorded attributes to
/// the requested values
/// </summary>
/// <param name="t0Idx"></param>
/// <param name="railIdx"></param>
protected void SetT0AndTotalSamples(ulong t0Idx, ulong railIdx)
{
var sea = new SetEventAttribute(this);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.TriggerSampleNumber, t0Idx, true);
sea.SyncExecute();
sea = new SetEventAttribute(this);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.TotalSamplesRecorded, railIdx - 1, true);
sea.SyncExecute();
}
/// <summary>
/// scans for when a rail starts and a possible T0 as defined by a local max peak or local min trough
/// returns true if rail is found, however note that a railindex of T0 means that the data is starting
/// off railed and never recorded.
/// railed data must be railed for a certain number of samples to be railed
/// (defined by T0_REQUIRED_CONSECUTIVE_RAILS)
/// makes use of FindRail to find the rail, and then FindT0 to find the t0, so this
/// is an aggregator of those functions
/// </summary>
/// <param name="channel">channel to scan on</param>
/// <param name="railIndex">index of where railed data starts</param>
/// <param name="T0Index">index of max/min local peak/trough</param>
/// <returns></returns>
protected bool FindRailAndT0(int channel, out ulong railIndex, out ulong T0Index,
SliceServiceAsyncInfo info)
{
info.NewData(DFConstantsAndEnums.T0CorrectionStatus.ScanningForPowerLoss);
railIndex = 0;
T0Index = 0;
var start = 0UL;
var end = Convert.ToUInt64(MaxMemory());
var samples = GetSamples(channel, start, start + T0_SAMPLING_SIZE);
//if no samples were retrieved then there are other issues preventing getting data ...
//(like maybe corruption or other missing event data)
if (0 == samples.Length) { return false; }
//first check to see that we don't start with railed data, if we do
//we can just stop right away
if (FindRailStart(samples, out var railStart, out _))
{
if (railStart == 0)
{
return false;
}
else
{
railIndex = railStart;
}
start += T0_SAMPLING_SIZE;
}
while (0 == railIndex)
{
//we'll search binary from start to end, so find where that is
var delta = (end - start) / 2;
var segment = start + delta;
//we'll be requesting T0_SAMPLING_SIZE samples, so try to put
//our midpoint in the middle of that number of samples as well
if (segment >= T0_SAMPLING_SIZE)
{
segment -= T0_SAMPLING_SIZE / 2;
}
samples = GetSamples(channel, segment, segment + T0_SAMPLING_SIZE);
//if there are 0 samples then there is nothing we can probe ...
var allRailed = false;
if (0 == samples.Length)
{
end = segment;
allRailed = true;
}
else
{
if (FindRailStart(samples, out railStart, out allRailed))
{
//if we found the start of railed data AND we all samples aren't railed
//then we did actually find the start of railed data, so record it and stop looking for it
if (!allRailed)
{
railIndex = segment + railStart;
break;
}
}
}
if (end - start <= T0_SAMPLING_SIZE)
{
//didn't find the rail and we searched all the samples in this block and those
//are all the samples were we could find it...
return false;
}
else
{
if (allRailed)
{
//if all the data was railed, then the start of railed data is before our current point
//move the end of where we are looking accordingly
//now the segment is T0_SAMPLING_SIZE # of samples, so we could slide this even further
end = segment;
}
else
{
//if we didn't find the start of railed data and it's not all railed, that
//means the start of railed data must happen after our current point
//adjust the start point where we are looking accordingly
start = segment + T0_SAMPLING_SIZE;
}
}
}
//if our rail index > 0, then it's possible there's a T0 we can find via max/min peak/trough
if (railIndex > 0)
{
APILogger.Log($"{SerialNumber} appears to rail at {railIndex}");
info.NewData(DFConstantsAndEnums.T0CorrectionStatus.ScanningForPeaksAndTroughs);
T0Index = FindT0(channel, railIndex);
}
return railIndex != 0;
}
private const int FIND_T0_UPDATE_FREQUENCY = 10000;
/// <summary>
/// scans the channel data from 0 to the where rail is hit looking for peaks and troughs
/// </summary>
/// <param name="channel">channel to look at on das</param>
/// <param name="railIndex">the index of where railed data starts at</param>
/// <returns>index of guessed T0</returns>
private ulong FindT0(int channel, ulong railIndex)
{
var samples = GetSamples(channel, 0, railIndex);
//if we receive no samples there is nowhere to look for the T0 ...
if (0 == samples.Length)
{
return 0;
}
//we want to find the max/min peak value, so goes down after (or up on min);
var min = short.MaxValue;
var max = short.MinValue;
var minIndex = -1;
var maxIndex = -1;
//go to sample -1 as we are looking for a peak or trough, so the next sample has to go up from trough or down from peak
for (var i = 1; i < samples.Length - 1; i++)
{
if (0 == i % FIND_T0_UPDATE_FREQUENCY)
{
APILogger.Log($"{SerialNumber} : Find T0 : {100 * i / samples.Length:F0}%");
}
var adc = samples[i];
if (adc < min)
{
//a local bottom found, we are < min and the next is greater
if (samples[i + 1] > adc)
{
min = adc;
minIndex = i;
}
}
if (adc > max)
{
if (samples[i + 1] < adc)
{
//a local peak found, we are > max and the next is lesser
max = adc;
maxIndex = i;
}
}
}
APILogger.Log($"{SerialNumber} has min:{min}@{minIndex} and max:{max}@{maxIndex}");
//it could be a trough or a peak, find which has a larger difference and go with it
if (Math.Abs(min) > max)
{
return (ulong)minIndex;
}
return (ulong)maxIndex;
}
/// <summary>
/// attempts to find the start of railed data
/// </summary>
/// <param name="samples">samples to check for rails in</param>
/// <param name="railStart">index of where railed data starts (relative to the samples input)</param>
/// <param name="startsRailed">all data is railed from the start (relative to samples input)</param>
/// <returns>true if railed data was found</returns>
private bool FindRailStart(short[] samples, out ulong railStart, out bool startsRailed)
{
//we require REQUIRED_CONS_RAILS, so only go up to length - required number
for (var i = 0; i < samples.Length - T0_REQUIRED_CONSECUTIVE_RAILS; i++)
{
//if we find a railed sample, then check the next x samples are also railed
if (IsRailed(samples[i]))
{
var isValid = true;
for (var consecutive = 0; consecutive < T0_REQUIRED_CONSECUTIVE_RAILS; consecutive++)
{
if (!IsRailed(samples[i + consecutive]))
{
//for an optimization we could jump to i+consecutive +1 since we know the next possible
//start of rail is there ...
isValid = false;
break;
}
}
if (isValid)
{
//we found our required number of samples, so set the index and return
railStart = (ulong)i;
startsRailed = 0 == railStart;
return true;
}
}
}
//we didn't find the start of railed data
railStart = 0UL;
startsRailed = false;
return false;
}
/// <summary>
/// tests whether a sample is railed
/// </summary>
/// <param name="sample"></param>
/// <returns></returns>
private bool IsRailed(short sample)
{
return sample == short.MinValue || sample == short.MaxValue;
}
/// <summary>
/// how many samples to grab when probing dataset
/// we want a number that is around the max a DAS will return,
/// however because of page boundaries this we still have no
/// idea how many file operations will hit on the DAS
/// </summary>
private const int T0_SAMPLING_SIZE = 900;
/// <summary>
/// when detecting railed data data must be railed for atleast this
/// number of consecutive samples to avoid situations where a signal
/// is bouncing off of a rail or is on the rail but has some noise
/// and isn't in fact railed
/// </summary>
private const int T0_REQUIRED_CONSECUTIVE_RAILS = 5;
private const int T0_LOGDOWNLOAD_CUTOFF = 100000;
private const ulong T0_LOGDOWNLOAD_FREQUENCY_PERCENT = 5UL;
/// <summary>
/// returns all the samples for device between start and end
/// </summary>
/// <param name="channel">channel to query</param>
/// <param name="start">start sample index</param>
/// <param name="end">last sample index to include</param>
/// <returns></returns>
protected short[] GetSamples(int channel, ulong start, ulong end)
{
var numNeeded = end - start;
var newData = new List<short>();
var dr = new DownloadRequest();
dr.DASChannelNumber = DownloadRequest.ALL_CHANNELS;
dr.EventNumber = 0;
dr.StartSample = start;
dr.EndSample = end;
SetWhatToDownload(dr, false);
var waitHandle = new ManualResetEvent(false);
ulong count = 0;
var lastUpdate = 0UL;
Download((ServiceCallbackData data) =>
{
switch (data.Status)
{
case ServiceCallbackData.CallbackStatus.Success:
waitHandle.Set();
break;
case ServiceCallbackData.CallbackStatus.NewData:
foreach (var dataSample in data.DataSamples)
{
newData.AddRange(dataSample.Data[channel]);
count += (ulong)dataSample.Data[channel].Length;
if (numNeeded > T0_LOGDOWNLOAD_CUTOFF)
{
var percent = 100 * count / numNeeded;
if ((percent - lastUpdate) > T0_LOGDOWNLOAD_FREQUENCY_PERCENT)
{
lastUpdate = percent;
APILogger.Log($"{SerialNumber} : Downloading: {percent}%");
}
}
}
break;
case ServiceCallbackData.CallbackStatus.Failure:
waitHandle.Set();
break;
}
}, this);
waitHandle.WaitOne();
return newData.ToArray();
}
void IDownloadActions.SetEventInfo(int eventIndex, string id,
Guid guid,
ulong totalSamples,
ulong[] triggerSamples,
ulong startRecordSample,
uint eventHasDownloaded,
ServiceCallback callback,
object userData)
{
var info = new SliceSetEventInfoAsync(callback, userData, eventIndex,
startRecordSample, totalSamples, triggerSamples, guid, id, eventHasDownloaded);
LaunchAsyncWorker("Slice.SetEventInfo", new WaitCallback(AsyncSetEventInfo), info);
}
private void AsyncSetEventInfo(object asyncInfo)
{
var info = asyncInfo as SliceSetEventInfoAsync;
try
{
if (!string.IsNullOrEmpty(info.Id))
{
var sea = new SetEventAttribute(this);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.Name, info.Id.ToCharArray(), true);
sea.SyncExecute();
}
if (Guid.Empty != info.Guid)
{
var sea = new SetEventAttribute(this);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.EventGuid, info.Guid.ToString(), true);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SyncExecute();
}
if (ulong.MaxValue != info.StartRecordSample)
{
var sea = new SetEventAttribute(this);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.StartRecordSampleNumber, info.StartRecordSample, true);
sea.SyncExecute();
}
if (ulong.MaxValue != info.TotalSamples)
{
var sea = new SetEventAttribute(this);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.TotalSamplesRecorded, info.TotalSamples, true);
sea.SyncExecute();
}
if (null != info.TriggerSamples)
{
var sea = new SetEventAttribute(this);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.TriggerSampleNumber, info.TriggerSamples[0], true);
sea.SyncExecute();
}
if (uint.MaxValue != info.EventHasDownloaded)
{
var sea = new SetEventAttribute(this);
sea.EventNumber = Convert.ToUInt16(info.EventIndex);
sea.SetValue(AttributeTypes.ArmAndEventAttributes.EventHasBeenDownloaded, info.EventHasDownloaded, true);
sea.SyncExecute();
}
}
catch (Exception ex)
{
info.Error("Failed to set event info", ex);
return;
}
info.Success();
}
public virtual long MaxMemory()
{
if (null == DASInfo || 0 == DASInfo.NumberOfBytesPerSampleClock) { return 0L; }
return (long)(DASInfo.MaxEventStorageSpaceInBytes / DASInfo.NumberOfBytesPerSampleClock);
}
public virtual uint MaxSampleRate(int numberOfConfiguredChannels)
{
int modules = DASInfo.Modules.Length;
return modules > 0 ? Convert.ToUInt32(120000 / modules) : 120000;
}
public virtual uint MinSampleRate()
{
return 5;
}
public virtual uint MaxAAFilterRate()
{
return MaxSampleRate(0) / 5;
}
protected class SliceDownloadState : SliceServiceAsyncInfo
{
public IDownloadRequest Request;
public ulong SamplesDownloaded; // how many samples have we downloaded so far
public QueryEventDataBase DownloadCommand;
public SliceDownloadState(ServiceCallback cb, object cbObj, IDownloadRequest _Request)
: base(cb, cbObj)
{
Request = _Request;
SamplesDownloaded = 0;
DownloadCommand = null;
}
}
protected class SliceUARTDownloadState : SliceServiceAsyncInfo
{
public IUARTDownloadRequest Request;
public ulong BytesDownloaded; // how many bytes have we downloaded so far
public QueryUARTEventData DownloadCommand;
public SliceUARTDownloadState(ServiceCallback cb, object cbObj, IUARTDownloadRequest _request)
: base(cb, cbObj)
{
Request = _request;
BytesDownloaded = 0;
DownloadCommand = null;
}
}
public virtual void Download(ServiceCallback callback, object userData)
{
if (!Connected)
{
// "Slice.Download: Not currently connected"
throw new Exception(Strings.Slice_Download_Err1);
}
if (WhatToDownload.SamplesToSkip < 2)
{
// no sub-sampling
var state = new SliceDownloadState(callback, userData, WhatToDownload);
ThreadPool.QueueUserWorkItem(ExtraDownloadStart, state);
}
else
{
var state = new SliceDownloadState(callback, userData, WhatToDownload);
LaunchAsyncWorker("Slice.Download", DownloadSubSampled, state);
}
}
protected virtual void ExtraDownloadStart(object obj)
{
try
{
DownloadEventStartCmd(obj as SliceDownloadState);
}
catch (Exception ex)
{
APILogger.Log("MessageBox", Strings.SLICEDownloadExtraDownloadStartError, ex);
}
}
protected virtual QueryEventDataBase GetQueryEventData()
{
return new QueryEventDataBase(this, AbstractCommandBase.Default_IO_Timeout);
}
protected virtual void DownloadEventStartCmd(SliceDownloadState state)
{
try
{
if (state.SamplesDownloaded >= state.Request.EndSample - state.Request.StartSample + 1) return;
state.DownloadCommand = GetQueryEventData();
state.DownloadCommand.LogCommands = false;
state.DownloadCommand.EventNumber = state.Request.EventNumber;
state.DownloadCommand.Channel = state.Request.DASChannelNumber;
if (state.Request.DASChannelNumber == DownloadRequest.ALL_CHANNELS)
{
state.DownloadCommand.ChannelsDownloaded = IsTOM() ?
9 : EventInfo.Events[state.Request.EventNumber].Modules.Sum(module => module.NumberOfChannels());
}
state.DownloadCommand.LastSample = state.Request.EndSample;
// Ask for the next batch of samples
state.DownloadCommand.FirstSample = state.Request.StartSample + state.SamplesDownloaded;
state.DownloadCommand.Execute(DownloadEventCallback, state);
}
catch (CanceledException)
{
state.Cancel();
}
catch (Exception ex)
{
state.Error(ex.Message, ex);
}
}
protected CommandReceiveAction DownloadEventCallback(ICommandReport report)
{
var state = report.CallbackObject as SliceDownloadState;
try
{
if (report.Status == CommandStatus.Failure)
{
// no, it didn't work out
// "Slice.Download: BAD DATA"
state.Error(Strings.Slice_DownloadEventCallback_Err1);
return CommandReceiveAction.StopReceiving;
}
if (report.Status == CommandStatus.Canceled)
{
// user wants us to bail out
state.Cancel();
return CommandReceiveAction.StopReceiving;
}
var rep = report as QueryEventDataReport;
// OK, we have our data block
state.SamplesDownloaded += (ulong)state.DownloadCommand.Count;
var channelsToUse = IsTOM() ? 16 : state.DownloadCommand.ChannelsDownloaded;
var newData = new short[channelsToUse][];
for (var channelIdx = 0; channelIdx < state.DownloadCommand.ChannelsDownloaded; channelIdx++)
{
newData[channelIdx] = rep.Data[channelIdx].ToArray();
}
for (var channelIdx = state.DownloadCommand.ChannelsDownloaded; channelIdx < channelsToUse; channelIdx++)
{
newData[channelIdx] = rep.Data[0].ToArray();//Hack alert: In the case of a TOM, we only request 9 channels, but need to return 16 total
}
state.NewData(newData, 0, ulong.MinValue, ulong.MinValue);
double ratio = Math.Min(1.0, state.SamplesDownloaded / (double)(state.Request.EndSample - state.Request.StartSample + 1));
state.Progress((int)(ratio * 100.0));
// if we have it all we're done
if (state.SamplesDownloaded < (state.Request.EndSample - state.Request.StartSample + 1))
{
// otherwise, update parameters and call again
DownloadEventStartCmd(state);
}
else
{
var config = GetConfigAttributes(this);//new ConfigAttributes(this);
config.SetEventDownloaded(state.Request.EventNumber, 1);
state.Success();
}
}
catch (CanceledException)
{
state.Cancel();
}
catch (Exception ex)
{
state.Error(ex.Message, ex);
}
return CommandReceiveAction.StopReceiving;
}
private void DownloadSubSampled(object asyncInfo)
{
var state = asyncInfo as SliceDownloadState;
try
{
var NumberOfChannels = EventInfo.Events[WhatToDownload.EventNumber].Modules.Sum(module => module.NumberOfChannels());
// these two are per channel of course
ulong TotalNumberOfSamples = WhatToDownload.EndSample - WhatToDownload.StartSample + 1;
ulong NumberOfSubSamples = TotalNumberOfSamples / WhatToDownload.SamplesToSkip;
// allocate array to return to user
var SubSampledData = new short[NumberOfChannels][];
for (var idx = 0; idx < NumberOfChannels; idx++)
{
SubSampledData[idx] = new short[NumberOfSubSamples];
}
// loop thru and get our samples
var SubSampleIdx = 0;
for (var sampleIdx = WhatToDownload.StartSample;
sampleIdx < WhatToDownload.EndSample && (ulong)SubSampleIdx < NumberOfSubSamples;
sampleIdx += WhatToDownload.SamplesToSkip)
{
var newData = GetSingleSample(WhatToDownload.EventNumber, NumberOfChannels, sampleIdx);
// loop thru the channels and get data from cmd
for (var channelIdx = 0; channelIdx < NumberOfChannels; channelIdx++)
{
SubSampledData[channelIdx][SubSampleIdx] = newData[channelIdx][0];
}
SubSampleIdx++;
// calculate where we are
double ratio = Math.Min(1.0, SubSampleIdx / (double)NumberOfSubSamples);
state.Progress((int)(ratio * 100.0));
Application.DoEvents();
}
// send data to user
state.NewData(SubSampledData, 0, ulong.MinValue, ulong.MinValue);
state.Success();
}
catch (CanceledException)
{
state.Cancel();
}
catch (Exception ex)
{
state.Error(ex.Message, ex);
}
}
private short[][] GetSingleSample(ushort eventNumber, int numberOfChannels, ulong sampleNumber)
{
// create cmd
//var DownloadCommand = new QueryEventData(this);
var DownloadCommand = GetQueryEventData();
// we don't want to log this
DownloadCommand.LogCommands = false;
// the event number to retrive
DownloadCommand.EventNumber = eventNumber;
// the channels to get
DownloadCommand.Channel = DownloadRequest.ALL_CHANNELS;
DownloadCommand.ChannelsDownloaded = numberOfChannels;
// the sample to get
DownloadCommand.FirstSample = sampleNumber;
DownloadCommand.LastSample = sampleNumber;
// call HW to get samples
DownloadCommand.SyncExecute();
// allocate array to return to user
var newData = new short[numberOfChannels][];
// loop thru the channels and get data from cmd
for (var channelIdx = 0; channelIdx < numberOfChannels; channelIdx++)
{
DownloadCommand.GetChannelData(channelIdx, out newData[channelIdx]);
}
return newData;
}
#endregion
#region Query download
public virtual bool CheckAAF(float rate) { return true; }
public class QueryDownloadProgress
{
private SliceServiceAsyncInfo info;
private readonly int TotalNumberOfSteps;
private int CurrentStep;
public QueryDownloadProgress(SliceServiceAsyncInfo _info, int steps, int initialStep)
{
info = _info;
TotalNumberOfSteps = steps;
CurrentStep = initialStep;
report();
}
private void report()
{
if (CurrentStep <= TotalNumberOfSteps && TotalNumberOfSteps > 0)
{
var step = (int)(CurrentStep / (double)TotalNumberOfSteps * 100.0);
info.Progress(step);
}
else
{
info.Progress(100);
}
}
public void Step()
{
CurrentStep++;
report();
}
}
internal class QueryDownloadAsyncInfo : SliceServiceAsyncInfo
{
public int EventIndex { get; set; }
public QueryDownloadAsyncInfo(ServiceCallback callback, object userData, int eventIndex)
: base(callback, userData)
{
EventIndex = eventIndex;
}
}
void IDownloadActions.QueryDownload(ServiceCallback callback, object userData, int eventIndex, TDASServiceSetupInfo setupInfo)
{
var info = new QueryDownloadAsyncInfo(callback, userData, eventIndex);
LaunchAsyncWorker("Slice.QueryDownload", new WaitCallback(AsyncQueryDownload), info);
}
protected void GetEventTimeStampInfo(int eventIdx, out uint startRecordTimestampSec,
out uint triggerTimestampSec, out uint startRecordTimestampNanoSec,
out uint triggerTimestampNanoSec, out bool pTPMasterSync)
{
startRecordTimestampSec = 0;
triggerTimestampSec = 0;
startRecordTimestampNanoSec = 0;
triggerTimestampNanoSec = 0;
pTPMasterSync = false;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPTimestamp))
{
try
{
var qea = new QueryEventAttribute(this);
qea.EventNumber = Convert.ToUInt16(eventIdx);
qea.Key = AttributeTypes.ArmAndEventAttributes.ADCStartTimeStampSecNanoSec;
qea.SyncExecute();
startRecordTimestampSec = ((uint[])qea.Value)[0];
startRecordTimestampNanoSec = ((uint[])qea.Value)[1];
}
catch (Exception ex) { APILogger.Log(ex); }
try
{
var qea = new QueryEventAttribute(this);
qea.EventNumber = Convert.ToUInt16(eventIdx);
qea.Key = AttributeTypes.ArmAndEventAttributes.TriggerTimeStampSecNanoSec;
qea.SyncExecute();
triggerTimestampSec = ((uint[])qea.Value)[0];
triggerTimestampNanoSec = ((uint[])qea.Value)[1];
}
catch (Exception ex) { APILogger.Log(ex); }
try
{
uint lockedTime, outOfSyncTime;
var qea = new QueryEventAttribute(this);
qea.EventNumber = Convert.ToUInt16(eventIdx);
qea.Key = AttributeTypes.ArmAndEventAttributes.PtpSyncLockedTimeSec;
qea.SyncExecute();
lockedTime = (uint)qea.Value;
qea.EventNumber = Convert.ToUInt16(eventIdx);
qea.Key = AttributeTypes.ArmAndEventAttributes.PtpOutOfSyncTimeSec;
qea.SyncExecute();
outOfSyncTime = (uint)qea.Value;
if (lockedTime > outOfSyncTime)
{
pTPMasterSync = true;
}
else { pTPMasterSync = false; }
}
catch (Exception ex) { APILogger.Log(ex); }
}
}
protected virtual void AsyncQueryDownload(object asyncInfo)
{
var info = asyncInfo as QueryDownloadAsyncInfo;
// since we don't know exact how many XML attributes, modules or channels that's in the
// configuration, we guess on the high end.
// (44 per stored config + 4 + 3 per module + 3 per channel) * number of events
// equals 44 + 4 + 30 + 90 = 168 per event
const int StepsPerEvent = 168;
try
{
if (info.EventIndex < 0) { RetrieveDASBootCount(); }
else { TurnOffDiagnosticsMode(); }
var EventCount = new QueryTotalEventCount(this);
EventCount.SyncExecute();
var config = GetConfigAttributes(this);//new ConfigAttributes(this);
// 6 * modules + 44
var progress = new QueryDownloadProgress(info, StepsPerEvent * EventCount.Count, 1);
// we'll also freshen up the cached event GUIDs
// NOT IF WE ARE QUERYING A SPECIFIC GUID
var eventGuids = new Guid[EventCount.Count];
var faultFlags = new ushort[EventCount.Count];
var faultFlagsEx = new ushort[EventCount.Count];
var armAttempts = new byte[EventCount.Count];
var extendedFaultFlags = new List<uint[]>();
var dlReport = new DownloadReport();
//22295 Since all SLICE6Air DAS derive from IUARTDownload, SLICE6Air Ethernet Recorders
//would fall in here and create a UARTEvent if we didn't call IsSLICE6ERFirmware. An issue
//(FB 23347) was opened to draw attention to possibly modifying this workaround.
if ((this is IUARTDownload) && !(DFConstantsAndEnums.IsSLICE6ERFirmware(FirmwareVersion)) && (0 > info.EventIndex))
{
dlReport.UARTEvents = new DownloadReport.UARTEventInfo[EventCount.Count];
for (var eventIdx = 0; eventIdx < EventCount.Count; eventIdx++)
{
if (info.EventIndex >= 0 && eventIdx != info.EventIndex) { continue; }
var uartInfo = new QueryUARTEventInfo(this) { EventNumber = (ushort)eventIdx };
uartInfo.SyncExecute();
// the object to store it all in
var uartEventInfo = new DownloadReport.UARTEventInfo()
{
EventNumber = uartInfo.EventNumber,
DataPresent = uartInfo.DataPresent,
DataDownloaded = uartInfo.DataDownloaded,
TotalByteCount = uartInfo.TotalByteCount,
TriggerByteCount = uartInfo.TriggerByteCount,
StartTimestamp = uartInfo.StartTimestamp,
EndTimestamp = uartInfo.EndTimestamp,
BaudRate = uartInfo.BaudRate
};
if (0 > info.EventIndex)
{
// store it in the object
dlReport.UARTEvents[eventIdx] = uartEventInfo;
}
else { EventInfo.UARTEvents[eventIdx] = uartEventInfo; }
}
}
if (0 > info.EventIndex)
{
dlReport.Events = new DownloadReport.EventInfo[EventCount.Count];
}
for (var eventIdx = 0; eventIdx < EventCount.Count; eventIdx++)
{
GetEventTimeStampInfo(eventIdx, out var startRecordTimestampSec,
out var triggerTimestampSec, out var startRecordTimestampNanoSec, out var triggerTimestampNanoSec,
out var pTPMasterSync);
if (info.EventIndex >= 0 && eventIdx != info.EventIndex) { continue; }
extendedFaultFlags.Add(GetExtendedFaultFlags(eventIdx));
// Retrieved the stored configuration
var storedConfigStr = config.RetrieveEventXMLConfig(eventIdx, progress, this); // I/O * n
// convert it from XML to object
var storedConfig = ConfigurationData.DeserializeFromString(storedConfigStr);
// the object to store it all in
var eventInfo = new DownloadReport.EventInfo();
// get the event level values
eventInfo.Description = config.GetEventDescription(eventIdx); // I/O
try
{
if (null != ConfigData) { eventInfo.Description = ConfigData.Description; }
}
catch (Exception) { }
progress.Step();
eventInfo.TestID = config.GetEventID(eventIdx, this).TrimEnd(new char[] { '\0' }); ; // I/O
progress.Step();
eventInfo.EventNumber = eventIdx;
progress.Step();
try
{
//eventInfo.TestGUID = config.GetEventGuid(eventIdx); // I/O
eventInfo.TestGUID = GetEventGuid(eventIdx);
}
catch (Exception) { eventInfo.TestGUID = Guid.NewGuid(); }
try
{
eventInfo.FaultFlags = config.GetEventFaultFlags(eventIdx);
eventInfo.FaultFlagsEx = config.GetEventFaultFlagsEx(eventIdx);
}
catch (Exception ex)
{
APILogger.Log("could not get fault flags", ex);
}
try
{
eventInfo.ArmAttempts = config.GetEventArmAttempts(eventIdx);
}
catch (Exception ex)
{
APILogger.Log("could not get arm attempts", ex);
}
eventGuids[eventIdx] = eventInfo.TestGUID;
faultFlags[eventIdx] = eventInfo.FaultFlags;
faultFlagsEx[eventIdx] = eventInfo.FaultFlagsEx;
armAttempts[eventIdx] = eventInfo.ArmAttempts;
eventInfo.HasBeenDownloaded = config.EventHasBeenDownloaded(eventIdx, out uint flag); // I/O
progress.Step();
// figure out how many modules we have
var numberOfChannels = GetEventTotalChannels(eventIdx); // I/O
progress.Step();
var numberOfModules = config.CalculateNumberOfModules(storedConfig, numberOfChannels, IsTOM());
var numberOfUARTs = config.CalculateNumberOfUARTs(storedConfig);
var numberOfStreamOuts = config.CalculateNumberOfStreamOuts(storedConfig);
var numberOfStreamIns = config.CalculateNumberOfStreamIns(storedConfig);
// make sure we have the right number of modules
// make sure we have the right number of UARTs FB18363
if (numberOfModules + numberOfUARTs + numberOfStreamOuts + numberOfStreamIns != storedConfig.Modules.Length)
{
// "Slice.QueryDownload: The information in the recorder is corrupt"
info.Error(Strings.Slice_QueryDownload_Err1);
//SS: need to add code here to try to extract enough info
return;
}
eventInfo.Modules = new DASModule[numberOfModules];
var numberOfSamples = config.GetEventTotalSamples(eventIdx); // I/O
var triggerSampleNumber = config.GetEventTriggerSampleNumber(eventIdx); // I/O
var eventStartRecordSampleNumber = config.GetEventStartRecordSampleNumber(eventIdx); // I/O
var eventStartTime = config.GetEventStartTime(eventIdx); // I/O
var levelTriggerOffsetCorrection = config.GetEventLevelTriggerT0AdjustmentSamples(eventIdx);
var levelTriggerSeen = config.GetEventLevelTriggerSeen(eventIdx);
var actualSampleRate = config.GetEventSamplerate(eventIdx);
var stackActualSampleRate = config.GetEventStackSamplerate(eventIdx);
// 17873 download should only use event attributes rather than arm attributes
var factors = config.GetEventScaleFactors(eventIdx);
var aafilter = config.GetEventAAFilter(eventIdx);
var currentChannel = 0;
//toyota boshoku -flat data issue zendesk 5702?
//basically make sure we are using the event attribute ScaleFactorMvADC and not what is in the
//xml
for (var moduleIdx = 0; moduleIdx < numberOfModules; moduleIdx++)
{
// take the module from the stored config
var module = (DASModule)storedConfig.Modules[moduleIdx];
module.SampleRateHz = Convert.ToUInt32(actualSampleRate);
module.EmbeddedSampleRateHz = stackActualSampleRate;
/// <DesignNote topic="Level Trigger Configuration" author="PKXH">
/// Amongst other things, the level trigger offset correction, if it exists, is getting applied
/// to the module trigger sample numbers below. There shouldn't be any danger of this value being
/// erroneously applied to outside "tweaking" since the only outside tweak should only ever happen
/// if there was no trigger. Still I'm a little concerned that this value will still be invisibly
/// applied to user-overridden trigger sample numbers. We could conceivably guard against this
/// situation with an "original trigger sample number" concept, which we could then check against
/// and not re-apply things like level trigger offset correction if the current value doesn't
/// match it, implying it has been manually overridden.
/// </DesignNote>
// now update the module with dynamic data
module.OwningDAS = this;
module.NumberOfSamples = numberOfSamples;
progress.Step();
module.TriggerSampleNumbers = new ulong[1]; // only one so far
var phaseShift = GetPhaseShiftSamples(Convert.ToUInt32(1 + module.ModuleArrayIndex), Convert.ToDouble(actualSampleRate), Convert.ToUInt32(aafilter), triggerSampleNumber);
module.TriggerSampleNumbers[0] = triggerSampleNumber + phaseShift;
eventInfo.WasTriggered = module.TriggerSampleNumbers[0] > 0;
progress.Step();
module.StartRecordSampleNumber = 0;
if (module.RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBuffer
|| module.RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART
|| module.RecordingMode == DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode
|| module.RecordingMode == DFConstantsAndEnums.RecordingMode.a16_CircularBufferAndStreamSubSampleMode
|| module.RecordingMode == DFConstantsAndEnums.RecordingMode.RAMActive
|| module.RecordingMode == DFConstantsAndEnums.RecordingMode.MultipleEventRAMActive)
{
ulong preTriggerSamples = Convert.ToUInt64(System.Math.Abs(module.PreTriggerSeconds * module.SampleRateHz + 1D));
if (preTriggerSamples < module.TriggerSampleNumbers[0])
{
module.StartRecordSampleNumber = module.TriggerSampleNumbers[0] - preTriggerSamples;
}
}
else if (module.RecordingMode == DFConstantsAndEnums.RecordingMode.AutoActiveMode ||
module.RecordingMode == DFConstantsAndEnums.RecordingMode.AerospaceWithMotion)
{
ulong preTriggerSamples = Convert.ToUInt64(System.Math.Abs(module.PreTriggerSeconds * module.SampleRateHz + 1D));
if (preTriggerSamples < module.TriggerSampleNumbers[0])
{
module.StartRecordSampleNumber = eventStartRecordSampleNumber;
}
}
#region Get_PTP_Event_Timestamp
module.StartRecordTimestampSec = startRecordTimestampSec;
module.TriggerTimestampSec = triggerTimestampSec;
module.StartRecordTimestampNanoSec = startRecordTimestampNanoSec;
module.TriggerTimestampNanoSec = triggerTimestampNanoSec;
module.PTPMasterSync = pTPMasterSync;
#endregion
#region Slice6TiltSensor
var tiltSensorDataPre = new short[3];
module.TiltSensorAxisXDegreesPre = double.NaN;
module.TiltSensorAxisYDegreesPre = double.NaN;
module.TiltSensorAxisZDegreesPre = double.NaN;
module.TiltSensorAxisXDegreesPost = double.NaN;
module.TiltSensorAxisYDegreesPost = double.NaN;
module.TiltSensorAxisZDegreesPost = double.NaN;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InSliceTiltSensorADCPre))
{
// Get the saved values from the final pre - test query(if any)
try
{
var query = new QueryEventAttribute(this)
{
Key = AttributeTypes.ArmAndEventAttributes.tiltSensorPreEventADC
};
query.SyncExecute();
tiltSensorDataPre = query.Value as short[];
}
catch (System.Exception ex) { APILogger.Log(ex); }
var tiltSensorCals = new double[18];
for (int tiltCalKeyOffset = 0; tiltCalKeyOffset < 18; tiltCalKeyOffset++)
{
var qSA_BS6 = new QuerySystemAttribute_BridgeSlice6(this);
qSA_BS6.DeviceID = 1;
qSA_BS6.Key = (AttributeTypes.SystemAttributes_BridgeSlice6)((int)AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_1 + tiltCalKeyOffset);
qSA_BS6.SyncExecute();
tiltSensorCals[tiltCalKeyOffset] = (float)qSA_BS6.Value;
}
var tiltDataEU = Test.Module.GetTiltDegreesEU(tiltSensorDataPre, tiltSensorCals, module.TiltAxes, module.AxisIgnored, new float[] { (float)module.MountOffsetAxisOne, (float)module.MountOffsetAxisTwo });
module.TiltSensorAxisXDegreesPre = tiltDataEU[0];
module.TiltSensorAxisYDegreesPre = tiltDataEU[1];
module.TiltSensorAxisZDegreesPre = tiltDataEU[2];
}
#endregion
progress.Step();
// update the channel info
for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++)
{
var channel = (DASChannel)module.Channels[channelIdx];
channel.OwningModule = module;
channel.EventStartTime = eventStartTime;
if (channel is AnalogInputDASChannel analog)
{
if (analog.ScalefactorMilliVoltsPerADC != factors[currentChannel])
{
//2019-04-11 toyota boshoku flatline data issue
APILogger.Log(
$"{SerialNumber}:{analog.Number} has a mismatched XML scale factor vs attribute scale factor ({analog.ScalefactorMilliVoltsPerADC} vs {factors[currentChannel]})");
analog.ScalefactorMilliVoltsPerADC = factors[currentChannel];
}
}
// CGO
// This is a hacked change for now to support 00G8 and prior firmware
// in the 1.04 release of SLICEWare. This and the other CGO commented
// block of code need more general cleanup before 1.05.
if (null == levelTriggerOffsetCorrection)
{
channel.LevelTriggerT0AdjustmentSamples = 0;
}
else if (currentChannel < levelTriggerOffsetCorrection.Length)
{
channel.LevelTriggerT0AdjustmentSamples = levelTriggerOffsetCorrection[currentChannel];
}
else
{
channel.LevelTriggerT0AdjustmentSamples = 0;
}
if (null == levelTriggerSeen)
{
channel.LevelTriggerSeen = false;
}
else if (currentChannel < levelTriggerSeen.Length)
{
channel.LevelTriggerSeen = levelTriggerSeen[currentChannel];
}
else
{
channel.LevelTriggerSeen = false;
}
currentChannel++;
progress.Step();
}
// store it in the object
eventInfo.Modules[moduleIdx] = module;
}
if (0 > info.EventIndex)
{
// store it in the object
dlReport.Events[eventIdx] = eventInfo;
}
else { EventInfo.Events[eventIdx] = eventInfo; }
}
// now assigned the data to the public property
if (0 > info.EventIndex)
{
SetEventInfo(dlReport);
}
SetEventFaultFlags(faultFlags);
((IDownload)this)?.SetExtendedFaultFlags(extendedFaultFlags.ToArray());
SetEventGuids(eventGuids);
SetEventArmAttemps(armAttempts);
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
#endregion
#region Query downloaded status
void IDownloadActions.QueryDownloadedStatus(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.QueryDownloadedStatus", new WaitCallback(AsyncQueryDownloadedStatus), info);
}
private void AsyncQueryDownloadedStatus(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
var EventCount = new QueryTotalEventCount(this);
EventCount.SyncExecute();
var config = GetConfigAttributes(this);//new ConfigAttributes(this);
var progress = new QueryDownloadProgress(info, EventCount.Count + 1, 1);
var eventDownloadedStatus = new bool[EventCount.Count];
var eventGuids = new Guid[EventCount.Count];
for (int eventIdx = 0; eventIdx < EventCount.Count; eventIdx++)
{
eventDownloadedStatus[eventIdx] = config.EventHasBeenDownloaded(eventIdx, out uint flag); // I/O
progress.Step();
try
{
eventGuids[eventIdx] = GetEventGuid(eventIdx);
}
catch (Exception) { eventGuids[eventIdx] = Guid.NewGuid(); }
}
// now assigned the data to the public property
SetEventDownloadStatus(eventDownloadedStatus);
SetEventGuids(eventGuids);
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
#endregion
#region Set trigger sample numbers
void IDownloadActions.SetTriggerSampleNumbers(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.SetTriggerSampleNumbers", new WaitCallback(AsyncSetTriggerSampleNumbers), info);
}
private void AsyncSetTriggerSampleNumbers(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
if (EventInfo == null || EventInfo.Events == null || EventInfo.Events.Length == 0 ||
EventInfo.Events[0].Modules == null || EventInfo.Events[0].Modules.Length == 0)
{
info.Error("SetTriggerSampleNumbers: EventInfo, Events or Modules are null or empty");
return;
}
var config = GetConfigAttributes(this);//new ConfigAttributes(this);
for (int eventIdx = 0; eventIdx < EventInfo.Events.Length; eventIdx++)
{
if (EventInfo.Events[eventIdx].Modules != null && EventInfo.Events[eventIdx].Modules.Length > 0 &&
EventInfo.Events[eventIdx].Modules[0].TriggerSampleNumbers.Length > 0)
{
// we only support 1 trigger at this point
config.SetEventTriggerSampleNumber(eventIdx, EventInfo.Events[eventIdx].Modules[0].TriggerSampleNumbers[0]);
}
}
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
#endregion
#region Set downloaded
void IDownloadActions.SetDownloaded(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.SetDownloaded", new WaitCallback(AsyncSetDownloaded), info);
}
private void AsyncSetDownloaded(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
if (EventInfo == null || EventInfo.Events == null || EventInfo.Events.Length == 0 ||
EventInfo.Events[0].Modules == null || EventInfo.Events[0].Modules.Length == 0)
{
info.Error("SetDownloaded: EventInfo, Events or Modules are null or empty");
return;
}
var config = GetConfigAttributes(this);
for (var eventIdx = 0; eventIdx < EventInfo.Events.Length; eventIdx++)
{
if (EventInfo.Events[eventIdx].Modules != null && EventInfo.Events[eventIdx].Modules.Length > 0 &&
EventInfo.Events[eventIdx].Modules[0].TriggerSampleNumbers.Length > 0)
{
config.SetEventDownloaded(eventIdx, 1);
}
}
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
#endregion
}
}