4571 lines
206 KiB
C#
4571 lines
206 KiB
C#
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);
|
|
|
|
/// <summary>
|
|
/// Delegate for service available event handler. A function of this form
|
|
/// should be defined to handle the ServiceAvailable event.
|
|
/// </summary>
|
|
/// <param name="sender">The object that fired the ServiceAvailable event.</param>
|
|
/// <param name="data">Same object that comes back to the Service Callback. data.target
|
|
/// will be the target for the service that is done.</param>
|
|
public delegate void ServiceBaseEventHandler(object sender, CallbackData data);
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public event ServiceBaseEventHandler ServiceAvailable;
|
|
|
|
/// <summary>
|
|
/// Delegate for error event handler
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="msg"></param>
|
|
/// <param name="caught"></param>
|
|
public delegate void ServiceCallbackErrorEventHandler(object sender, string msg, Exception caught);
|
|
|
|
/// <summary>
|
|
/// end the local event
|
|
/// </summary>
|
|
public event ServiceCallbackErrorEventHandler ServiceCallbackError;
|
|
|
|
/// <summary>
|
|
/// end the static event
|
|
/// </summary>
|
|
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<ServiceBase, List<ServiceBaseCallbackData>> DASUnits = new Dictionary<ServiceBase, List<ServiceBaseCallbackData>>();
|
|
protected readonly object DASUnitsLock = new object();
|
|
|
|
/// <summary>
|
|
/// cancels without regard to unit progress [realtime progress status is ambiguous]
|
|
/// </summary>
|
|
/// <param name="service"></param>
|
|
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<IDASCommunication> 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");
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// returns true if the unit can be soft disconnected
|
|
/// </summary>
|
|
/// <param name="unit"></param>
|
|
/// <returns></returns>
|
|
private bool CanSoftDisconnect(IDASCommunication unit)
|
|
{
|
|
switch (unit)
|
|
{
|
|
case EthernetSlice6 _:
|
|
case EthernetSlice6Air _:
|
|
case EthernetSlice6AirBridge _:
|
|
case EthernetSlice2 _:
|
|
case EthernetTsrAir _:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/// <summary>
|
|
/// soft connect the units (if they support it)
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
public void SoftConnectUnits(IDASCommunication[] units)
|
|
{
|
|
if (!HardwareConstants.AllowSoftDisconnects) { return; }
|
|
|
|
var allTasks = new List<Task>();
|
|
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// soft disconnects the units if they support it
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
public void SoftDisconnectUnits(List<IDASCommunication> units)
|
|
{
|
|
if (!HardwareConstants.AllowSoftDisconnects) { return; }
|
|
|
|
var allTasks = new List<Task>();
|
|
|
|
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);
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
public class DuplicateSerialNumbersException : Exception
|
|
{
|
|
public const string DUPLICATE_SERIALS_TEXT = "Duplicate serials detected among DAS";
|
|
}
|
|
public void SetUnitsBusy(ServiceBase service,
|
|
List<IDASCommunication> 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<ServiceBaseCallbackData>(units.Count));
|
|
}
|
|
|
|
var idasToCallback = new Dictionary<IDASCommunication, ServiceBaseCallbackData>();
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// this function handles proportioning out serviceStart calls to units
|
|
/// </summary>
|
|
/// <param name="service"></param>
|
|
/// <param name="units"></param>
|
|
/// <param name="callback"></param>
|
|
/// <param name="callbackData"></param>
|
|
/// <param name="caller"></param>
|
|
/// <param name="serviceStart"></param>
|
|
/// <param name="dataLookup"></param>
|
|
private void CreateWorkThreads(ServiceBase service,
|
|
List<IDASCommunication> units,
|
|
Callback callback,
|
|
object callbackData,
|
|
string caller,
|
|
ServiceStartDelegate serviceStart,
|
|
IDictionary<IDASCommunication, ServiceBaseCallbackData> dataLookup)
|
|
{
|
|
Parallel.ForEach(units, unit => { serviceStart(dataLookup[unit]); });
|
|
Thread.Sleep(UNITS_START_CHECK_INTERVAL);
|
|
}
|
|
|
|
/// <summary>
|
|
/// the thread that is spinning off calls to units iterates looking to call startService
|
|
/// this controls how long in ms between each iteration
|
|
/// </summary>
|
|
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<IDASCommunication, IntBool> OurUnits;
|
|
protected readonly object DicLock = new object();
|
|
|
|
public int Current { get; set; }
|
|
public int NumRemaining { get; set; }
|
|
|
|
public ProgressAccumulator(List<IDASCommunication> units)
|
|
{
|
|
OurUnits = new Dictionary<IDASCommunication, IntBool>(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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that there ARE units to be configured, that the configuration callbacks aren't
|
|
/// null, etc.
|
|
/// </summary>
|
|
///
|
|
/// <param name="units">
|
|
/// The units to be verified as "configuration"-able.
|
|
/// </param>
|
|
///
|
|
/// <param name="callback">
|
|
/// The <see cref="Callback"/> to be invoked upon configuration
|
|
/// service completion.
|
|
/// </param>
|
|
///
|
|
protected virtual void Verify(List<IDASCommunication> 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<IDASCommunication> 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
|
|
{
|
|
|
|
/// <summary>
|
|
/// Return the <see cref="string"/> name of this service.
|
|
/// </summary>
|
|
///
|
|
/// <returns>
|
|
/// The <see cref="string"/> name for this service.
|
|
/// </returns>
|
|
///
|
|
public override string ServiceName()
|
|
{
|
|
return "ConfigurationService";
|
|
}
|
|
|
|
public void SetFirstUseDate(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// ensures that trigger line is reset for all units
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
/// <param name="callback"></param>
|
|
/// <param name="callbackData"></param>
|
|
public void ResetHardwareLines(List<IDASCommunication> 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);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
///
|
|
/// <param name="units">
|
|
/// The list of <see cref="IDASCommunication"/>s to be configuration-checked.
|
|
/// </param>
|
|
///
|
|
/// <param name="doStrictCheck">
|
|
/// Setting this <see cref="bool"/> parameter to True will result in the check being pickier
|
|
/// (i.e., excepting on null/empty engineering unit properties, etc.).
|
|
/// </param>
|
|
/// <param name="errorCallback"></param>
|
|
public void ConfigurationValidEx(List<IDASCommunication> 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);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
/// <param name="callback"></param>
|
|
/// <param name="callbackData"></param>
|
|
public void AutoDetect(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> units,
|
|
bool doStrictCheck,
|
|
bool eventConfig,
|
|
Callback callback,
|
|
object callbackData,
|
|
ErrorCallback errorCallback,
|
|
bool dummyConfig,
|
|
double [] maxAaf,
|
|
bool configureDigitalOutputs,
|
|
bool turnOffAAFRealtime,
|
|
IStreamingFilterProfile dspFilterType,
|
|
bool discardDiagnostics,
|
|
Dictionary<IDASCommunication, ushort> timeChannelIds = null,
|
|
Dictionary<IDASCommunication, ushort> dataChannelIds = null,
|
|
Dictionary<IDASCommunication, UDPStreamProfile> streamProfiles = null,
|
|
Dictionary<IDASCommunication, int> streamADCPerPacket = null,
|
|
Dictionary<IDASCommunication, ushort> irigTDPIntervals = null,
|
|
Dictionary<IDASCommunication, string> addresses = null,
|
|
Dictionary<IDASCommunication, uint[]> tmnsConfigs = null,
|
|
Dictionary<IDASCommunication, uint> baudRates = null,
|
|
Dictionary<IDASCommunication, uint> dataBits = null,
|
|
Dictionary<IDASCommunication, StopBits> stopBits = null,
|
|
Dictionary<IDASCommunication, Parity> parities = null,
|
|
Dictionary<IDASCommunication, Handshake> flowControls = null,
|
|
Dictionary<IDASCommunication, UartDataFormat> dataFormats = null,
|
|
Dictionary<IDASCommunication, ushort> 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<IDASCommunication> 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<string, bool>)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<IDASCommunication> 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<string, byte>)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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication, uint>)cbObjects[0];
|
|
var dataBits = (Dictionary<IDASCommunication, uint>)cbObjects[1];
|
|
var stopBits = (Dictionary<IDASCommunication, StopBits>)cbObjects[2];
|
|
var parities = (Dictionary<IDASCommunication, Parity>)cbObjects[3];
|
|
var flowControls = (Dictionary<IDASCommunication, Handshake>)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<IDASCommunication> 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<IDASCommunication> units,
|
|
IReadOnlyDictionary<IDASCommunication, string> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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
|
|
|
|
/// <inheritdoc />
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="units">
|
|
/// The units to be verified as "configuration"-able.
|
|
/// </param>
|
|
/// <param name="callback">
|
|
/// The <see cref="T:DTS.DASLib.Service.ServiceBase.Callback" /> to be invoked upon configuration
|
|
/// service completion.
|
|
/// </param>
|
|
protected override void Verify(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> GetActiveDevicesDelegate();
|
|
public void CheckSafetyState(List<IDASCommunication> 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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerated type to indicate pre-event or post-event Diagnostics results. Passed to
|
|
/// <see cref="DiagnosticsService.GetEventDiagnosticsResults">
|
|
/// GetEventDiagnosticsResults
|
|
/// </see>
|
|
/// method.
|
|
/// </summary>
|
|
public enum PrePostResults
|
|
{
|
|
PreEventDiagnosticsResult,
|
|
PostEventDiagnosticsResult
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// <see cref="DiagnosticsService" >Diagnose(...)</see> will instruct
|
|
/// the hardware to perform diagnostics based on what values are set in the <see
|
|
/// cref="Service.IDiagnos" >Channel Diagnostics</see> array in
|
|
/// <see cref="IDASCommunication">IDASCommunication</see>.
|
|
/// </summary>
|
|
public class DiagnosticsService : ServiceBase
|
|
{
|
|
public override string ServiceName()
|
|
{
|
|
return "DiagnosticsService";
|
|
}
|
|
|
|
/// <exception cref="ArgumentException">Any members of the 'Units' parameter
|
|
/// do not implement the required Interface or are otherwise invalid.</exception>
|
|
/// <exception cref="BusyException">Another service is already executing commands on the hardware.</exception>
|
|
/// <exception cref="NotConnectedException">The device has been disconnected from the machine.</exception>
|
|
/// <exception cref="Exception">There is a problem with the logging sub-system.</exception>
|
|
public void PrepareForBridgeResistanceMeasurement(List<IDASCommunication> 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<IDASCommunication> units,
|
|
PrePostResults whichResult,
|
|
IReadOnlyDictionary<string, double> sampleRates,
|
|
IReadOnlyDictionary<string, float> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication>();
|
|
var s6DBUnits = new List<IDASCommunication>();
|
|
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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// clears any ILevelTriggerable:SampleAverageADC cached values
|
|
/// 14042 Flash Clear turns of excitation for s6
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
private void ClearLevelTriggerCache(IDASCommunication[] units)
|
|
{
|
|
if (!units.Any()) { return; }
|
|
Parallel.ForEach(units, (unit) =>
|
|
{
|
|
TriggerCheckService.ClearLevelTriggerCache(unit.ConfigData);
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
public void ClearDASTriggerLine(List<IDASCommunication> 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 });
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// calculates the max duration and delay among squibs in the test
|
|
/// returns delay, duration, whether only TOM exists, and whether a TOM was present
|
|
/// </summary>
|
|
private void CalculateMaxDurationDelay(List<IDASCommunication> 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<int, bool>();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// adds ChannelDiagnosticResults for any DAS, since Diagnostics can be run for specific modules
|
|
/// must preserve any existing channel diagnostic results
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
private void InitializeConfigurationResults(List<IDASCommunication> units)
|
|
{
|
|
foreach (var das in units)
|
|
{
|
|
var oldResults = new Dictionary<int, IDiagnosticResult>();
|
|
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<IDiagnosticResult>();
|
|
list.AddRange(oldResults.Values.ToArray());
|
|
if (null != results)
|
|
{
|
|
list.AddRange(results);
|
|
}
|
|
das.ChannelDiagnosticsResults = list.ToArray();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Call to execute hardware test as specified in
|
|
/// <see cref="Service.IDiagnos">.ChannelDiagnostics</see> in
|
|
/// IDASCommunication.
|
|
/// </summary>
|
|
/// <param name="units">List of <see cref="IDASCommunication">IDASCommunications</see>,
|
|
/// the ChannelDiagnostics array in each dictating what test to be performed.</param>
|
|
/// <param name="whichResult"></param>
|
|
/// <param name="callback">Callback function to be called by the service.</param>
|
|
/// <param name="callbackData">An object to pass along.</param>
|
|
/// <param name="eventNumber"></param>
|
|
/// <param name="maxTimeMs"></param>
|
|
/// <exception cref="ArgumentException">Any members of the 'Units' parameter
|
|
/// do not implement the required Interface or are otherwise invalid.</exception>
|
|
/// <exception cref="BusyException">Another service is already executing commands on the hardware.</exception>
|
|
/// <exception cref="NotConnectedException">The device has been disconnected from the machine.</exception>
|
|
/// <exception cref="Exception">There is a problem with the logging sub-system.</exception>
|
|
public void Diagnos(List<IDASCommunication> 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);
|
|
}
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
private bool SquibFireCheck(List<IDASCommunication> 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;
|
|
}
|
|
/// <summary>
|
|
/// If there are past events stored on the hardware their dagnostics results can
|
|
/// be retrieved with a call to this function.
|
|
/// </summary>
|
|
/// <param name="units">List of <see cref="IDASCommunication">IDASCommunications</see>
|
|
/// from which to get event diagnostics results.</param>
|
|
/// <param name="eventNumber">Event Number of the event cals to be retrieved.</param>
|
|
/// <param name="whichResult">Pre or Post? Diagnostics run before the test to check
|
|
/// integrity of system or after test to rediagnose? Must pass a
|
|
/// <see cref="PrePostResults">PrePostResults enumerated type</see>.</param>
|
|
/// <param name="callback">Call back to call from the API.</param>
|
|
/// <param name="callbackData">Object to be passed along.</param>
|
|
///
|
|
/// <exception cref="ArgumentException">Any members of the 'Units' parameter
|
|
/// do not implement the required Interface or are otherwise invalid.</exception>
|
|
/// <exception cref="BusyException">Another service is already executing commands on the hardware.</exception>
|
|
/// <exception cref="NotConnectedException">The device has been disconnected from the machine.</exception>
|
|
/// <exception cref="Exception">There is a problem with the logging sub-system.</exception>
|
|
public void GetEventDiagnosticsResults(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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
|
|
{
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
public const int SERVICECOMPLETE_COURTESY_WAIT = 50;
|
|
public override string ServiceName()
|
|
{
|
|
return "TriggerCheckService";
|
|
}
|
|
|
|
public void Start(List<IDASCommunication> 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<IDASCommunication>();
|
|
var s6DBUnits = new List<IDASCommunication>();
|
|
foreach (var unit in units)
|
|
{
|
|
if (unit is EthernetSlice6DB)
|
|
{
|
|
s6DBUnits.Add(unit);
|
|
}
|
|
else { nonS6DBUnits.Add(unit); }
|
|
}
|
|
|
|
var idasToStatus = new Dictionary<IDASCommunication, bool>();
|
|
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<CallbackData>();
|
|
var failureHash = new HashSet<CallbackData>();
|
|
|
|
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<IDASCommunication>();
|
|
//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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// clears out sampleaverageadc values in level-trigger-able channels on a unit
|
|
/// 18736 Level trigger failed on Arm check list navi step
|
|
/// </summary>
|
|
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<IDASCommunication> 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;
|
|
/// <summary>
|
|
/// whether to use UDP streaming when supported by units
|
|
/// </summary>
|
|
public bool UseUDPStreaming { get; set; } = false;
|
|
|
|
public string HostIPAddress { get; set; } = System.Net.IPAddress.Any.ToString();
|
|
public void StartActivePolling(List<IDASCommunication> units,
|
|
Callback callback,
|
|
object callbackData,
|
|
ManualResetEvent stopEvent,
|
|
Dictionary<IDASCommunication, byte[]> 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<IDASCommunication> units,
|
|
int samplesPerSecond,
|
|
int millisecBetweenSamples,
|
|
Callback callback,
|
|
object callbackData,
|
|
bool allowMultipleSampleRealtime,
|
|
int[] moduleIndices,
|
|
ManualResetEvent stopEvent,
|
|
Dictionary<IDASCommunication,
|
|
byte[]> 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<IDASCommunication> 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
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="das"></param>
|
|
/// <returns></returns>
|
|
public delegate float GetAafForHardwareDelegate(IDASCommunication das, double sampleRate);
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
/// <param name="millisecBetweenSamples"></param>
|
|
/// <param name="callback"></param>
|
|
/// <param name="callbackData"></param>
|
|
/// <param name="milliSecTimeoutWait">the time to sleep between samples
|
|
/// this is useful sometimes on slower single cpu machines</param>
|
|
/// <param name="samplesPerSecond">the sampling rate</param>
|
|
/// <param name="allowMultipleSampleRealtime"></param>
|
|
/// <param name="moduleIndices"></param>
|
|
/// <param name="stopEvent"></param>
|
|
/// <param name="idasToChannels"></param>
|
|
/// <param name="getAafForHardware"></param>
|
|
public void Start(List<IDASCommunication> units,
|
|
int samplesPerSecond,
|
|
int millisecBetweenSamples,
|
|
Callback callback,
|
|
object callbackData,
|
|
int milliSecTimeoutWait,
|
|
bool allowMultipleSampleRealtime,
|
|
int[] moduleIndices,
|
|
ManualResetEvent stopEvent,
|
|
Dictionary<IDASCommunication, byte[]> 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<IDASCommunication> 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<IDASCommunication> units,
|
|
Callback callback,
|
|
object callbackData,
|
|
object streamData)
|
|
{
|
|
LogServiceStart("RealtimeService.SetUDPStreamProfile", units);
|
|
|
|
Verify(units, callback);
|
|
var strObjects = (object[])streamData;
|
|
var streamProfiles = (Dictionary<IDASCommunication, UDPStreamProfile>)strObjects[0];
|
|
var addresses = (Dictionary<IDASCommunication, string>)strObjects[1];
|
|
var timeChannelIds = (Dictionary<IDASCommunication, ushort>)strObjects[2];
|
|
var dataChannelIds = (Dictionary<IDASCommunication, ushort>)strObjects[3];
|
|
var tmnsConfigs = (Dictionary<IDASCommunication, uint[]>)strObjects[4];
|
|
var irigTDPIntervals = (Dictionary<IDASCommunication, ushort>)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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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";
|
|
}
|
|
|
|
/// <summary>
|
|
/// re-arm all units potentially calling arm right away, and setting auto arm and repeat enable flags if necessary
|
|
/// </summary>
|
|
public void Rearm(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 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 <see cref="ArmNow" /> the unit is automatically
|
|
/// powered up, but before calling <see cref="DiagnosticsService" />Diagnose you
|
|
/// must call
|
|
/// <see cref="PrepareForDiagnostics"/> to "warm up" the analog circuits that
|
|
/// must be ready for diagnostics.
|
|
/// </summary>
|
|
/// <param name="units">Units to power down.</param>
|
|
/// <param name="callback">Service Callback</param>
|
|
/// <param name="callbackData">Object to pass along.</param>
|
|
///
|
|
/// <exception cref="ArgumentException">Any members of the 'Units' parameter
|
|
/// do not implement the required Interface or are otherwise invalid.</exception>
|
|
/// <exception cref="BusyException">Another service is already executing commands on the hardware.</exception>
|
|
/// <exception cref="NotConnectedException">The device has been disconnected from the machine.</exception>
|
|
/// <exception cref="Exception">There is a problem with the logging sub-system.</exception>
|
|
public void EnterLowPowerMode(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// Gets all extended fault ids from DAS
|
|
/// </summary>
|
|
public void GetExtendedFaultIds(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> 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";
|
|
}
|
|
|
|
/// <summary>
|
|
/// attempts to correct T0 and Total samples recorded for any DAS
|
|
/// supporting correction
|
|
/// </summary>
|
|
public void CorrectT0s(List<IDASCommunication> 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<IDASCommunication> 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<IDASCommunication> units,
|
|
Callback callback,
|
|
object callbackData,
|
|
DownloadParallelSettings setting,
|
|
int maxParallelByType)
|
|
{
|
|
LogServiceStart("DownloadService.Download", units);
|
|
var runs = new List<List<IDASCommunication>>();
|
|
|
|
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<IDASCommunication>(new[] { unit })));
|
|
break;
|
|
case DownloadParallelSettings.SameTypeParallel:
|
|
{
|
|
var dasByType = new Dictionary<string, List<IDASCommunication>>();
|
|
|
|
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<IDASCommunication>());
|
|
}
|
|
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<IDASCommunication>();
|
|
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<IDASCommunication>(); //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<IDASCommunication, bool>();
|
|
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<IDASCommunication> 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<IDASCommunication> 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<CallbackData>();
|
|
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<IDASCommunication> units,
|
|
Callback callback,
|
|
object callbackData)
|
|
{
|
|
LogServiceStart("DownloadService.UARTDownload", units);
|
|
|
|
var uartDAS = new List<IDASCommunication>();
|
|
foreach (var das in units.Where(x => x is IUARTDownload))
|
|
{
|
|
uartDAS.Add(das);
|
|
}
|
|
UARTDownloadAllParallel(uartDAS, callback, callbackData);
|
|
}
|
|
|
|
public void UARTDownloadAllParallel(List<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// A representation of a DAS' level trigger correction.
|
|
/// </summary>
|
|
private class LevelTriggerCorrection
|
|
{
|
|
/// <summary>
|
|
/// The DAS id <see cref="string"/> of the level trigger correction.
|
|
/// </summary>
|
|
public string DasId
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The <see cref="double"/> time value in seconds of
|
|
/// </summary>
|
|
public double TimeZeroOffsetSec
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize an instance of LevelTriggerCorrection.
|
|
/// </summary>
|
|
///
|
|
/// <param name="dasId">
|
|
/// The serial number <see cref="string"/> of the DAS associated with this level trigger correction.
|
|
/// </param>
|
|
///
|
|
/// <param name="sampleOffset"></param>
|
|
///
|
|
public LevelTriggerCorrection(string dasId, double sampleOffset)
|
|
{
|
|
try
|
|
{
|
|
DasId = dasId;
|
|
TimeZeroOffsetSec = sampleOffset;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new ApplicationException("encountered problem initializing " + typeof(LevelTriggerCorrection).FullName, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Manipulates the triggerSamplesNumbers to reflect any adjustment based on level trigger activity in any of the DAS
|
|
/// </summary>
|
|
/// <param name="communicationDevices">The list of DAS</param>
|
|
/// <param name="eventIndex">The event to adjust. Note TDAS only supports one event</param>
|
|
private static void MakeLevelTriggerAdjustments(List<IDASCommunication> 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<LevelTriggerCorrection>();
|
|
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<IDASCommunication> 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<IDASCommunication> 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);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="units"></param>
|
|
/// <param name="callback"></param>
|
|
/// <param name="callbackData"></param>
|
|
/// <param name="eventId"></param>
|
|
public void SetDownloaded(List<IDASCommunication> 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<IDASCommunication> units)
|
|
{
|
|
var nonOBRDDRUnits = new List<IDASCommunication>();
|
|
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<IDASCommunication> 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
|
|
}
|