Files
DP44/DataPRO/IService/Classes/GenericServices.cs

4571 lines
206 KiB
C#
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
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
}