using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using DTS.Common.Utilities.Logging;
using DTS.Common.Utilities;
using System.ComponentModel;
using DTS.Common.DASResource;
using DTS.Common.Enums;
using DTS.DASLib.Command.SLICE;
using DTS.Common.Interface.DASFactory;
using System.Threading.Tasks;
using DTS.Common.DAS.Concepts.DAS.Channel;
using DTS.Common.Enums.Sensors;
using DTS.Common.Classes.Connection;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.Common.Interface.DASFactory.ARM;
using DTS.Common.Interface.DASFactory.Download;
using DTS.Common.Interface.DASFactory.Config;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Enums.DASFactory;
using DTS.DASLib.Service.Interfaces;
using DTS.Common;
using System.IO.Ports;
using DTS.Common.Classes.DSP;
using DTS.Common.Enums.Hardware;
namespace DTS.DASLib.Service
{
#region ServiceBase class
public abstract class ServiceBase : IDisposable
{
//10285 Unhandled exception during diagnostics.
//the initial value for thread sleep is 50 millisecond.
//this value is configurable and it is introduced to prevent the unhandled excption during the diagnostic step in slow network.
private static int _checkUnitsIntervalMillisecond = 50;
public ServiceBase()
{
AggregateProgress = true;
}
public void Dispose()
{
}
public static void InitializeCheckUnitsInterval(int checkUnitsIntervalMillisecond)
{
_checkUnitsIntervalMillisecond = checkUnitsIntervalMillisecond;
}
~ServiceBase()
{
DASServiceLock.RunAllAvailable(this, null);
}
public class ServiceStartException : ApplicationException
{
public bool ServicesAreRunning { get; set; }
public ServiceStartException(Exception ex, bool running) : base("A unit failed to start", ex)
{
ServicesAreRunning = running;
}
}
public bool AggregateProgress { get; set; }
public class CallbackData
{
public enum CallbackStatus
{
Progress,
NewData,
AllFinished,
Success,
Failure,
Canceled
}
public CallbackStatus Status { get; set; }
public IDASCommunication Target { get; set; } // which one is the report for?
public string ErrorMessage { get; set; }
public Exception ErrorException { get; set; }
public int ProgressValue { get; set; }
public object UserData { get; set; }
public SampleData[] Data { get; set; }
public ServiceCallbackData.DiagnosticNewData NewDiagnosticData { get; set; }
public ServiceCallbackData.TiltNewData NewTiltData { get; set; }
public ServiceCallbackData.UARTNewData NewUARTData { get; set; }
public ServiceCallbackData.TemperatureData NewTemperatureData { get; set; }
public byte[] ByteData { get; set; }
public CallbackData()
{
Status = CallbackStatus.Failure; // just to be pessimistic
Target = null;
ErrorMessage = string.Empty;
ErrorException = null;
ProgressValue = 100;
UserData = null;
Data = null;
}
//copy constructor, added for
//13820 With the S6 ATD and a SLICE PRO system in the setup, consistently see Trigger Check Fail
public CallbackData(CallbackData copy)
{
Status = copy.Status;
Target = copy.Target;
ErrorMessage = copy.ErrorMessage;
ProgressValue = copy.ProgressValue;
UserData = copy.UserData;
Data = copy.Data;
}
}
public delegate void Callback(CallbackData data);
///
/// Delegate for service available event handler. A function of this form
/// should be defined to handle the ServiceAvailable event.
///
/// The object that fired the ServiceAvailable event.
/// Same object that comes back to the Service Callback. data.target
/// will be the target for the service that is done.
public delegate void ServiceBaseEventHandler(object sender, CallbackData data);
///
/// This event fires when a service is compeletly finished accessing the hardware.
/// Use this event to detirmine if the hardware is totally free for more services.
/// You must define a handler for this event in your program and add it to the
/// event list for this event. NOTE: Each service object will have its own instance
/// of this event.
///
public event ServiceBaseEventHandler ServiceAvailable;
///
/// Delegate for error event handler
///
///
///
///
public delegate void ServiceCallbackErrorEventHandler(object sender, string msg, Exception caught);
///
/// end the local event
///
public event ServiceCallbackErrorEventHandler ServiceCallbackError;
///
/// end the static event
///
public static event ServiceCallbackErrorEventHandler GlobalServiceCallbackError;
public abstract string ServiceName();
public static void VerifyGlobalDASUnitsEmpty()
{
DASServiceLock.VerifyEmpty();
}
public virtual void Cancel()
{
APILogger.LogString($"Entering {ServiceName()}.Cancel on thread {Thread.CurrentThread.ManagedThreadId}");
DASServiceLock.Cancel(this);
}
public virtual void ForceCancel()
{
APILogger.LogString(
$"Entering {ServiceName()}.ForceCancel on thread {Thread.CurrentThread.ManagedThreadId}");
DASServiceLock.ForceCancel(this);
}
#region Service glue functions
protected class ServiceGlueClass
{
public object UserObject { get; set; }
public Callback UserCallback { get; set; }
public ManualResetEvent AvailableEvent { get; set; }
public ServiceGlueClass(Callback userCallback, object userObject)
{
UserObject = userObject;
UserCallback = userCallback;
AvailableEvent = new ManualResetEvent(false);
}
}
protected void ServiceIsAvailable(object sender, CallbackData data)
{
ServiceAvailable -= ServiceIsAvailable;
(data.UserData as ServiceGlueClass)?.AvailableEvent.Set();
}
protected void ServiceCallback(CallbackData data)
{
if ((data.UserData as ServiceGlueClass)?.UserObject is CallbackData userData)
{
((ServiceGlueClass)data.UserData).UserCallback(userData);
}
}
#endregion
#region private members
protected class ServiceLocking
{
protected Dictionary> DASUnits = new Dictionary>();
protected readonly object DASUnitsLock = new object();
///
/// cancels without regard to unit progress [realtime progress status is ambiguous]
///
///
public void ForceCancel(ServiceBase service)
{
var managedId = Thread.CurrentThread.ManagedThreadId;
//obsolete, doesn't guarantee a stable thread id
try
{
lock (DASUnitsLock)
{
if (!DASUnits.Keys.Contains(service)) { return; }
foreach (var unit in DASUnits[service])
{
try
{
// this call is actually going straight to Communication
(unit.Unit as IDownloadActions)?.ForceCancel();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
DASUnits[service].Clear();
}
}
catch (SynchronizationLockException)
{
// somehow .NET forgets who owns this Monitor!
var managedIdNow = Thread.CurrentThread.ManagedThreadId;
var msg = $"ServiceLocking.Cancel: caught SynchronizationLockException at start, Managed({managedId}), at exception Managed({managedIdNow})";
APILogger.LogString(msg);
}
}
public void Cancel(ServiceBase service)
{
var managedId = Thread.CurrentThread.ManagedThreadId;
try
{
lock (DASUnitsLock)
{
if (!DASUnits.Keys.Contains(service)) { return; }
foreach (var unit in DASUnits[service])
{
if (unit.Progress == null || unit.Unit == null) continue;
if (!unit.Progress.IsFinished(unit.Unit))
{
// this call is actually going straight to Communication
(unit.Unit as IDownloadActions)?.Cancel();
}
}
}
}
catch (SynchronizationLockException)
{
// somehow .NET forgets who owns this Monitor!
// yes, GetCurrentThreadId is obsolete ... managedid should be sufficient
//http://go.microsoft.com/fwlink/?linkid=14202
var managedIdNow = Thread.CurrentThread.ManagedThreadId;
var msg = $"ServiceLocking.Cancel: caught SynchronizationLockException at start, Managed({managedId}), at exception Managed({managedIdNow})";
APILogger.LogString(msg);
}
}
public void ClearCancel(ServiceBase service)
{
lock (DASUnitsLock)
{
if (DASUnits.Keys.Contains(service))
{
foreach (var unit in DASUnits[service])
{
// this call is actually going straight to Communication
(unit.Unit as IDownloadActions)?.ClearCancel();
}
}
}
}
protected void CheckUnitsAvailable(ServiceBase service,
List units,
string caller)
{
var bPass = false;
var iCount = 0;
while (!bPass && iCount < 10)
{
//
// Monopolize the DASUnits data structure before we try to access it. Recall that the DASUnits
// member is a ServiceBase-keyed dictionary that maps to Lists of ServiceBaseCallbackData structures.
//
lock (DASUnitsLock)
{
bPass = true;
// do we have this service?
if (DASUnits.Keys.Contains(service))
{
// yes, check if it's empty
if (DASUnits[service].Count != 0)
{
// it's busy
var oldUnits = DASUnits[service].Select(u => u.Unit.ToString()).ToArray();
var st = new StackTrace(true);
APILogger.LogString(
$"SetUnitsBusy.Local: {caller} wants access on thread {Thread.CurrentThread.ManagedThreadId} with units {ArrayToString.ArrayObjectToString(units)}, " +
$"but this service is busy doing {DASUnits[service][0].CurrentService} on units {ArrayToString.ArrayObjectToString(oldUnits)}. Stacktrace: {st}");
bPass = false;
}
}
else
{
// no, not the service. check if we have any of the units
foreach (var pair in DASUnits)
{
foreach (var sbcbd in pair.Value.Where(x => units.Contains(x.Unit)))
{
APILogger.LogString(
$"SetUnitsBusy.Global: {caller} wants access on thread {Thread.CurrentThread.ManagedThreadId} with units {ArrayToString.ArrayObjectToString(units)}, " +
$"but one or more units are busy in {sbcbd.CurrentService}");
bPass = false;
}
}
}
}
iCount++;
if (!bPass) { Application.DoEvents(); Thread.Sleep(_checkUnitsIntervalMillisecond); }
}
if (!bPass)
{
throw new BusyException("Timing/threading error in application, check log for more details");
}
}
///
/// returns true if the unit can be soft disconnected
///
///
///
private bool CanSoftDisconnect(IDASCommunication unit)
{
switch (unit)
{
case EthernetSlice6 _:
case EthernetSlice6Air _:
case EthernetSlice6AirBridge _:
case EthernetSlice2 _:
case EthernetTsrAir _:
return true;
}
return false;
}
///
/// soft connect the units (if they support it)
///
///
public void SoftConnectUnits(IDASCommunication[] units)
{
if (!HardwareConstants.AllowSoftDisconnects) { return; }
var allTasks = new List();
foreach (var unit in units)
{
var process = CanSoftDisconnect(unit);
if (!process)
{
continue;
} //don't try to disconnect this unit ...
if (!(unit is ICommunication communication))
{
continue;
}
allTasks.Add(Task.Run(() =>
{
if (communication.Transport.IsSoftDisconnected)
{
communication.Transport.SoftConnect();
communication.SetupReader();
}
}));
}
if (allTasks.Any())
{
Task.WaitAll(allTasks.ToArray());
}
}
public void SoftDisconnectUnit(IDASCommunication unit)
{
var process = CanSoftDisconnect(unit);
if (!process) { return; }
if (!(unit is ICommunication communication))
{
return;
}
communication.Transport.SoftDisconnect();
}
///
/// soft disconnects the units if they support it
///
///
public void SoftDisconnectUnits(List units)
{
if (!HardwareConstants.AllowSoftDisconnects) { return; }
var allTasks = new List();
foreach (var unit in units)
{
var process = CanSoftDisconnect(unit);
if (!process) { continue; }
if (!(unit is ICommunication communication))
{
continue;
}
allTasks.Add(Task.Run(() => { communication.Transport.SoftDisconnect(); }));
}
if (allTasks.Any())
{
Task.WaitAll(allTasks.ToArray());
}
}
public delegate void ServiceStartDelegate(ServiceBaseCallbackData data);
///
/// 16231 Hardware display and Silent crash in Test Setup Discover Hardware
/// this is just a helper class that would allow a consumer to uniquely handle this exception easily
///
public class DuplicateSerialNumbersException : Exception
{
public const string DUPLICATE_SERIALS_TEXT = "Duplicate serials detected among DAS";
}
public void SetUnitsBusy(ServiceBase service,
List units,
Callback callback,
object callbackData,
string caller,
ServiceStartDelegate serviceStart)
{
try
{
//16231 Hardware display and Silent crash in Test Setup Discover Hardware
//there shouldn't be any duplicate serial numbers among the units being passed into the service
//fail the service if there are in a predictable way
var duplicates = units.GroupBy(u => u.SerialNumber).Any(g => g.Count() > 1);
if (duplicates)
{
Task.Run(() =>
{
foreach (var unit in units)
{
callback?.Invoke(new CallbackData()
{
Target = unit,
Status = CallbackData.CallbackStatus.Failure,
ErrorMessage = DuplicateSerialNumbersException.DUPLICATE_SERIALS_TEXT,
ErrorException = new DuplicateSerialNumbersException()
});
APILogger.Log($"service: {service.ServiceName()} - Serial number: {unit.SerialNumber} appears multiple times, failing service");
}
callback?.Invoke(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
});
return;
}
// is service or units busy?
CheckUnitsAvailable(service, units, caller);
lock (DASUnitsLock)
{
// no, it's good, add them
var progress = new ProgressAccumulator(units);
if (!DASUnits.Keys.Contains(service))
{
DASUnits.Add(service, new List(units.Count));
}
var idasToCallback = new Dictionary();
var serialToDASLookup = units.ToDictionary(u => u.SerialNumber);
var distributors = units.Where(u => u.IsSlice6Distributor());
foreach (var unit in units)
{
var data = new ServiceBaseCallbackData(unit, progress, callback, callbackData, caller);
DASUnits[service].Add(data);
idasToCallback[unit] = data;
}
//manages starting work on units to try to control and optimize connections
Task.Run(() => CreateWorkThreads(service, units, callback, callbackData, caller,
serviceStart, idasToCallback)).ContinueWith((t) =>
{
if (null != t.Exception)
{
//if we get here we failed to create the work threads, the service didn't start, mark all failed
//16062 crash on arm step
//FB16238: refactored to use the service's MyCallback. The passed callback will be called from there.
foreach (var unit in units)
{
service.MyCallback(new ServiceCallbackData()
{
Status = ServiceCallbackData.CallbackStatus.Failure,
ErrorException = t.Exception,
ErrorMessage = t.Exception.Message,
UserData = new ServiceBaseCallbackData(unit, progress, callback, callbackData, caller)
});
}
}
}).ConfigureAwait(false);
}
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
///
/// this function handles proportioning out serviceStart calls to units
///
///
///
///
///
///
///
///
private void CreateWorkThreads(ServiceBase service,
List units,
Callback callback,
object callbackData,
string caller,
ServiceStartDelegate serviceStart,
IDictionary dataLookup)
{
Parallel.ForEach(units, unit => { serviceStart(dataLookup[unit]); });
Thread.Sleep(UNITS_START_CHECK_INTERVAL);
}
///
/// the thread that is spinning off calls to units iterates looking to call startService
/// this controls how long in ms between each iteration
///
private const int UNITS_START_CHECK_INTERVAL = 20;
public void RunAllAvailable(ServiceBase service, Exception startException)
{
try
{
var currentServiceName = "";
object userCallbackData = null;
lock (DASUnitsLock)
{
if (service == null || !DASUnits.Keys.Contains(service) || DASUnits[service] == null || DASUnits[service].Count == 0)
{
return;
}
currentServiceName = DASUnits[service][0].CurrentService;
userCallbackData = DASUnits[service][0].CallbackObject;
// remove the ServiceBaseCallbackData (each unit is now available to use again)
DASUnits[service].Clear();
// remove the service (it is now available to use again)
DASUnits.Remove(service);
}
APILogger.LogString($"{currentServiceName}: All units are now finished, calling ServiceAvailable");
// notify our subscribers
if (service.ServiceAvailable == null) return;
var cbData = new CallbackData();
cbData.UserData = userCallbackData;
if (startException != null)
{
APILogger.Log(currentServiceName,
": service available startexception exists: ", startException, startException.Message);
cbData.ErrorException = startException;
cbData.ErrorMessage = startException.Message;
cbData.Status = CallbackData.CallbackStatus.Failure;
}
else
{
cbData.Status = CallbackData.CallbackStatus.AllFinished;
}
APILogger.Log(currentServiceName, ": calling service available");
service.ServiceAvailable(service, cbData);
}
catch (Exception ex)
{
service?.CallbackExceptionReporting(ex, "ServiceAvailable", "???");
}
}
public void VerifyEmpty()
{
}
}
protected static ServiceLocking DASServiceLock = new ServiceLocking();
protected class ServiceBaseCallbackData
{
public IDASCommunication Unit { get; set; }
public Callback Callback { get; set; }
public object CallbackObject { get; set; }
public ProgressAccumulator Progress { get; set; }
public string CurrentService { get; set; }
public ServiceBaseCallbackData(IDASCommunication unit,
ProgressAccumulator progress,
Callback callback,
object callbackObject,
string currentService)
{
Unit = unit;
Progress = progress;
Callback = callback;
CallbackObject = callbackObject;
CurrentService = currentService;
}
}
protected class ProgressAccumulator
{
protected class IntBool
{
public int Progress { get; set; }
public bool Finished { get; set; }
public IntBool()
{
Progress = 0;
Finished = false;
}
}
protected Dictionary OurUnits;
protected readonly object DicLock = new object();
public int Current { get; set; }
public int NumRemaining { get; set; }
public ProgressAccumulator(List units)
{
OurUnits = new Dictionary(units.Count);
foreach (var com in units)
{
if (!OurUnits.ContainsKey(com))
{
OurUnits.Add(com, new IntBool());
}
}
Current = 0;
NumRemaining = units.Count;
}
public int Update(IDASCommunication com, int newValue)
{
if (newValue < 0 || newValue > 100)
{
// "ProgressAccumulator: new value must be between 0 and 100"
APILogger.Log("ProgressAccumulator: new value is not between 0 and 100 ", newValue, com.SerialNumber);
newValue = newValue > 100 ? 100 : 0;
}
lock (DicLock)
{
OurUnits[com].Progress = newValue;
Current = OurUnits.Sum(pair => pair.Value.Progress) / OurUnits.Count;
}
return Current;
}
public int Finished(IDASCommunication com)
{
lock (DicLock)
{
OurUnits[com].Finished = true;
NumRemaining = OurUnits.Count(pair => !pair.Value.Finished);
}
return NumRemaining;
}
public bool IsFinished(IDASCommunication com)
{
lock (DicLock)
{
return OurUnits[com].Finished;
}
}
}
///
/// Verify that there ARE units to be configured, that the configuration callbacks aren't
/// null, etc.
///
///
///
/// The units to be verified as "configuration"-able.
///
///
///
/// The to be invoked upon configuration
/// service completion.
///
///
protected virtual void Verify(List units, Callback callback)
{
if (units == null || units.Count == 0)
{
// "ServiceBase: Parameter Units can't be null or empty"
throw new ArgumentException(Strings.ServiceBase_Verify_Err1);
}
if (callback == null)
{
// "ServiceBase: The callback parameter can't be null"
throw new ArgumentException(Strings.ServiceBase_Verify_Err2);
}
}
protected virtual bool LogService => true;
private DateTime _serviceStart = DateTime.MinValue;
protected void LogServiceStart(string serviceName, List units)
{
_serviceStart = DateTime.Now;
APILogger.LogString(
$"Entering {serviceName} on thread {Thread.CurrentThread.ManagedThreadId} with units: {ArrayToString.ArrayObjectToString(units)}{Environment.NewLine}{GetCaller()}");
}
protected virtual string GetCaller()
{
var callFrame = new StackFrame(3, true);
var callMeth = callFrame.GetMethod();
var callInfo =
$"Called from: {callMeth.DeclaringType?.Name}.{callMeth.Name} Line: {callFrame.GetFileLineNumber()}";
return callInfo;
}
private readonly object _myCallbackLock = new object();
internal void CallbackExceptionReporting(Exception ex,
string callbackName,
string status)
{
try
{
var errMsg =
$"Service {ServiceName()} caught an exception in the {callbackName} callback, Status: {status}";
APILogger.Log(errMsg, ex);
if (ServiceCallbackError != null)
{
ServiceCallbackError(this, errMsg, ex);
}
else if (GlobalServiceCallbackError != null)
{
GlobalServiceCallbackError(this, errMsg, ex);
}
else
{
Debug.Assert(false, "No GlobalServiceCallbackError event registered");
}
}
catch (Exception exx)
{
Debug.Assert(false, "Exception in the ServiceCallbackError event! Message: " + exx.Message);
}
}
internal virtual void MyCallback(ServiceCallbackData data)
{
lock (_myCallbackLock)
{
var thisUnit = data.UserData as ServiceBaseCallbackData;
// handle it...
var cbData = new CallbackData();
cbData.Target = thisUnit?.Unit;
cbData.UserData = thisUnit?.CallbackObject;
switch (data.Status)
{
case ServiceCallbackData.CallbackStatus.Success:
APILogger.Log($"{thisUnit.Unit.ToString()} has completed service {ServiceName()}");
cbData.Status = CallbackData.CallbackStatus.Success;
thisUnit?.Progress.Finished(thisUnit.Unit);
DASServiceLock.SoftDisconnectUnit(thisUnit.Unit);
break;
case ServiceCallbackData.CallbackStatus.Failure:
cbData.Status = CallbackData.CallbackStatus.Failure;
cbData.ErrorMessage = data.ErrorMessage;
cbData.ErrorException = data.ErrorException;
thisUnit?.Progress.Finished(thisUnit.Unit);
DASServiceLock.SoftDisconnectUnit(thisUnit.Unit);
break;
case ServiceCallbackData.CallbackStatus.InvalidParameters:
case ServiceCallbackData.CallbackStatus.Timeout:
cbData.Status = CallbackData.CallbackStatus.Failure;
cbData.ErrorMessage = data.ErrorMessage;
thisUnit?.Progress.Finished(thisUnit.Unit);
DASServiceLock.SoftDisconnectUnit(thisUnit.Unit);
break;
case ServiceCallbackData.CallbackStatus.ProgressReport:
if (AggregateProgress)
{
cbData.ProgressValue = thisUnit.Progress.Update(thisUnit.Unit, data.ProgressValue);
}
else
{
cbData.ProgressValue = data.ProgressValue;
}
cbData.Status = CallbackData.CallbackStatus.Progress;
break;
case ServiceCallbackData.CallbackStatus.Canceled:
thisUnit?.Progress.Finished(thisUnit.Unit);
cbData.Status = CallbackData.CallbackStatus.Canceled;
DASServiceLock.SoftDisconnectUnit(thisUnit.Unit);
break;
case ServiceCallbackData.CallbackStatus.NewData:
cbData.Status = CallbackData.CallbackStatus.NewData;
cbData.Data = data.DataSamples;
cbData.NewDiagnosticData = data.NewDiagnosticData;
cbData.NewTiltData = data.NewTiltData;
cbData.NewUARTData = data.NewUARTData;
cbData.ByteData = data.ByteData;
cbData.NewTemperatureData = data.NewTemperatureData;
break;
default:
cbData.Status = CallbackData.CallbackStatus.Failure;
// "ServiceBase: Unknown status from unit {0}"
cbData.ErrorMessage = string.Format(Strings.ServiceBase_MyCallback_Err1, thisUnit?.Unit);
DASServiceLock.SoftDisconnectUnit(thisUnit.Unit);
break;
}
var atAllFinishedCallback = false;
try
{
if (thisUnit?.Callback != null)
{
thisUnit.Callback(cbData);
// if NumRemaining is 0, then they're all done
if (null != thisUnit.Progress && thisUnit.Progress.NumRemaining == 0)
{
var cbDataClone = new CallbackData();
cbDataClone.Status = CallbackData.CallbackStatus.AllFinished;
cbDataClone.UserData = thisUnit.CallbackObject;
atAllFinishedCallback = true;
thisUnit.Callback(cbDataClone);
// cancel the cancel
DASServiceLock.ClearCancel(this);
}
}
}
catch (Exception ex)
{
CallbackExceptionReporting(ex, atAllFinishedCallback ? "AllFinished" : data.Status.ToString(), data.Status.ToString());
}
finally
{
// if we're completely done, we empty the list so this service can be reused
if (thisUnit.Progress.NumRemaining == 0)
{
try
{
if (LogService)
{
APILogger.Log(
$"{ServiceName()} finished - total time ({(uint)DateTime.Now.Subtract(_serviceStart).TotalMinutes}m {DateTime.Now.Subtract(_serviceStart).Seconds}s {DateTime.Now.Subtract(_serviceStart).Milliseconds}ms)");
}
}
catch (Exception)
{
// ignored as when we are unable to log we want to continue on, the log statement is just informational
}
// remove them from global list and call ServiceAvailable
DASServiceLock.RunAllAvailable(this, null);
}
}
}
}
#endregion
}
#endregion
#region Configure service
public class ConfigurationService : ServiceBase
{
///
/// Return the name of this service.
///
///
///
/// The name for this service.
///
///
public override string ServiceName()
{
return "ConfigurationService";
}
public void SetFirstUseDate(List units, DateTime date, Callback callback, object callbackData)
{
LogServiceStart("ConfigurationService.SetFirstUseDate", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.SetFirstUseDate",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).SetFirstUseDate(date, MyCallback, data);
});
}
public void StoreTestSetupXML(List units, Callback callback, object callbackData, string testSetupXML)
{
LogServiceStart("ConfigurationService.StoreTestSetupXML", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.StoreTestSetupXML",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).StoreTestSetupXML(MyCallback, data, testSetupXML);
});
}
public void CheckAAFilterRate(List units, Callback callback, object callbackData)
{
LogServiceStart("ConfigurationService.CheckAAFilterRate", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.CheckAAFilterRate",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).CheckAAFilterRate(MyCallback, data);
});
}
///
/// ensures that trigger line is reset for all units
///
///
///
///
public void ResetHardwareLines(List units, Callback callback, object callbackData)
{
LogServiceStart("ConfigurationService.ResetHardwareLines", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.ResetHardwareLines",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).ResetHardwareLines(MyCallback, data);
});
}
///
/// Check the specified units to ensure that they have properly constructed ConfigData objects.
/// The units themselves will perform their own checks since the notion of a valid configuration can
/// vary from unit type to unit type, but generally things like null tests IDs, zero-lengthed module
/// lists, bad sample rates, trigger times, etc., will result in a thrown exception explaining why
/// the particular module's configuation isn't up to snuff.
///
///
///
/// The list of s to be configuration-checked.
///
///
///
/// Setting this parameter to True will result in the check being pickier
/// (i.e., excepting on null/empty engineering unit properties, etc.).
///
///
public void ConfigurationValidEx(List units, bool doStrictCheck, ErrorCallback errorCallback)
{
foreach (var unit in units)
{
// now let the unit make a deeper analysis of the config data
(unit as IConfigurationActions)?.VerifyConfig(doStrictCheck, errorCallback);
}
}
///
/// for DAS that support setting channel types and auto detecting attached sensor types
/// this sets the channels back into auto detect configuration and out of a forced
/// configuration.
/// an example of this is SLICE 2 which supports forced IEPE and forced bridge (and autodect)
/// this sets the bridge type and queries each channel for channel type and stores the result in ConfigData channels
/// [(c as AnalogInputDASChannel).IEPEChannel]
/// all DAS can call this action without error even if they can't autodetect channel types
/// Requires that ConfigData is already present to function properly
///
///
///
///
public void AutoDetect(List units,
bool bRequeryConfiguration,
Callback callback,
object callbackData)
{
if (SensorConstants.DisableAutoSense)
{
callback(new CallbackData() { UserData = callbackData, Status = CallbackData.CallbackStatus.AllFinished });
return;
}
LogServiceStart("ConfigurationService.AutoDetect", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.AutoDetect",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).AutoDetect(bRequeryConfiguration, MyCallback, data);
});
}
public void ApplyLevelTriggers(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.ApplyLevelTriggers", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.ApplyLevelTriggers",
delegate (ServiceBaseCallbackData data)
{
((IConfigurationActions)data.Unit).ApplyLevelTriggers(MyCallback, data);
});
}
public void SetConfiguration(List units,
bool doStrictCheck,
bool eventConfig,
Callback callback,
object callbackData,
ErrorCallback errorCallback,
bool dummyConfig,
double [] maxAaf,
bool configureDigitalOutputs,
bool turnOffAAFRealtime,
IStreamingFilterProfile dspFilterType,
bool discardDiagnostics,
Dictionary timeChannelIds = null,
Dictionary dataChannelIds = null,
Dictionary streamProfiles = null,
Dictionary streamADCPerPacket = null,
Dictionary irigTDPIntervals = null,
Dictionary addresses = null,
Dictionary tmnsConfigs = null,
Dictionary baudRates = null,
Dictionary dataBits = null,
Dictionary stopBits = null,
Dictionary parities = null,
Dictionary flowControls = null,
Dictionary dataFormats = null,
Dictionary tmatsIntervals = null
)
{
LogServiceStart("ConfigurationService.SetConfiguration", units);
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
//SetUnitsBusy ordinarily connect to units, however ConfigurationValidEx sometimes
//talks to the units ahead of SetUnitsBusy, so move the connect up ahead of time ...
DASServiceLock.SoftConnectUnits(units.ToArray());
// Verify that the unit configuration structures contain acceptably-initialized
// configuration values.
ConfigurationValidEx(units, doStrictCheck, errorCallback);
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.SetConfiguration",
delegate (ServiceBaseCallbackData data)
{
uint crc = 0;
(data.Unit as IConfigurationActions)?.Configure(MyCallback, data, eventConfig, dummyConfig, maxAaf, configureDigitalOutputs, crc,
turnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(data.Unit),
dspFilterType, discardDiagnostics,
timeChannelIds, dataChannelIds, streamProfiles, streamADCPerPacket, irigTDPIntervals, addresses, tmnsConfigs, baudRates, dataBits, stopBits,
parities, flowControls, dataFormats, tmatsIntervals);
});
}
public void SetClocks(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.SetClocks", units);
// only set on supporting units
units = (from das in units where das is IClockSyncActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
var cbObjects = (object[])callbackData;
var dasClockMasters = (Dictionary)cbObjects[0];
var clockSyncProfileMaster = (ClockSyncProfile)cbObjects[1];
var clockSyncProfileSlave = (ClockSyncProfile)cbObjects[2];
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.SetClocks",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IClockSyncActions)?.SetClockSyncConfig(MyCallback, data, dasClockMasters[data.Unit.SerialNumber] ? clockSyncProfileMaster : clockSyncProfileSlave);
});
}
public void SetPTPDomain(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.SetClocks", units);
// only set on supporting units
units = (from das in units where das is IClockSyncActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
var domainIDs = (Dictionary)callbackData;
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.SetPTPDomain",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IClockSyncActions)?.SetPTPDomainID(MyCallback, data, domainIDs[data.Unit.SerialNumber]);
});
}
public void GetUARTSettings(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.GetUARTSettings", units);
// only set on supporting units
units = (from das in units where das is IUARTDownloadActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetUARTSettings",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IUARTDownloadActions)?.GetUARTSettings(MyCallback, data);
});
}
public void SetUARTSettings(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.SetUARTSettings", units);
// only set on supporting units
units = (from das in units where das is IUARTDownloadActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
var cbObjects = (object[])callbackData;
var baudRates = (Dictionary)cbObjects[0];
var dataBits = (Dictionary)cbObjects[1];
var stopBits = (Dictionary)cbObjects[2];
var parities = (Dictionary)cbObjects[3];
var flowControls = (Dictionary)cbObjects[4];
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.SetUARTSettings",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IUARTDownloadActions)?.SetUARTSettings(MyCallback, data, baudRates[data.Unit],
dataBits[data.Unit],
(uint)stopBits[data.Unit],
(uint)parities[data.Unit],
(uint)flowControls[data.Unit]);
});
}
public static double NumberPerBucket = 4D;
public void GetTestSetup(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.GetTestSetup", units);
// verify the data, types and callback
Verify(units, callback);
foreach (var das in units)
{
das.ConfigureHasBeenRun = false;
}
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetTestSetup",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.QueryTestSetup(MyCallback, data);
});
}
public void UpdateConfigurationFromFile(List units,
IReadOnlyDictionary dasToFile,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.UpdateConfigurationFromFile", units);
// verify the data, types and callback
Verify(units, callback);
foreach (var das in units)
{
das.ConfigureHasBeenRun = false;
}
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetConfiguration",
delegate (ServiceBaseCallbackData data)
{
var strConfig = string.Empty;
(data.Unit as IConfigurationActions)?.UpdateConfigurationFromFile(MyCallback, data,
dasToFile[data.Unit]);
});
}
public void GetConfiguration(List units,
bool bReadIds,
Callback callback,
object callbackData,
bool bDeviceScaleFactors = true,
bool differentModuleCountsAreOK = false)
{
LogServiceStart("ConfigurationService.GetConfiguration", units);
// verify the data, types and callback
Verify(units, callback);
foreach (var das in units)
{
das.ConfigureHasBeenRun = false;
}
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetConfiguration",
delegate (ServiceBaseCallbackData data)
{
uint crc = 0;
var strConfig = string.Empty;
(data.Unit as IConfigurationActions)?.QueryConfiguration(MyCallback, data, crc, strConfig, bReadIds, bDeviceScaleFactors,
differentModuleCountsAreOK);
});
}
public void GetClocks(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.GetClocks", units);
// only fetch from supporting units
units = (from das in units where das is IClockSyncActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetClocks",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IClockSyncActions)?.GetClockSyncStatus(MyCallback, data);
});
}
public void GetPTPDomain(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.GetPTPDomain", units);
// only fetch from supporting units
units = (from das in units where das is IClockSyncActions select das).ToList();
// Verify the data, types and callback. Specifically, ensure that there ARE units
// and that they have the interfaces to support configuration service operations,
// and that the configuration service callback exists.
Verify(units, callback);
// Mark these units as busy so the configuration service can work in peace.
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.GetPTPDomain",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IClockSyncActions)?.GetPTPDomainID(MyCallback, data);
});
}
public void UpdateIDs(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.UpdateIDs", units);
// verify the data, types and callback
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.UpdateIDs",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.UpdateIDs(MyCallback, data);
});
}
// other safetyswitch routines appear to have looping behavior
//this just checks for any units in arm position as a one time check
public void CheckSafetySwitchSafe(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.CheckSafetySwitchSafe", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.CheckSafetySwitchSafe",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.CheckSafetyState(false, MyCallback, data);
});
}
// FB15335: Move UART and ClockProfile sets to RunTest -> Hardware NavStep, add Reboot
public void Reboot(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ConfigurationService.Reboot", units);
// verify the data, types and callback
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ConfigurationService.Reboot",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.Reboot(MyCallback, data);
});
}
#region private stuff
///
///
/// Verify that the specified units express the interfaces required to support the SLICEWare
/// concept of "configuration". Also make sure that there ARE units to be configured, that
/// the configuration callbacks aren't null, etc.
///
///
/// The units to be verified as "configuration"-able.
///
///
/// The to be invoked upon configuration
/// service completion.
///
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is IConfiguration))
{
// "ConfigurationService: Some member of Units isn't an IConfiguration"
throw new ArgumentException(Strings.ConfigurationService_Verify_Err1);
}
if (!(unit is IConfigurationActions))
{
// "ConfigurationService: Some member of Units isn't an IConfigurationActions"
throw new ArgumentException(Strings.ConfigurationService_Verify_Err2);
}
}
}
public void CheckArmSwitch(List units,
bool bArmed, Callback callback, object callbackData)
{
LogServiceStart("ConfigurationService.CheckArmSwitch", units);
Verify(units, callback);
var bHaveOneInWrongState = false;
var waitHandle = new ManualResetEvent(false);
var bHaveResistances = false;
var localCallback = new Callback(delegate (CallbackData localcallbackdata)
{
if (localcallbackdata.Status == CallbackData.CallbackStatus.Success)
{
//it's in the right state
}
else if (localcallbackdata.Status == CallbackData.CallbackStatus.Failure)
{
//note that resistances are only accurate while we set for armed, so if we get this error we must be switching to safe but are still in arm switch state
if (localcallbackdata.ErrorMessage == "SQUIB_RESISTANCE") { bHaveResistances = true; }
bHaveOneInWrongState = true;
}
else if (localcallbackdata.Status == CallbackData.CallbackStatus.AllFinished)
{
waitHandle.Set();
}
});
DASServiceLock.SetUnitsBusy(this,
units,
localCallback,
callbackData,
"ConfigurationService.CheckArmSwitch",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.CheckSafetyState(bArmed,
MyCallback,
data);
});
waitHandle.WaitOne();
if (bHaveOneInWrongState)
{
var data = new CallbackData();
data.Status = CallbackData.CallbackStatus.Failure;
if (bHaveResistances)
{
data.ErrorMessage = "SQUIB_RESISTANCE";
}
callback(data);
}
else
{
var dta = new CallbackData();
dta.Status = CallbackData.CallbackStatus.Success;
callback(dta);
}
var d = new CallbackData();
d.Status = CallbackData.CallbackStatus.AllFinished;
callback(d);
}
public delegate List GetActiveDevicesDelegate();
public void CheckSafetyState(List units,
bool bArmed, ManualResetEvent cancelEvent, GetActiveDevicesDelegate getActiveDevices, Callback callback, object callbackData)
{
Verify(units, callback);
// If we've never established configuration with the DAS the ConfigData will be null. This is used by
// the service to decide which modules are TOMs. It's safe to say, if we never established configuration
// we don't need to be responsible for confirming the switch state. Practically speaking, I believe the
// only time this happens is when you start a test, but get hung up on a validation error on the first screen
// If the list is empty, we're done.
if (!units.Any())
{
var info = new CallbackData();
info.Status = CallbackData.CallbackStatus.AllFinished;
callback(info);
return;
}
var bKeepGoing = true;
var bHaveOneInWrongState = false;
var bSafetySwitchException = false;
Exception ex = null;
while (bKeepGoing)
{
units = getActiveDevices();
if (!units.Any())
{
var info = new CallbackData();
info.Status = CallbackData.CallbackStatus.AllFinished;
callback(info);
return;
}
var waitHandle = new ManualResetEvent(false);
bHaveOneInWrongState = false;
var localCallback = new Callback(delegate (CallbackData localcallbackdata)
{
if (localcallbackdata.Status == CallbackData.CallbackStatus.Canceled || cancelEvent.WaitOne(1, false))
{
bHaveOneInWrongState = false;
bKeepGoing = false;
}
if (localcallbackdata.Status == CallbackData.CallbackStatus.Success)
{
//it's in the right state
}
else if (localcallbackdata.Status == CallbackData.CallbackStatus.Failure && !cancelEvent.WaitOne(0, false))
{
bHaveOneInWrongState = true;
if (localcallbackdata.ErrorMessage == "SAFETYSWITCH_EXCEPTION")
{
bSafetySwitchException = true;
ex = localcallbackdata.ErrorException;
}
}
else if (localcallbackdata.Status == CallbackData.CallbackStatus.AllFinished)
{
waitHandle.Set();
}
});
LogServiceStart("ConfigurationService.CheckSafetyState", units);
DASServiceLock.SetUnitsBusy(this,
units,
localCallback,
callbackData,
"ConfigurationService.CheckSafetyState",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.CheckSafetyState(bArmed,
MyCallback,
data);
});
waitHandle.WaitOne();
if (!bHaveOneInWrongState) { bKeepGoing = false; }
else
{
var data = new CallbackData();
data.Status = CallbackData.CallbackStatus.Failure;
if (bSafetySwitchException)
{
data.ErrorMessage = "SAFETYSWITCH_EXCEPTION";
data.ErrorException = ex;
}
callback(data);
}
}
if (bHaveOneInWrongState && !cancelEvent.WaitOne(1, false))
{
var data = new CallbackData();
data.Status = CallbackData.CallbackStatus.Failure;
callback(data);
}
else
{
var dta = new CallbackData();
dta.Status = CallbackData.CallbackStatus.Success;
callback(dta);
}
var d = new CallbackData();
d.Status = CallbackData.CallbackStatus.AllFinished;
callback(d);
}
#endregion
}
#endregion
#region Diagnostics service
public enum DiagnosticsStatusIndicatorState
{
[Description("GRN")]
Success,
[Description("RED")]
Failure,
[Description("YEL")]
Pending,
[Description("OFF")]
Off
}
///
/// Enumerated type to indicate pre-event or post-event Diagnostics results. Passed to
///
/// GetEventDiagnosticsResults
///
/// method.
///
public enum PrePostResults
{
PreEventDiagnosticsResult,
PostEventDiagnosticsResult
}
///
/// Diagnostics service provides a diagnostic interface to the DAS hardware.
/// Through it one may prepare and execute diagnostics as well as retrieve
/// diagnostics from past tests or events. Calling
/// Diagnose(...) will instruct
/// the hardware to perform diagnostics based on what values are set in the Channel Diagnostics array in
/// IDASCommunication.
///
public class DiagnosticsService : ServiceBase
{
public override string ServiceName()
{
return "DiagnosticsService";
}
/// Any members of the 'Units' parameter
/// do not implement the required Interface or are otherwise invalid.
/// Another service is already executing commands on the hardware.
/// The device has been disconnected from the machine.
/// There is a problem with the logging sub-system.
public void PrepareForBridgeResistanceMeasurement(List units,
uint diagnosticsSampleRateHz,
float diagnosticsAaFilterFrequencyHz,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.PrepareForBridgeResistanceMeasurement", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.PrepareForBridgeResistanceMeasurement",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PrepareForBridgeResistanceMeasurement(diagnosticsSampleRateHz,
diagnosticsAaFilterFrequencyHz,
MyCallback,
data);
});
}
public void PrepareForDiagnostics(List units,
PrePostResults whichResult,
IReadOnlyDictionary sampleRates,
IReadOnlyDictionary aafs,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.PrepareForCalibration", units);
Verify(units, callback);
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.PrepareForCalibration",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PrepareForDiagnostics(Convert.ToUInt32(sampleRates[data.Unit.SerialNumber]),
aafs[data.Unit.SerialNumber],
whichResult,
MyCallback,
data);
});
}
public void PrepareForDiagnostics(List units,
uint diagnosticsSampleRateHz,
float diagnosticsAaFilterFrequencyHz,
PrePostResults whichResult,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.PrepareForCalibration", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.PrepareForCalibration",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PrepareForDiagnostics(diagnosticsSampleRateHz,
diagnosticsAaFilterFrequencyHz,
whichResult,
MyCallback,
data);
});
}
public void PerformArmCheck(List units,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.PerformArmCheck", units);
Verify(units, callback);
//5211 Add check to ECM event line during ArmChecklist
//we need to do a trigger check dance per 5211 (if we are checking the lines)
var bNeedToRunTriggerCheck = units.Exists(u => u.ArmCheckActions.PerformEventLineCheck);
if (bNeedToRunTriggerCheck)
{
// since some of the units (SLICE have changed to using the ITriggerCheckActions trigger check, we have to clear IsTrigger, etc
foreach (var das in units)
{
if (null != das.DASArmStatus)
{
//14684 DataPRO crashed while running arm checklist in check trigger
//if we have an issue here, log the error and return
try
{
das.DASArmStatus.ClearTriggerCheckStatus();
}
catch (Exception ex)
{
APILogger.Log(ex);
//abandon the whole service and return
callback(new CallbackData()
{
Status = CallbackData.CallbackStatus.Failure,
Target = das
});
callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
return;
}
}
}
}
//there was concern that S6DBs were getting flipped during Start and affecting chained S6
//because of that we process the S6DB first, and then the S6 1.5 seconds later (by which time the pulse should alredy be complete)
var nonS6DBUnits = new List();
var s6DBUnits = new List();
foreach (var unit in units)
{
if (unit is EthernetSlice6DB)
{
s6DBUnits.Add(unit);
}
else
{
nonS6DBUnits.Add(unit);
}
}
var mreLocal = new ManualResetEvent(false);
//13820 With the S6 ATD and a SLICE PRO system in the setup, consistently see Trigger Check Fail
//what we do here is a bit complicated, we run PRE start on all units
//we run START on all units
//then we run POST start on all units
//14684 DataPRO crashed while running arm checklist in check trigger
//this is used to signal when an issue has occured in the process and we should return
//back to the consumer without further work
bool bAbandonService = false;
//intercept the final status for all calls, so that we don't accidently pass back all finished
//keep track of the final status seperately and pass them all back at once
//don't intercept new data or progress, just pass those through
var callback2 = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished:
mreLocal.Set();
break;
case CallbackData.CallbackStatus.Failure:
//this returns an error for the device under test to consumer
//and signals no additional processing should happen
bAbandonService = true;
callback(data);
break;
default:
callback(data);
break;
}
});
//another part of the 5211 trigger check dance, this is the dance to clear any existing
//triggers etc
if (bNeedToRunTriggerCheck)
{
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
units,
callback2,
units,
"TriggerCheckService.PreStartTriggerCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.PreStartTriggerCheck(MyCallback, data);
});
mreLocal.WaitOne();
//courtesy wait after completing PreStartTriggerCheck to avoid units are still busy
Thread.Sleep(TriggerCheckService.SERVICECOMPLETE_COURTESY_WAIT);
//5211 Add check to ECM event line during ArmChecklist
}
if (bAbandonService)
{
callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
return;
}
mreLocal.Reset();
//we have to do arm checks, and then finally PostTriggerCheckStart as documented in issue 5211
DASServiceLock.SetUnitsBusy(this,
units,
callback2,
units,
"CalibrationService.PerformArmCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PerformArmChecks(MyCallback, data);
});
mreLocal.WaitOne();
if (bAbandonService)
{
callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
return;
}
//courtesy wait after completing ArmChecks to avoid units are still busy
Thread.Sleep(TriggerCheckService.SERVICECOMPLETE_COURTESY_WAIT);
if (bNeedToRunTriggerCheck)
{
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
units,
callback2,
units,
"TriggerCheckService.PostStartTriggerCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.PostStartTriggerCheck(MyCallback, data);
});
mreLocal.WaitOne();
//finally, since some of the units (SLICE have changed to using the ITriggerCheckActions trigger check, we have to populate the armchecklistresult values for trigger check
foreach (var das in units)
{
if (null != das.DASArmStatus)
{
if (das.DASArmStatus.IsTriggerShorted)
{
das.ArmCheckResults.EventLineShorted = true;
}
if (das.DASArmStatus.IsStartShorted)
{
das.ArmCheckResults.StartLineShorted = true;
}
}
}
}
callback(new CallbackData { Status = CallbackData.CallbackStatus.AllFinished, UserData = units });
}
public void SaveTiltSensorDataPre(List units,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.SaveTiltSensorDataPre", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.SaveTiltSensorDataPre",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.SaveTiltSensorDataPre(MyCallback, data);
});
}
public void SaveTemperaturesPre(List units,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.SaveTemperaturesPre", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.SaveTemperature",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.SaveTemperaturesPre(MyCallback, data);
});
}
public void PerformVoltageCheck(List units,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.PerformVoltageCheck", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.PerformVoltageCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PerformVoltageCheck(MyCallback, data);
});
}
public void PerformVoltageCheckTaOnly(List units, Callback callback, object callbackData)
{
LogServiceStart("CalibrationService.PerformVoltageCheck", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.PerformVoltageCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.PerformVoltageCheckTAOnly(MyCallback, data);
});
}
public void MeasureBridge(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DiagnosticsService.MeasureBridge", units);
Verify(units, callback);
//first see if TOM Squib Check is on or not, if it is then make sure SafetySwitch is ARMED
var maxDelay = 0D;
var maxDuration = 0D;
foreach (var das in units)
{
foreach (var module in das.ConfigData.Modules)
{
if (!IsTom((DASModule)module)) { continue; }
foreach (var channel in module.Channels.Where(x => x is OutputSquibChannel))
{
var osc = channel as OutputSquibChannel;
if (!osc.IsConfigured()) { continue; }
if (osc.MeasurementType == SquibMeasurementType.CURRENT) { continue; }
maxDuration = Math.Max(maxDuration, osc.DurationMS);
maxDelay = Math.Max(maxDelay, osc.DelayMS);
}
}
}
//the TOM firecheck needs something to control it at a higher level than the tdas.service level, so we do it here
//intercepting the original callback and then passing data on to it when we are done.
var waitHandle = new ManualResetEvent(false);
var c2 = new Callback(delegate (CallbackData cbd)
{
if (cbd.Status == CallbackData.CallbackStatus.AllFinished)
{
waitHandle.Set();
}
else
{
callback(cbd);
}
});
LogServiceStart("CalibrationService.MeasureBridge", units);
DASServiceLock.SetUnitsBusy(this,
units,
c2,
callbackData,
"CalibrationService.MeasureBridge",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.GetBridgeMeasurement(MyCallback,
data);
});
while (!waitHandle.WaitOne(100, false))
{
Application.DoEvents();
}
waitHandle.Reset();
_ = new Callback(delegate (CallbackData cbd3)
{
if (cbd3.Status == CallbackData.CallbackStatus.AllFinished)
{
waitHandle.Set();
}
});
Thread.Sleep(100);
}
public void SetStatusIndicator(List units,
DiagnosticsStatusIndicatorState state,
Callback callback,
object callbackData)
{
Verify(units, callback);
LogServiceStart("CalibrationService.SetStatusIndicator", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.SetStatusIndicator",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.SetStatusIndicator(state,
MyCallback,
data);
});
}
///
/// clears any ILevelTriggerable:SampleAverageADC cached values
/// 14042 Flash Clear turns of excitation for s6
///
///
private void ClearLevelTriggerCache(IDASCommunication[] units)
{
if (!units.Any()) { return; }
Parallel.ForEach(units, (unit) =>
{
TriggerCheckService.ClearLevelTriggerCache(unit.ConfigData);
});
}
///
/// clears trigger lines of all das
/// originally part of 14463 Trigger shorted error during arm checklist in run test tile.
/// this is a 3 step process where the trigger out is set, time is waited, and then QATS is run
///
public void ClearDASTriggerLine(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DiagnosticsService.ClearDASTriggerLine", units);
var mre = new ManualResetEvent(false);
//step 1, set TriggerOut to all Units
DASServiceLock.SetUnitsBusy(this, units, delegate (CallbackData cbd)
{
switch (cbd.Status)
{
case CallbackData.CallbackStatus.AllFinished: mre.Set(); break;
}
}, callbackData,
"DiagnosticsService.ClearDASTriggerLine_SetTriggerOut",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.ClearTriggerOut(MyCallback, data);
});
mre.WaitOne();
//step 2, wait 1.7 seconds (min 1.5s) for fpga unlatch
//updated to 3 seconds now
Thread.Sleep(DFConstantsAndEnums.OneShotWaitTimeMs);
//step 3, run QATS again
mre.Reset();
DASServiceLock.SetUnitsBusy(this, units, delegate (CallbackData cbd)
{
switch (cbd.Status)
{
case CallbackData.CallbackStatus.AllFinished: mre.Set(); break;
}
}, callbackData,
"DiagnosticsService.ClearDASTriggerLine_QATS",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.ClearLatches(MyCallback, data);
});
mre.WaitOne();
//now send the original service all done out
callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
}
///
/// calculates the max duration and delay among squibs in the test
/// returns delay, duration, whether only TOM exists, and whether a TOM was present
///
private void CalculateMaxDurationDelay(List units,
out bool hasOnlyTdasTom, out double maxDuration, out double maxDelay,
out float diagnosticsAaFilterFrequencyHz, out uint diagnosticsSampleRateHz,
out bool haveTom)
{
maxDelay = 0D;
maxDuration = 0D;
diagnosticsAaFilterFrequencyHz = 2000F;
diagnosticsSampleRateHz = 10000;
haveTom = false;
hasOnlyTdasTom = true;
foreach (var das in units)
{
if (das.ConfigData == null || null == das.ChannelDiagnostics || !das.ChannelDiagnostics.Any()) continue;
var hasDiagnostics = new Dictionary();
foreach (var ch in das.ChannelDiagnostics)
{
if (ch.AllActionsDisabled()) { continue; }
hasDiagnostics[ch.DASChannelNumber] = true;
}
foreach (var module in das.ConfigData.Modules)
{
if (!IsTom((DASModule)module)) { continue; }
if (!(das is EthernetTDAS)) { hasOnlyTdasTom = false; }
foreach (var channel in module.Channels)
{
if (!(channel is OutputSquibChannel)) continue;
var osc = channel as OutputSquibChannel;
if (!osc.IsConfigured()) { continue; }
if (!hasDiagnostics.ContainsKey(osc.Number) || !hasDiagnostics[osc.Number]) continue;
//SLICE/TDAS TOM diagnostics in a different order?, that's what the +1 hack above is about
maxDuration = Math.Max(maxDuration, osc.DurationMS);
maxDelay = Math.Max(maxDelay, osc.DelayMS);
diagnosticsAaFilterFrequencyHz = osc.OwningModule.AAFilterRateHz;
diagnosticsSampleRateHz = osc.OwningModule.SampleRateHz;
haveTom = true;
}
}
}
}
///
/// adds ChannelDiagnosticResults for any DAS, since Diagnostics can be run for specific modules
/// must preserve any existing channel diagnostic results
///
///
private void InitializeConfigurationResults(List units)
{
foreach (var das in units)
{
var oldResults = new Dictionary();
if (null != das.ChannelDiagnosticsResults && das.ChannelDiagnosticsResults.Length > 0)
{
foreach (var r in das.ChannelDiagnosticsResults)
{
oldResults[r.DASChannelNumber] = r;
}
}
// prepare the result array
IDiagnosticResult[] results;
if (das.ChannelDiagnostics != null && das.ChannelDiagnostics.Length > 0)
{
results = new DiagnosticsResult[das.ChannelDiagnostics.Length];
for (var idx = 0; idx < das.ChannelDiagnostics.Length; idx++)
{
results[idx] = new DiagnosticsResult
{
DASChannelNumber = das.ChannelDiagnostics[idx].DASChannelNumber
};
if (oldResults.ContainsKey(results[idx].DASChannelNumber))
{
oldResults.Remove(results[idx].DASChannelNumber);
}
results[idx].EventNumber = DFConstantsAndEnums.EVENT_NUMBER_PRETEST_DIAG; // it's not really an event
}
}
else
{
// this case will only generate the base input values
results = new IDiagnosticResult[0];
}
var list = new List();
list.AddRange(oldResults.Values.ToArray());
if (null != results)
{
list.AddRange(results);
}
das.ChannelDiagnosticsResults = list.ToArray();
}
}
///
/// Call to execute hardware test as specified in
/// .ChannelDiagnostics in
/// IDASCommunication.
///
/// List of IDASCommunications,
/// the ChannelDiagnostics array in each dictating what test to be performed.
///
/// Callback function to be called by the service.
/// An object to pass along.
///
///
/// Any members of the 'Units' parameter
/// do not implement the required Interface or are otherwise invalid.
/// Another service is already executing commands on the hardware.
/// The device has been disconnected from the machine.
/// There is a problem with the logging sub-system.
public void Diagnos(List units,
int eventNumber,
PrePostResults whichResult,
Callback callback,
object callbackData,
int maxTimeMs)
{
var start = DateTime.Now;
LogServiceStart("DiagnosticsService.Diagnos", units);
Verify(units, callback);
InitializeConfigurationResults(units);
//clear any existing ILevelTriggerable cached SampleAverageADC
ClearLevelTriggerCache(units.ToArray());
var elapsed = DateTime.Now.Subtract(start);
if (elapsed.TotalMilliseconds > maxTimeMs)
{
foreach (var unit in units)
{
callback(new CallbackData { Status = CallbackData.CallbackStatus.Canceled, Target = unit, UserData = callbackData });
}
callback(new CallbackData { Status = CallbackData.CallbackStatus.AllFinished, UserData = callbackData });
return;
}
CalculateMaxDurationDelay(units, out var hasOnlyTdasTom, out var maxDuration, out var maxDelay, out var diagnosticsAaFilterFrequencyHz,
out var diagnosticsSampleRateHz, out var haveTom);
var failed = false;
if (haveTom && (whichResult == PrePostResults.PreEventDiagnosticsResult))
{
failed = SquibFireCheck(units, callback, callbackData, maxDuration, maxDelay, hasOnlyTdasTom, start, elapsed, maxTimeMs,
diagnosticsAaFilterFrequencyHz, diagnosticsSampleRateHz);
}
//the TOM firecheck needs something to control it at a higher level than the tdas.service level, so we do it here
//intercepting the original callback and then passing data on to it when we are done.
var waitHandle = new ManualResetEvent(false);
CallbackData originalCallbackData = null;
var c2 = new Callback(delegate (CallbackData cbd)
{
if (cbd.Status == CallbackData.CallbackStatus.AllFinished)
{
originalCallbackData = cbd; waitHandle.Set();
}
else
{
callback(cbd);
}
});
LogServiceStart("CalibrationService.DiagnosAndGetResults", units);
DASServiceLock.SetUnitsBusy(this,
units,
c2,
callbackData,
"CalibrationService.DiagnosAndGetResults",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.DiagnosAndGetResults(eventNumber,
whichResult,
MyCallback,
data);
});
while (!waitHandle.WaitOne(100, false))
{
Application.DoEvents();
elapsed = DateTime.Now.Subtract(start);
if (!(elapsed.TotalMilliseconds > maxTimeMs)) continue;
foreach (var unit in units)
{
callback(new CallbackData { Status = CallbackData.CallbackStatus.Canceled, Target = unit, UserData = callbackData });
}
Cancel();
return;
}
waitHandle.Reset();
callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished });
callback(originalCallbackData);
}
///
/// does the squib fire check portion of diagnostics, which involves
/// arming a unit in internal fire mode, triggering, downloading, and checking
/// the resulting duration and delay against what was desired
///
private bool SquibFireCheck(List units, Callback callback, object callbackData,
double maxDuration, double maxDelay, bool hasOnlyTdasTom, DateTime start, TimeSpan elapsed,
double maxTimeMs, float diagnosticsAaFilterFrequencyHz, uint diagnosticsSampleRateHz)
{
var bFailed = false;
var waitHandle = new ManualResetEvent(false);
var c3 = new Callback(delegate (CallbackData cbd3)
{
switch (cbd3.Status)
{
case CallbackData.CallbackStatus.Failure:
bFailed = true;
callback(new CallbackData { Status = CallbackData.CallbackStatus.Failure, Target = cbd3.Target, UserData = cbd3 });
break;
case CallbackData.CallbackStatus.AllFinished:
waitHandle.Set();
break;
}
});
//unlatch any latched SLICE ...
//14229 DataPRO crash when run diagnostics second time
//part of 5211 trigger check dance
//this will reset any ECM/S6DBs
//18715 Safety switch state should be checked before running diagnostics, not just before updating configuration
//don't continue with trigger check if there's any units in arm position
DASServiceLock.SetUnitsBusy(this,
units,
c3,
units,
"ConfigurationService.CheckSafetySwitchSafe",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IConfigurationActions)?.CheckSafetyState(false, MyCallback, data);
});
waitHandle.WaitOne();
Thread.Sleep(TriggerCheckService.SERVICECOMPLETE_COURTESY_WAIT);
//if we can't get the safetyswitchstate, don't continue
if (bFailed)
{
return bFailed;
}
waitHandle.Reset();
DASServiceLock.SetUnitsBusy(this,
units,
c3,
units,
"DiagnosticsService.ResetECMsSDBs",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.PreStartTriggerCheck(MyCallback, data);
});
waitHandle.WaitOne();
Thread.Sleep(TriggerCheckService.SERVICECOMPLETE_COURTESY_WAIT);
waitHandle.Reset();
//now handle any SLICE2/SLICE1.5/SLICE6?
DASServiceLock.SetUnitsBusy(this,
units,
c3,
units,
"DiagnosticsService.ResetDAS",
(ServiceBaseCallbackData data) =>
{
(data.Unit as IDiagnosticsActions).ClearDASTriggerLine(MyCallback, data);
});
Thread.Sleep(TriggerCheckService.SERVICECOMPLETE_COURTESY_WAIT);
waitHandle.WaitOne();
waitHandle.Reset();
LogServiceStart("CalibrationService.SquibFireCheckArm", units);
DASServiceLock.SetUnitsBusy(this,
units,
c3,
callbackData,
"CalibrationService.SquibFireCheckArm",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.SquibFireCheckArm(maxDelay, maxDuration, MyCallback, data);
});
waitHandle.WaitOne();
waitHandle.Reset();
if (bFailed)
{
return false;
}
c3 = delegate (CallbackData cbd3)
{
switch (cbd3.Status)
{
case CallbackData.CallbackStatus.Failure:
bFailed = true;
callback(new CallbackData
{
Status = CallbackData.CallbackStatus.Failure,
Target = cbd3.Target,
UserData = cbd3,
//needs to passback any exceptions
ErrorException = cbd3.ErrorException,
ErrorMessage = cbd3.ErrorMessage
});
break;
case CallbackData.CallbackStatus.AllFinished:
waitHandle.Set();
break;
}
};
//warmup TOMs for cap discharge - this may only need to be done for SPT, but it seems safest to always do this for now
var timeWaited = 0;
APILogger.Log("Starting TOM warmup");
while (timeWaited < RunTestVariables.TOMExcitationWarmupTimeMS)
{
elapsed = DateTime.Now.Subtract(start);
if (elapsed.TotalMilliseconds > maxTimeMs)
{
foreach (var unit in units)
{
callback(new CallbackData { Status = CallbackData.CallbackStatus.Canceled, Target = unit, UserData = callbackData });
}
return false;
}
Thread.Sleep(1000);
timeWaited += 1000;
}
APILogger.Log("Completed TOM warmup");
LogServiceStart("CalibrationService.TriggerCheckTrigger", units);
DASServiceLock.SetUnitsBusy(this,
units,
c3,
callbackData,
"CalibrationService.TriggerCheckTrigger",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.TriggerCheckTrigger(MyCallback, data);
});
waitHandle.WaitOne();
waitHandle.Reset();
if (bFailed)
{
return false;
}
if (!hasOnlyTdasTom) { Thread.Sleep(Convert.ToInt32(maxDelay + maxDuration + 2000D)); }
c3 = delegate (CallbackData cbd3)
{
switch (cbd3.Status)
{
case CallbackData.CallbackStatus.Success:
callback(cbd3);
break;
case CallbackData.CallbackStatus.Failure:
bFailed = true;
callback(new CallbackData { Status = CallbackData.CallbackStatus.Failure, Target = cbd3.Target, UserData = cbd3 });
break;
case CallbackData.CallbackStatus.AllFinished:
waitHandle.Set();
break;
}
};
Thread.Sleep(100);
LogServiceStart("CalibrationService.TriggerCheckDownload", units);
DASServiceLock.SetUnitsBusy(this,
units,
c3,
callbackData,
"CalibrationService.TriggerCheckDownload",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.TriggerCheckDownload(maxDelay, maxDuration, diagnosticsAaFilterFrequencyHz, diagnosticsSampleRateHz, MyCallback, data);
});
while (!waitHandle.WaitOne(100, false))
{
Application.DoEvents();
if (bFailed)
{
return false;
}
elapsed = DateTime.Now.Subtract(start);
if (elapsed.TotalMilliseconds > maxTimeMs)
{
foreach (var unit in units)
{
callback(new CallbackData { Status = CallbackData.CallbackStatus.Canceled, Target = unit, UserData = callbackData });
}
return false;
}
}
return true;
}
private static bool IsTom(DASModule module)
{
try
{
if (module.SerialNumber().ToLower().Contains("tom"))
{
return true;
}
if (null != module.Channels && module.Channels.Length > 0 &&
module.Channels[0] is OutputSquibChannel)
{
return true;
}
}
catch (Exception ex) { APILogger.Log("problem getting serial number", ex); }
return false;
}
///
/// If there are past events stored on the hardware their dagnostics results can
/// be retrieved with a call to this function.
///
/// List of IDASCommunications
/// from which to get event diagnostics results.
/// Event Number of the event cals to be retrieved.
/// Pre or Post? Diagnostics run before the test to check
/// integrity of system or after test to rediagnose? Must pass a
/// PrePostResults enumerated type.
/// Call back to call from the API.
/// Object to be passed along.
///
/// Any members of the 'Units' parameter
/// do not implement the required Interface or are otherwise invalid.
/// Another service is already executing commands on the hardware.
/// The device has been disconnected from the machine.
/// There is a problem with the logging sub-system.
public void GetEventDiagnosticsResults(List units,
int eventNumber,
PrePostResults whichResult,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.GetEventCalibrationResults", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.GetEventCalibrationResults",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.GetEventDiagnosticsResults(eventNumber,
whichResult,
MyCallback,
data);
});
}
public void MeasureTransferSpeed(List units,
Callback callback,
object callbackData)
{
LogServiceStart("CalibrationService.MeasureTransferSpeed", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"CalibrationService.MeasureTransferSpeed",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDiagnosticsActions)?.MeasureTransferSpeed(MyCallback,
data);
});
}
#region private members
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is IDiagnos))
{
throw new ArgumentException(Strings.DiagnosticsService_Verify_Err1);
}
if (!(unit is IDiagnosticsActions))
{
throw new ArgumentException(Strings.DiagnosticsService_Verify_Err2);
}
}
}
#endregion
}
#endregion
#region Trigger check service
public class TriggerCheckService : ServiceBase
{
///
/// courtesy wait after completing service (for example) PreStartTriggerCheck to avoid units are still busy
/// This happens because RunAllAvailable or AllFinished can happen before the units are actually marked not busy again
///
public const int SERVICECOMPLETE_COURTESY_WAIT = 50;
public override string ServiceName()
{
return "TriggerCheckService";
}
public void Start(List units,
Callback callback,
object callbackData)
{
LogServiceStart("TriggerCheckService.Start", units);
Verify(units, callback);
//there was concern that S6DBs were getting flipped during Start and affecting chained S6
//because of that we process the S6DB first, and then the S6 1.5 seconds later (by which time the pulse should alredy be complete)
var nonS6DBUnits = new List();
var s6DBUnits = new List();
foreach (var unit in units)
{
if (unit is EthernetSlice6DB)
{
s6DBUnits.Add(unit);
}
else { nonS6DBUnits.Add(unit); }
}
var idasToStatus = new Dictionary();
var mreLocal = new ManualResetEvent(false);
//intercept the final status for all calls, so that we don't accidently pass back all finished
//keep track of the final status seperately and pass them all back at once
//don't intercept new data or progress, just pass those through
//13820 With the S6 ATD and a SLICE PRO system in the setup, consistently see Trigger Check Fail
//what we do here is a bit complicated, we run PRE start on all units
//we run START on all units
//then we run POST start on all units
//but we want to make sure we pass back success on only units that
//succeeded both START and POST
var successHash = new HashSet();
var failureHash = new HashSet();
var callback2 = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished:
mreLocal.Set();
break;
case CallbackData.CallbackStatus.Success:
successHash.Add(new CallbackData(data));
break;
case CallbackData.CallbackStatus.Failure:
failureHash.Add(new CallbackData(data));
break;
default:
callback(data);
break;
}
});
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
units,
callback2,
units,
"TriggerCheckService.PreStartTriggerCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.PreStartTriggerCheck(MyCallback, data);
});
mreLocal.WaitOne();
//courtesy wait after completing PreStartTriggerCheck to avoid units are still busy
Thread.Sleep(SERVICECOMPLETE_COURTESY_WAIT);
//we don't worry about failures in precheck, only during start or post, just clear the lists for now
successHash.Clear();
failureHash.Clear();
if (s6DBUnits.Any())
{
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
s6DBUnits,
callback2,
s6DBUnits,
"TriggerCheckService.Start(Distributors)",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.StartTriggerCheck(MyCallback,
data);
});
mreLocal.WaitOne();
//S6DB and ECMs will pulse the trigger, wait atleast 1.5s for the pulse to complete
Thread.Sleep(200);
}
if (nonS6DBUnits.Any())
{
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
nonS6DBUnits,
callback2,
nonS6DBUnits,
"TriggerCheckService.Start(DAS)",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.StartTriggerCheck(MyCallback,
data);
});
mreLocal.WaitOne();
}
var failedUnits = new HashSet();
//for any unit that has failed, keep a record that it failed
//and don't send back success after POST, send back this failed instead
if (failureHash.Any())
{
using (var e = failureHash.GetEnumerator())
{
while (e.MoveNext())
{
callback(e.Current); //do the failure callback
failedUnits.Add(e.Current.Target);
}
}
}
successHash.Clear();
failureHash.Clear();
mreLocal.Reset();
DASServiceLock.SetUnitsBusy(this,
units,
callback2,
units,
"TriggerCheckService.PostStartTriggerCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.PostStartTriggerCheck(MyCallback, data);
});
mreLocal.WaitOne();
if (failureHash.Any())
{
using (var e = failureHash.GetEnumerator())
{
while (e.MoveNext())
{
callback(e.Current); //do the failure callback
failedUnits.Add(e.Current.Target);
}
}
}
//the successhash here should contain anything that succeed the last call, but we have to make sure they didn't fail an earlier call
//before we send back success
if (successHash.Any())
{
using (var e = successHash.GetEnumerator())
{
while (e.MoveNext())
{
if (null == e.Current || null == e.Current.Target)
{
//sanity check, don't do anything if you don't have a current or a target...
}
else if (!failedUnits.Contains(e.Current.Target))
{
callback(e.Current);//send back success
}
}
}
}
callback(new CallbackData { Status = CallbackData.CallbackStatus.AllFinished, UserData = units });
//now we start this service just so we can officially complete it and run any waiting instructions.
Thread.Sleep(50);
DASServiceLock.SetUnitsBusy(this, units, callback, callbackData, "TriggerCheckService.Start (return)", delegate { });
Thread.Sleep(50);
DASServiceLock.RunAllAvailable(this, null);
}
public void DoStartCheck(List units,
Callback callback,
object callbackData)
{
LogServiceStart("TriggerCheckService.DoStartCheck", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"TriggerCheckService.DoStartCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.DoStartCheck(MyCallback, data);
});
}
public void DoTriggerCheck(List units,
Callback callback,
object callbackData)
{
LogServiceStart("TriggerCheckService.DoTriggerCheck", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"TriggerCheckService.DoTriggerCheck",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.DoTriggerCheck(MyCallback, data);
});
}
public void Stop(List units,
Callback callback,
object callbackData)
{
LogServiceStart("TriggerCheckService.Stop", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"TriggerCheckService.Stop",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITriggerCheckActions)?.CancelTriggerCheck(MyCallback, data);
});
}
///
/// clears out sampleaverageadc values in level-trigger-able channels on a unit
/// 18736 Level trigger failed on Arm check list navi step
///
public static void ClearLevelTriggerCache(IConfigurationData ConfigData)
{
if (null != ConfigData && null != ConfigData.Modules)
{
foreach (var m in ConfigData.Modules)
{
foreach (var ch in m.Channels)
{
if (!(ch is ILevelTriggerable triggerable)) { continue; }
triggerable.SampleAverageADC = null;
}
}
}
}
#region private members
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is ITriggerCheck))
{
// "TriggerCheckService: Some member of Units isn't an ITriggerCheck"
throw new ArgumentException(Strings.TriggerCheckService_Verify_Err1);
}
if (!(unit is ITriggerCheckActions))
{
// "TriggerCheckService: Some member of Units isn't an ITriggerCheckActions"
throw new ArgumentException(Strings.TriggerCheckService_Verify_Err2);
}
}
}
#endregion
}
#endregion
#region Realtime service
public class RealtimeService : ServiceBase
{
public override string ServiceName()
{
return "RealtimeService";
}
protected override bool LogService => false;
///
/// whether to use UDP streaming when supported by units
///
public bool UseUDPStreaming { get; set; } = false;
public string HostIPAddress { get; set; } = System.Net.IPAddress.Any.ToString();
public void StartActivePolling(List units,
Callback callback,
object callbackData,
ManualResetEvent stopEvent,
Dictionary idasToChannels
)
{
lock (RealtimeStartStopLock)
{
Thread.Sleep(20);
LogServiceStart("RealtimeService.StartActivePolling", units);
Verify(units, callback); // we just need to check for null
var mre = new ManualResetEvent(false);
Callback intercept = delegate (CallbackData data)
{
mre.Set();//treat any callback as an okay things have started
callback(data);
};
try
{
DASServiceLock.SetUnitsBusy(this,
units,
intercept,
callbackData,
"RealtimeService.StartActivePolling",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IRealTimeActions)?.RealTimePolling(
MyCallback,
data,
stopEvent,
idasToChannels[data.Unit]);
});
mre.WaitOne();
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
}
private const int MIN_CALLBACK_UPDATE_TIME_MS = 50;
public void Start(List units,
int samplesPerSecond,
int millisecBetweenSamples,
Callback callback,
object callbackData,
bool allowMultipleSampleRealtime,
int[] moduleIndices,
ManualResetEvent stopEvent,
Dictionary idasToChannels,
GetAafForHardwareDelegate getAafForHardware)
{
lock (RealtimeStartStopLock)
{
Thread.Sleep(20);
LogServiceStart("RealtimeService.Start", units);
Verify(units, callback); // we just need to check for null
var mre = new ManualResetEvent(false);
var intercept = new Callback(delegate (CallbackData data)
{
mre.Set();//treat any callback as an okay things have started
callback(data);
});
try
{
DASServiceLock.SetUnitsBusy(this,
units,
intercept,
callbackData,
"RealtimeService.Start",
delegate (ServiceBaseCallbackData data)
{
var index = units.IndexOf(data.Unit);
var moduleIndex = moduleIndices[index];
(data.Unit as IRealTimeActions)?.RealTime(samplesPerSecond,
millisecBetweenSamples,
MyCallback,
data,
allowMultipleSampleRealtime,
moduleIndex,
stopEvent,
idasToChannels[data.Unit],
getAafForHardware(data.Unit, samplesPerSecond),
MIN_CALLBACK_UPDATE_TIME_MS,
UseUDPStreaming,
HostIPAddress);
});
mre.WaitOne();
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
}
public void ReadTilt(List units,
Callback callback,
object callbackData,
/*int[] moduleIndices,*/
ManualResetEvent stopEvent)
{
lock (RealtimeStartStopLock)
{
Thread.Sleep(20);
LogServiceStart("RealtimeService.ReadTilt", units);
Verify(units, callback); // we just need to check for null
var mre = new ManualResetEvent(false);
var intercept = new Callback(delegate (CallbackData data)
{
mre.Set();//treat any callback as an okay things have started
callback(data);
});
try
{
DASServiceLock.SetUnitsBusy(this,
units,
intercept,
callbackData,
"RealtimeService.ReadTilt",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IRealTimeActions)?.RealTimeTiltPolling(MyCallback,
data,
stopEvent);
});
mre.WaitOne();
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
}
#region Glue functions for Start
///
/// defines a function that takes in an IDASCommunication device and returns an AAF rate
/// this is done because AAF is very hardware specific even at a given sample rate
///
///
///
public delegate float GetAafForHardwareDelegate(IDASCommunication das, double sampleRate);
///
/// This is a glue layer for the regular start function but it also provides an implicit
/// service available function that waits until the service is done or time outs
///
///
///
///
///
/// the time to sleep between samples
/// this is useful sometimes on slower single cpu machines
/// the sampling rate
///
///
///
///
///
public void Start(List units,
int samplesPerSecond,
int millisecBetweenSamples,
Callback callback,
object callbackData,
int milliSecTimeoutWait,
bool allowMultipleSampleRealtime,
int[] moduleIndices,
ManualResetEvent stopEvent,
Dictionary idasToChannels,
GetAafForHardwareDelegate getAafForHardware)
{
lock (RealtimeStartStopLock)
{
Thread.Sleep(20);
var userObj = new ServiceGlueClass(callback, callbackData);
var mre = new ManualResetEvent(false);
Callback intercept = delegate (CallbackData data)
{
mre.Set();//treat any data as the go ahead to continue on and unlock the realtime service
callback(data);
};
ServiceAvailable += ServiceIsAvailable;
try
{
Start(units, samplesPerSecond, millisecBetweenSamples, intercept, userObj, allowMultipleSampleRealtime, moduleIndices, stopEvent, idasToChannels, getAafForHardware);
mre.WaitOne();
if (!userObj.AvailableEvent.WaitOne(milliSecTimeoutWait, false))
{
// timeout
throw new TimeoutException();
}
}
catch (Exception ex)
{
ServiceAvailable -= ServiceIsAvailable;
Cancel();
APILogger.Log(ex);
throw;
}
}
}
#endregion
private static readonly object RealtimeStartStopLock = new object();
public void Exit(List units,
Callback callback,
object callbackData)
{
lock (RealtimeStartStopLock)
{
Thread.Sleep(20);
LogServiceStart("RealtimeService.Exit", units);
var mre = new ManualResetEvent(false);
var intercept = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished: mre.Set(); break;
}
callback(data);
});
if (null != units && units.Count > 0)
{
Verify(units, callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
intercept,
callbackData,
"RealtimeService.Exit",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IRealTimeActions)?.ExitRealTimeMode(MyCallback,
data);
});
mre.WaitOne();
}
else
{
throw new NotConnectedException("no units in exit, we appear disconnected");
}
}
}
// FB15313 set UDP options
public void SetUDPStreamProfile(List units,
Callback callback,
object callbackData,
object streamData)
{
LogServiceStart("RealtimeService.SetUDPStreamProfile", units);
Verify(units, callback);
var strObjects = (object[])streamData;
var streamProfiles = (Dictionary)strObjects[0];
var addresses = (Dictionary)strObjects[1];
var timeChannelIds = (Dictionary)strObjects[2];
var dataChannelIds = (Dictionary)strObjects[3];
var tmnsConfigs = (Dictionary)strObjects[4];
var irigTDPIntervals = (Dictionary)strObjects[5];
try
{
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"RealtimeService.SetUDPStreamProfile",
delegate (ServiceBaseCallbackData data)
{
// FB15388: udp ports must be unique, start index-0 unit at the default and iterate offset from there
_ = units.IndexOf(data.Unit);
(data.Unit as IRealTimeActions)?.SetUDPStreamProfile(MyCallback, data,
streamProfiles[data.Unit], addresses[data.Unit], timeChannelIds[data.Unit], dataChannelIds[data.Unit], tmnsConfigs[data.Unit], irigTDPIntervals[data.Unit]);
});
}
catch (Exception ex)
{
throw ex;
}
}
public void GetUDPStreamProfile(List units,
Callback callback,
object callbackData)
{
LogServiceStart("RealtimeService.GetUDPStreamProfile", units);
Verify(units, callback);
try
{
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"RealtimeService.GetUDPStreamProfile",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IRealTimeActions)?.GetUDPStreamProfile(MyCallback, data);
});
}
catch (Exception ex)
{
throw ex;
}
}
#region private members
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is IRealTime))
{
// "RealtimeService: Some member of Units isn't an IRealTime"
throw new ArgumentException(Strings.RealtimeService_Verify_Err1);
}
if (!(unit is IRealTimeActions))
{
// "RealtimeService: Some member of Units isn't an IRealTimeActions"
throw new ArgumentException(Strings.RealtimeService_Verify_Err2);
}
}
}
#endregion
}
#endregion
#region Arming service
public class TimingService : ServiceBase
{
public override string ServiceName()
{
return "TimeSyncService";
}
public void Synchronize(List units,
Callback callback,
object callbackData,
DateTime time)
{
LogServiceStart("TimeSyncService.Synchronize", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"TimeSyncService.Synchronize",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITimeActions)?.Synchronize(MyCallback,
data,
time);
});
}
public void QueryTime(List units,
Callback callback,
object callbackData,
DateTime time)
{
LogServiceStart("TimeSyncService.QueryTime", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"TimeSyncService.QueryTime",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as ITimeActions)?.QueryTime(MyCallback, data);
});
}
}
public class ArmingService : ServiceBase
{
public override string ServiceName()
{
return "ArmingService";
}
///
/// re-arm all units potentially calling arm right away, and setting auto arm and repeat enable flags if necessary
///
public void Rearm(List units,
Callback callback,
object callbackData,
bool autoArm,
bool arm,
bool repeatEnable)
{
LogServiceStart("ArmingService.ReArm", units);
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.ReArm",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.ReArm(MyCallback,
data,
autoArm, arm, repeatEnable);
});
}
public void ReadyForArm(List units,
Callback callback,
object callbackData,
int armNowTimeout,
bool testingMode,
int maxNumberEvents,
ushort autoDiagLevel/*not used - 15832 Remove "AutoDiagnostic_Setting" from config file.*/,
Guid eventGuid,
bool dummyArm,
bool sysMode)
{
LogServiceStart("ArmingService.ReadyArm", units);
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.ArmNow",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.ReadyForArming(MyCallback,
data,
eventGuid, armNowTimeout,
testingMode,
maxNumberEvents, dummyArm, sysMode);
});
}
// just show arm and choose internally which to use
public void ArmNow(List units,
Callback callback,
object callbackData,
int armNowTimeout,
bool testingMode,
int maxNumberEvents,
Guid eventGuid,
bool sysMode)
{
LogServiceStart("ArmingService.ArmNow", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.ArmNow",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.ArmNow(MyCallback,
data,
eventGuid, armNowTimeout,
testingMode,
maxNumberEvents, sysMode);
});
}
public void PrepareForArmNow(List units,
Callback callback,
object callbackData,
int armNowTimeout,
bool testingMode,
int maxNumberEvents,
Guid eventGuid,
bool sysMode)
{
LogServiceStart("ArmingService.PrepareForArmNow", units);
Verify(units, callback);
if (!RunTestVariables.EliminatePrepareForDataCollection)
{
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On);
}
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.PrepareForArmNow",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.PrepareForArmNow(MyCallback,
data,
eventGuid, armNowTimeout,
testingMode,
maxNumberEvents, sysMode);
});
}
public void PreparedArmNow(List units,
Callback callback,
object callbackData,
int armNowTimeout,
bool checkoutMode,
int maxNumberEvents,
Guid eventGuid,
bool sysMode)
{
LogServiceStart("ArmingService.PreparedArmNow", units);
Verify(units, callback);
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.PreparedArmNow",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.PreparedArmNow(MyCallback,
data,
eventGuid, armNowTimeout,
checkoutMode,
maxNumberEvents, sysMode);
});
}
public void AutoArmNow(List units,
Callback callback,
object callbackData,
int armNowTimeout,
bool testingMode,
uint diagnosticsDelayMs,
int maxNumberEvents,
bool repeatEnable,
bool preserveDiagnostics)
{
LogServiceStart("ArmingService.AutoArmNow", units);
Verify(units, callback);
var eventGuid = Guid.NewGuid();
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.AutoArmNow",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.AutoArmNow(MyCallback,
data,
eventGuid, armNowTimeout,
testingMode,
diagnosticsDelayMs,
maxNumberEvents,
repeatEnable,
preserveDiagnostics);
});
}
// you must call ArmNow, monitor status to make sure they are all armed, then call this
public void EnableFaultChecking(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.EnableFaultChecking", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.EnableFaultChecking",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.EnableFaultChecking(MyCallback,
data);
});
}
public void DisAutoArm(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.DisAutoArm", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.DisAutoArm",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.DisAutoArm(MyCallback,
data);
});
}
public void Disarm(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.Disarm", units);
Verify(units, callback);
units.ForEach(d => d.ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.Off);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.Disarm",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.Disarm(MyCallback,
data);
});
}
public void CheckAlreadyLevelTriggered(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.CheckAlreadyLevelTriggered", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.CheckAlreadyLevelTriggered",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.CheckAlreadyLevelTriggered(MyCallback, data);
});
}
///
/// This commands the firmware to shut off all analog signals that are not needed at
/// the moment. There are two times when a unit MUST NOT be in low power mode: diagnostics and arming.
/// This is because the analog voltages across and within the sensors need some time to
/// "warm up". When calling the unit is automatically
/// powered up, but before calling Diagnose you
/// must call
/// to "warm up" the analog circuits that
/// must be ready for diagnostics.
///
/// Units to power down.
/// Service Callback
/// Object to pass along.
///
/// Any members of the 'Units' parameter
/// do not implement the required Interface or are otherwise invalid.
/// Another service is already executing commands on the hardware.
/// The device has been disconnected from the machine.
/// There is a problem with the logging sub-system.
public void EnterLowPowerMode(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.EnterLowPowerMode", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.EnterLowPowerMode",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.EnterLowPowerMode(MyCallback,
data);
});
}
public void TurnOffT0Lights(List units)
{
LogServiceStart("ArmingService.TurnOffT0Lights", units);
var mre = new ManualResetEvent(false);
var callback = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
});
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
units,
"ArmingService.TurnOffT0Lights",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.TurnOffT0Lights(MyCallback,
data);
});
mre.WaitOne();
}
public void BeginBackgroundFlashErase(List units)
{
LogServiceStart("ArmingService.BeginBackgroundFlashErase", units);
var mre = new ManualResetEvent(false);
var callback = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
});
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units, callback, units, "ArmingService.BeginBackgroundFlashErase",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.BeginBackgroundFlashErase(MyCallback, data);
});
mre.WaitOne();
}
public void BeginFlashErase(List units,
Callback callback,
object callbackData,
bool dummyArm)
{
LogServiceStart("ArmingService.BeginFlashErase", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this, units, callback, callbackData,
"ArmingService.BeginFlashErase",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.BeginFlashErase(MyCallback, data, dummyArm);
});
}
public void GetFlashEraseStatus(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.GetFlashEraseStatus", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this, units, callback, callbackData,
"ArmingService.GetFlashEraseStatus",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.QueryFlashEraseStatus(MyCallback, data);
});
}
///
/// Gets all extended fault ids from DAS
///
public void GetExtendedFaultIds(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.GetExtendedFaultIds", units);
Verify(units, callback);
try
{
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.GetExtendedFaultIds",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.GetExtendedFaultIds(MyCallback, data);
});
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
public void GetArmStatus(List units,
Callback callback,
object callbackData, uint inputVoltageCutoff, int? maxTimeout = null)
{
LogServiceStart("ArmingService.GetArmStatus", units);
Verify(units, callback);
try
{
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.GetArmStatus",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.GetArmStatus(MyCallback,
data, inputVoltageCutoff, null == maxTimeout? Constants.GETARMSTATUS_TIMEOUT : (int)maxTimeout);
});
}
catch (Exception ex)
{
throw ex;
}
}
public void GetAutoArmStatus(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.GetAutoArmStatus", units);
//performance improvement, skip check for AutoArm if unnecessary for system
//reduce # of commands
if (RunTestVariables.InRunTest && !RunTestVariables.CheckForAutoArmedInRunTest)
{
Task.Run(() => callback(new CallbackData() { Status = CallbackData.CallbackStatus.AllFinished }));
return;
}
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.GetAutoArmStatus",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.GetAutoArmStatus(MyCallback,
data);
});
}
public void StartRecord(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.StartRecord", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.StartRecord",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.StartRecord(MyCallback,
data);
});
}
public void Trigger(List units,
Callback callback,
object callbackData)
{
LogServiceStart("ArmingService.Trigger", units);
Verify(units, callback);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"ArmingService.Trigger",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IArmActions)?.Trigger(MyCallback,
data);
});
}
#region private members
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is IArmStatus))
{
// "ArmingService: Some member of Units isn't an IArmStatus"
throw new ArgumentException(Strings.ArmingService_Verify_Err1);
}
if (!(unit is IArmActions))
{
// "ArmingService: Some member of Units isn't an IArmActions"
throw new ArgumentException(Strings.ArmingService_Verify_Err2);
}
}
}
#endregion
}
#endregion
#region Download service
public class DownloadService : ServiceBase
{
public static double DownloadPriority = 32; //NORMAL BY DEFAULT
public static int DownloadAffinity = 0xFF; //ALL BY DEFAULT
public override string ServiceName()
{
return "DownloadService";
}
///
/// attempts to correct T0 and Total samples recorded for any DAS
/// supporting correction
///
public void CorrectT0s(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.CorrectT0s", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.SetEventInfo",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDownloadActions)?.CorrectT0s(
MyCallback,
data);
});
}
public void SetEventInfo(List units,
Callback callback,
object callbackData,
int eventIndex,
Guid guid,
string id,
ulong startRecordSample,
ulong totalSamples,
ulong[] triggerSamples,
uint eventHasDownloaded)
{
LogServiceStart("DownloadService.SetEventInfo", units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.SetEventInfo",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDownloadActions)?.SetEventInfo(
eventIndex,
id,
guid,
totalSamples,
triggerSamples,
startRecordSample,
eventHasDownloaded,
MyCallback,
data);
});
}
public enum DownloadParallelSettings
{
AllParallel,
SameTypeParallel,
AllSerial
}
public void Download(List units,
Callback callback,
object callbackData,
DownloadParallelSettings setting,
int maxParallelByType)
{
LogServiceStart("DownloadService.Download", units);
var runs = new List>();
try
{
Process.GetCurrentProcess().PriorityClass = (ProcessPriorityClass)DownloadPriority;
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(DownloadAffinity);
}
catch (Exception)
{
//ignore
}
switch (setting)
{
case DownloadParallelSettings.AllParallel:
runs.Add(units);
break;
case DownloadParallelSettings.AllSerial:
runs.AddRange(units.Select(unit => new List(new[] { unit })));
break;
case DownloadParallelSettings.SameTypeParallel:
{
var dasByType = new Dictionary>();
foreach (var unit in units)
{
var type = "N/A";
switch (unit)
{
case EthernetRibeye _:
type = "EthernetRibeye";
break;
case EthernetSliceDB _:
type = "SLICE";
break;
case EthernetSlice6DB _:
type = "SLICE";
break;
case EthernetSlice2 _:
type = "SLICE";
break;
case EthernetSlice _:
type = "SLICE";
break;
case EthernetSlice1_5 _:
type = "SLICE";
break;
case EthernetTDAS _:
if (unit.SerialNumber.StartsWith("5M"))
{
type = "G5";
}
else if (unit.SerialNumber.StartsWith("DR"))
{
type = "Rack";
}
else if (unit.SerialNumber.StartsWith("LR"))
{
type = "Lab Rack";
}
break;
default:
type = "SLICE";
break;
}
if (!dasByType.ContainsKey(type))
{
dasByType.Add(type, new List());
}
dasByType[type].Add(unit);
}
using (var e = dasByType.GetEnumerator())
{
while (e.MoveNext())
{
if (!e.Current.Value.Any()) continue;
if (e.Current.Key == "SLICE")
{
runs.Add(e.Current.Value);
}
else
{
//iterate through all the devices in this list and add them to a list, once we have x of a type,
//add that entire list to the local list of downloads (run)
var l = new List();
for (var i = 0; i < e.Current.Value.Count; i++)
{
if (0 == i % maxParallelByType)
{
//if we fall into here we have achieved the first x of this type (or it's the first)
if (l.Any())
{
runs.Add(l);
l = new List(); //create a new list to start adding to
}
}
l.Add(e.Current.Value[i]);
}
//finally, if we add the remainder of the leftovers
if (l.Any())
{
runs.Add(l);
}
}
}
}
}
break;
}
var idasToStatus = new Dictionary();
var mreLocal = new ManualResetEvent(false);
//intercept the final status for all downloads, so that we don't accidently pass back all finished
//keep track of the final status seperately and pass them all back at once
//don't intercept new data or progress, just pass those through
var callback2 = new Callback(delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.AllFinished:
mreLocal.Set();
break;
case CallbackData.CallbackStatus.Success:
idasToStatus[data.Target] = true;
break;
case CallbackData.CallbackStatus.Failure:
idasToStatus[data.Target] = false;
break;
default:
callback(data);
break;
}
});
//do the actual downloads using our own service
foreach (var list in runs)
{
using (var ds = new DownloadService())
{
mreLocal.Reset();
ds.DownloadAllParallel(list, callback2, list);
mreLocal.WaitOne();
}
}
//pass back the final status of all units
foreach (var unit in units)
{
var status = CallbackData.CallbackStatus.Failure;
if (idasToStatus.ContainsKey(unit) && idasToStatus[unit])
{
status = CallbackData.CallbackStatus.Success;
}
callback(new CallbackData
{
Target = unit,
UserData = units,
Status = status
});
}
callback(new CallbackData { Status = CallbackData.CallbackStatus.AllFinished, UserData = units });
//now we start this service just so we can officially complete it and run any waiting instructions.
Thread.Sleep(50);
DASServiceLock.SetUnitsBusy(this, units, callback, callbackData, "DownloadService.Download", delegate { });
try
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal;
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(0xFF);
}
catch (Exception)
{
//ignore
}
Thread.Sleep(50);
DASServiceLock.RunAllAvailable(this, null);
}
public void DownloadAllParallel(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.Download", units);
Verify(units, callback); // we just need to check for null
VerifyDownload(units);
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.Download",
delegate (ServiceBaseCallbackData data)
{
((IDownloadActions)data.Unit).Download(MyCallback,
data);
});
}
public void QueryDownload(List units,
Callback callback,
object callbackData,
int eventIndex,
TDASServiceSetupInfoLookup setupInfoLookup)
{
LogServiceStart("DownloadService.QueryDownload", units);
//I decided to adjust this, the trigger adjustment should be applied immediately as soon as information is available
//the problem was that we'd set the trigger adjustment, requery, and the trigger adjustment would be lost, so make sure
//it's set immediately on querying ...
//do do this we silently intercept the original function and wrap it.
Verify(units, callback); // we just need to check for null
var successUnits = new List();
DASServiceLock.SetUnitsBusy(this,
units,
delegate (CallbackData data)
{
switch (data.Status)
{
case CallbackData.CallbackStatus.Success:
successUnits.Add(data);
break;
case CallbackData.CallbackStatus.AllFinished:
{
MakeLevelTriggerAdjustments(units, eventIndex);
foreach (var cb in successUnits) { callback(cb); }
callback(data);
}
break;
default:
callback(data);
break;
}
}
,
callbackData,
"DownloadService.QueryDownload",
delegate (ServiceBaseCallbackData data)
{
TDASServiceSetupInfo setupInfo = null;
if (setupInfoLookup != null)
{
setupInfo = new TDASServiceSetupInfo(
setupInfoLookup.SetupDescription,
setupInfoLookup.SamplesPerSecondLookup[data.Unit.SerialNumber],
setupInfoLookup.HardwareFilterRateHzLookup[data.Unit.SerialNumber],
setupInfoLookup.RecordingMode,
setupInfoLookup.CheckoutMode);
}
(data.Unit as IDownloadActions)?.QueryDownload(MyCallback,
data, eventIndex, setupInfo);
});
}
public void UARTDownload(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.UARTDownload", units);
var uartDAS = new List();
foreach (var das in units.Where(x => x is IUARTDownload))
{
uartDAS.Add(das);
}
UARTDownloadAllParallel(uartDAS, callback, callbackData);
}
public void UARTDownloadAllParallel(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.UARTDownload", units);
Verify(units, callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.UARTDownload",
delegate (ServiceBaseCallbackData data)
{
((IUARTDownloadActions)data.Unit).UARTDownload(MyCallback,
data);
});
}
public void QueryUARTDownload(List units,
Callback callback,
object callbackData,
int eventIndex)
{
LogServiceStart("DownloadService.QueryUARTDownload", units);
// filter out units that don't support uart downloads
units = units.Where(das => das is IUARTDownloadActions).ToList();
Verify(units, callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.QueryUARTDownload",
delegate (ServiceBaseCallbackData data)
{
((IUARTDownloadActions)data.Unit).QueryUARTDownload(MyCallback, data, eventIndex, null);
});
}
///
/// A representation of a DAS' level trigger correction.
///
private class LevelTriggerCorrection
{
///
/// The DAS id of the level trigger correction.
///
public string DasId
{
get;
}
///
/// The time value in seconds of
///
public double TimeZeroOffsetSec
{
get;
}
///
/// Initialize an instance of LevelTriggerCorrection.
///
///
///
/// The serial number of the DAS associated with this level trigger correction.
///
///
///
///
public LevelTriggerCorrection(string dasId, double sampleOffset)
{
try
{
DasId = dasId;
TimeZeroOffsetSec = sampleOffset;
}
catch (Exception ex)
{
throw new ApplicationException("encountered problem initializing " + typeof(LevelTriggerCorrection).FullName, ex);
}
}
}
///
/// Manipulates the triggerSamplesNumbers to reflect any adjustment based on level trigger activity in any of the DAS
///
/// The list of DAS
/// The event to adjust. Note TDAS only supports one event
private static void MakeLevelTriggerAdjustments(List communicationDevices, int eventIndex)
{
// Build a list of all modules that experienced a level trigger. Note that for this process
// we don't have to have a list that identifies the channel that triggered; only what the
// earliest trigger time is and on which DAS it appeared (so we don't reapply it there).
var startIndex = eventIndex;
var endIndex = eventIndex;
if (eventIndex == -1)
{
startIndex = 0;
foreach (var eventInfo in communicationDevices.Select(x=>x.EventInfo) )
{
if (null == eventInfo) { continue; }
eventIndex = Math.Max(eventIndex, eventInfo.Events.Length);
}
}
for (var idx = startIndex; idx <= endIndex; idx++)
{
var levelTriggerCorrections = new List();
foreach (var das in communicationDevices)
{
if (null == das.EventInfo) { continue; }
if (idx >= das.EventInfo.Events.Length) { continue; }
foreach (var module in das.EventInfo.Events[idx].Modules)
{
if (0 < module.GetLevelTriggerT0AdjustmentSamplesAutoApplied())
levelTriggerCorrections.Add(
new LevelTriggerCorrection(das.SerialNumber,
(double)module.GetLevelTriggerT0AdjustmentSamplesAutoApplied() / module.SampleRateHz));
}
}
// Apply the defining level trigger's offset time to everyone.
// Find the module that experienced the level trigger first.
var criterion = levelTriggerCorrections.Find(
ltc => ltc.TimeZeroOffsetSec == levelTriggerCorrections.Max(m => m.TimeZeroOffsetSec));
// Apply the defining level trigger's offset time to everyone.
if (null != criterion)
{
foreach (var das in communicationDevices)
{
if (null == das.EventInfo) { continue; }
if (idx >= das.EventInfo.Events.Length) { continue; }
var bDoAdjustment = true;
// This implementation difference should be pushed into DAS specific services, but that is too disruptive right now.
// In SLICE, everyone but the DAS that level trigger needs to adjust. In TDAS, everyone needs to adjust.
if (!(das is EthernetTDAS))
{
//this is a bit clunky, but first go through all the modules
//if one of them has 0 samples autoapplied, then don't apply to this module ...
//otherwise ... apply them
foreach (var module in das.EventInfo.Events[idx].Modules)// ConfigData.Modules )
for (var i = 0; i < module.TriggerSampleNumbers.Length; i++)
{
if (0 != module.GetLevelTriggerT0AdjustmentSamplesAutoApplied())
{
bDoAdjustment = false;
}
}
if (bDoAdjustment)
{
foreach (var module in das.EventInfo.Events[idx].Modules)// ConfigData.Modules )
for (var i = 0; i < module.TriggerSampleNumbers.Length; i++)
{
var offset = Math.Floor(criterion.TimeZeroOffsetSec * module.SampleRateHz);
var triggerAdjustment = offset >= module.GetLevelTriggerT0AdjustmentSamplesAutoApplied() ?
Convert.ToUInt64(offset - Convert.ToDouble(module.GetLevelTriggerT0AdjustmentSamplesAutoApplied())) :
0;
module.TriggerSampleNumbers[i] = (module.TriggerSampleNumbers[i] >= triggerAdjustment) ?
module.TriggerSampleNumbers[i] - triggerAdjustment : 0;
module.StartRecordSampleNumber = (module.StartRecordSampleNumber >= triggerAdjustment) ?
module.StartRecordSampleNumber = module.StartRecordSampleNumber - triggerAdjustment : 0;
}
}
}
else
{
if (1 <= idx) { throw new NotImplementedException("TDAS does not support more than one event"); }
foreach (var module in das.EventInfo.Events[idx].Modules)
for (var i = 0; i < module.TriggerSampleNumbers.Length; i++)
{
var triggerAdjustment = (ulong)Math.Floor(criterion.TimeZeroOffsetSec * module.SampleRateHz);
module.TriggerSampleNumbers[i] = (module.TriggerSampleNumbers[i] >= triggerAdjustment) ?
module.TriggerSampleNumbers[i] - triggerAdjustment : 0;
module.StartRecordSampleNumber = (module.StartRecordSampleNumber >= triggerAdjustment) ?
module.StartRecordSampleNumber = module.StartRecordSampleNumber - triggerAdjustment : 0;
}
}
}
}
}
}
public void QueryDownloadedStatus(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.QueryDownloadedStatus", units);
Verify(units, callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.QueryDownloadedStatus",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDownloadActions)?.QueryDownloadedStatus(MyCallback,
data);
});
}
public void SetTriggerSampleNumbers(List units,
Callback callback,
object callbackData)
{
LogServiceStart("DownloadService.SetTriggerSampleNumbers", units);
Verify(units, (Callback)(object)callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.SetTriggerSampleNumbers",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDownloadActions)?.SetTriggerSampleNumbers(MyCallback,
data);
});
}
///
/// This is to mark DAS as downloaded when there is no data to download so that
/// it doesn't appear to contain undownloaded data the next time it's checked
///
///
///
///
///
public void SetDownloaded(List units,
Callback callback,
object callbackData,
string eventId)
{
LogServiceStart("DownloadService.SetDownloaded", units);
Verify(units, callback); // we just need to check for null
DASServiceLock.SetUnitsBusy(this,
units,
callback,
callbackData,
"DownloadService.SetDownloaded",
delegate (ServiceBaseCallbackData data)
{
(data.Unit as IDownloadActions)?.SetDownloaded(MyCallback,
data);
});
}
#region private members
protected void VerifyDownload(List units)
{
var nonOBRDDRUnits = new List();
foreach (var unit in units)
{
if (unit is EthernetSlice6Air s6A && s6A.IsEthernetRecorder)
{
continue;
}
nonOBRDDRUnits.Add(unit);
}
// check that all dasses have a WhatToDownload
if (nonOBRDDRUnits.Exists(das => das.WhatToDownload == null))
{
throw new ArgumentException("One or more DAS units have no data to download");
}
// check that all dasses have an EventInfo
if (nonOBRDDRUnits.Exists(das => (das.EventInfo == null || das.EventInfo.Events == null || das.EventInfo.Events.Length == 0)))
{
throw new ArgumentException("One or more DAS units have EventInfo==null");
}
// check that all dasses have the same event number
if ((nonOBRDDRUnits.Count > 0) && (nonOBRDDRUnits.Select(das => das.WhatToDownload.EventNumber).Distinct().Count() != 1))
{
throw new ArgumentException("All DAS units must have the same event number");
}
// if we're doing sub-sampling, number of samples must be a multiple of SamplesToSkip
if ((nonOBRDDRUnits.Count > 0) &&
(nonOBRDDRUnits.Exists(das => (das.WhatToDownload.SamplesToSkip > 1) &&
((das.WhatToDownload.EndSample - das.WhatToDownload.StartSample + 1) % das.WhatToDownload.SamplesToSkip != 0))))
{
throw new ArgumentException("DownloadService.Download: When doing sub-sampling, number of samples must be a multiple of SamplesToSkip");
}
}
protected override void Verify(List units, Callback callback)
{
base.Verify(units, callback);
foreach (var unit in units)
{
if (!(unit is IDownload))
{
// "DownloadService: Some member of Units isn't an IDownload"
throw new ArgumentException(Strings.DownloadService_Verify_Err1);
}
if (!(unit is IDownloadActions))
{
// "DownloadService: Some member of Units isn't an IDownloadActions"
throw new ArgumentException();
}
}
}
#endregion
}
#endregion
}