This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,721 @@
using DTS.Common.Interface.Channels;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DASFactory.Config;
using DTS.Common.Interface.Sensors;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Service.StateMachine.StatusAndParameters.Configure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace DTS.DASLib.Service.StateMachine
{
public class ConfigureStatusInformation : IStatusInfo
{
/// <summary>
/// all possible status that can be relayed to consumers
/// </summary>
public enum StatusValues
{
ApplyingConfiguration,
AutoResolvingChannels,
ManuallyResolvedChannels,
Completed,
Cancelling,
Cancelled,
ChannelOutOfPosition,
NoChannelsAssigned,
AllChannelsResolved,
EIDNotFound,
ApplyConfigFailed,
AppliedConfiguration,
PrepareForDiagnostics,
PrepareForDiagnosticsFailed,
PrepareForDiagnosticsSuccess,
LowPower,
LowPowerSuccess,
LowPowerFailure
}
/// <summary>
/// no channels are currently assigned
/// </summary>
public bool NoChannelsAssigned { get; internal set; } = true;
/// <summary>
/// all channels to be resolved are resolved
/// </summary>
public bool AllChannelsResolved { get; internal set; } = false;
/// <summary>
/// one or more channels are out of position with respect to how
/// they requested to be positioned (they are on a different channel than they were specified to be on)
/// </summary>
public bool ChannelsOutOfPosition { get; internal set; } = false;
/// <summary>
/// all units that were to be configured were configured
/// </summary>
public bool HaveAppliedConfigAllUnits { get; set; } = false;
/// <summary>
/// signals a cancel request
/// </summary>
public ManualResetEvent CancelEvent = new ManualResetEvent(false);
/// <summary>
/// signals when work is finished
/// </summary>
public ManualResetEvent DoneEvent = new ManualResetEvent(false);
/// <summary>
/// action to take on completion of work
/// </summary>
public ActionCompleteDelegate CompleteAction { get; set; }
/// <summary>
/// action to take on progress notification
/// </summary>
public SetProgressValueDelegate ProgressAction { get; set; }
/// <summary>
/// action to take on a status notification
/// </summary>
public StatusIntDelegate StatusAction { get; set; }
/// <summary>
/// action to take on extended status notification
/// </summary>
public StatusExIntDelegate StatusExAction { get; set; }
/// <summary>
/// holds references to all units which were successfully configured
/// </summary>
public IDASCommunication[] UnitsConfigured { get; set; } = new IDASCommunication[0];
/// <summary>
/// task for turning excitation on/off, used for monitoring task or interrupting
/// </summary>
private Task _ExcitationTask = null;
/// <summary>
/// task for applying configuration, used for monitoring task or interrupting
/// </summary>
private Task _ApplyConfigTask = null;
/// <summary>
/// used to canel running task
/// returns when task is cancelled or completed
/// </summary>
/// <returns></returns>
public async Task Cancel()
{
CancelEvent.Set();
StatusAction?.Invoke((int)StatusValues.Cancelling);
//simulate time spent waiting for cancel to be acknowledged
Thread.Sleep(100);
DoneEvent.WaitOne();
StatusAction?.Invoke((int)StatusValues.Cancelled);
}
/// <summary>
/// turns on power (if requested and appropriate)
/// </summary>
private void PrepareForDiagnostics()
{
var param = States.Instance.Configure.Status.ConfigureParameters;
var status = States.Instance.Configure.Status.ConfigureStatus;
var global = States.Instance.Configure.Status.GlobalStatusInformation;
var dasFactory = States.Instance.Configure.DASFactory;
//make sure we have some units to turn on, if not fail task
if (!status.UnitsConfigured.Any())
{
StatusAction?.Invoke((int)StatusValues.PrepareForDiagnosticsFailed);
return;
}
ProgressAction?.Invoke(0D);
StatusAction?.Invoke((int)StatusValues.PrepareForDiagnostics);
//record if any units fail to turn on excitation
var bPassed = true;
var mre = new ManualResetEvent(false);
try
{
using (var diagnosticsService = new DiagnosticsService())
{
var units = status.UnitsConfigured.ToList();
diagnosticsService.PrepareForDiagnostics(units,
PrePostResults.PreEventDiagnosticsResult,
param.SampleRateLookup,
param.AAFRateLookup,
(ServiceBase.CallbackData data) =>
{
switch (data.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Success:
StatusExAction?.Invoke((int)StatusValues.PrepareForDiagnosticsSuccess, data.Target);
global.AddUnitAtHighPower(data.Target);
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
bPassed = false;
StatusExAction?.Invoke((int)StatusValues.PrepareForDiagnosticsFailed, data.Target);
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
}
},
units);
}
}
catch (Exception ex)
{
bPassed = false;
throw ex;
}
var timeWaited = 0;
while (!mre.WaitOne(PREPARE_SPIN_TIME, false))
{
timeWaited += PREPARE_SPIN_TIME;
ProgressAction?.Invoke(100D * timeWaited / EXPECTED_PREPARE_TIME);
}
if (bPassed)
{
StatusAction?.Invoke((int)StatusValues.PrepareForDiagnosticsSuccess);
States.Instance.ConfigureStart.Status.GlobalStatusInformation.ExcitationOn = true;
}
else
{
StatusAction?.Invoke((int)StatusValues.PrepareForDiagnosticsFailed);
}
}
private const int PREPARE_SPIN_TIME = 200;
private const int EXPECTED_PREPARE_TIME = 8000;
/// <summary>
/// lock used to control thread access to shared resources
/// [like UnitsConfigured]
/// </summary>
private static readonly object MyLock = new object();
private void AddConfiguredDevice(IDASCommunication device)
{
lock (MyLock)
{
if (UnitsConfigured.Contains(device)) { return; }
var list = new List<IDASCommunication>();
list.AddRange(UnitsConfigured);
list.Add(device);
UnitsConfigured = list.ToArray();
}
StatusExAction?.Invoke((int)StatusValues.AppliedConfiguration, device);
}
/// <summary>
/// Turns off excitation, returns immediately
/// </summary>
public void TurnOffExcitation()
{
var param = States.Instance.ConfigureStart.Status.ConfigureParameters;
//reset the flag that got us here
param.TurnOffExcitation = false;
var status = States.Instance.ConfigureStart.Status.ConfigureStatus;
var dasFactory = States.Instance.ConfigureStart.DASFactory;
_ExcitationTask = Task.Run(() =>
{
StatusAction?.Invoke((int)StatusValues.LowPower);
var units = dasFactory.GetDASList();
var anyFailed = false;
try
{
var mre = new ManualResetEvent(false);
using (var service = new ArmingService())
{
service.EnterLowPowerMode(units, (ServiceBase.CallbackData data) =>
{
switch (data.Status)
{
case ServiceBase.CallbackData.CallbackStatus.Success:
StatusExAction?.Invoke((int)StatusValues.LowPowerSuccess, data.Target);
break;
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
anyFailed = true;
StatusExAction?.Invoke((int)StatusValues.LowPowerFailure, data.Target);
break;
}
}, units);
}
mre.WaitOne();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
//if any failed to turn off record the error
//if all turned off, then set the global status param that excitation is now known off
if (anyFailed)
{
StatusAction?.Invoke((int)StatusValues.LowPowerFailure);
}
else
{
States.Instance.ConfigureStart.Status.GlobalStatusInformation.ExcitationOn = false;
StatusAction?.Invoke((int)StatusValues.LowPowerSuccess);
}
StatusAction?.Invoke((int)StatusValues.Completed);
CompleteAction?.Invoke();
DoneEvent.Set();
});
}
/// <summary>
/// starts ApplyConfig process, returns immediately
/// </summary>
public void ApplyConfig()
{
DoneEvent.Reset();
var param = States.Instance.ConfigureStart.Status.ConfigureParameters;
if (param.TurnOffExcitation)
{
TurnOffExcitation();
return;
}
var unitsToConfigure = param.UnitsToConfigure.ToList();
var dasFactory = States.Instance.ConfigureStart.DASFactory;
_ApplyConfigTask = Task.Run(() =>
{
//the user could just be turning on power, in which case the configuration is already set
//if the flag for set configuration is set, then set the configuration, otherwise don't
if (param.SetConfiguration)
{
HaveAppliedConfigAllUnits = false;
StatusAction?.Invoke((int)StatusValues.ApplyingConfiguration);
//check if we are supposed to configure a unit, but it's not available
//mark it failed if the unit is not available
var units = dasFactory.GetDASList();
foreach (var unit in unitsToConfigure)
{
if (!units.Contains(unit))
{
unitsToConfigure.Remove(unit);
StatusExAction?.Invoke((int)StatusValues.ApplyConfigFailed, unit);
}
}
var mre = new ManualResetEvent(false);
if (unitsToConfigure.Any())
{
using (var configService = new ConfigurationService())
{
configService.SetConfiguration(unitsToConfigure, param.DoStrictCheck, param.EventConfig,
(data) =>
{
switch (data.Status)
{
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
mre.Set();
break;
case ServiceBase.CallbackData.CallbackStatus.Progress:
ProgressAction?.Invoke(data.ProgressValue);
break;
case ServiceBase.CallbackData.CallbackStatus.Failure:
StatusExAction?.Invoke((int)StatusValues.ApplyConfigFailed, data.Target,
data.ErrorMessage, data.ErrorException);
break;
case ServiceBase.CallbackData.CallbackStatus.Success:
AddConfiguredDevice(data.Target);
break;
}
}, unitsToConfigure,
param.ErrorRequiringActionAction,
param.DummyConfig,
param.MaxAAF,
param.ConfigureDigitalOutputs,
param.TurnOffAAFRealtime,
param.DSPFilterType,
param.DiscardDiagnostics);
}
}
mre.WaitOne();
//check that all the units that should have been configured were
var allConfigured = Array.TrueForAll(param.UnitsToConfigure, d => UnitsConfigured.Contains(d));
HaveAppliedConfigAllUnits = allConfigured;
}
//finally turn on power UNLESS the user has specified not to (most likely by pressing "Low Power" button)
//but also potentially by a property setting
if (!CancelEvent.WaitOne(1, false))
{
if (param.PrepareForDiagnostics && !param.SkipTurnOnPower)
{
PrepareForDiagnostics();
}
CompleteAction?.Invoke();
}
StatusAction?.Invoke((int)StatusValues.Completed);
DoneEvent.Set();
});
}
/// <summary>
/// holds channels that are not resolved for one reason or another
/// </summary>
private readonly List<GroupChannelWithMeta> _unresolvedChannels = new List<GroupChannelWithMeta>();
/// <summary>
/// holds channels that are now resolved/assigned
/// </summary>
private readonly List<GroupChannelWithMeta> _resolvedChannels = new List<GroupChannelWithMeta>();
private bool IsUnresolved(IGroupChannel ch)
{
return _unresolvedChannels.Exists(channel => channel.Channel == ch);
}
private bool IsResolved(IGroupChannel ch)
{
return _resolvedChannels.Exists(channel => channel.Channel == ch);
}
private void AddResolvedChannel(IGroupChannel channel,
IDASChannel hwChannel,
bool eidOutOfPlace,
ConfigureStatusParameters param,
IDictionary<IDASChannel, IDASCommunication> lookupChannelToDAS,
ISensorData sd,
bool missingEID)
{
//remove any possible duplicates
_resolvedChannels.RemoveAll(ch => ch.Channel == channel);
_unresolvedChannels.RemoveAll(ch => ch.Channel == channel);
var meta = new GroupChannelWithMeta()
{
Channel = channel,
ChannelConflict = false,
ConflictingChannel = null,
DASChannel = hwChannel,
EIDOutOfPlace = eidOutOfPlace,
HWChannelIncompatible = false,
MissingID = missingEID,
MissingSensor = false
};
_resolvedChannels.Add(meta);
//set the hardware in the group if needed
var includedHardware = channel.Group.IncludedHardware.ToList();
var hwId = param.GetDatabaseIdAction(lookupChannelToDAS[hwChannel]);
if (!includedHardware.Contains(hwId))
{
includedHardware.Add(hwId);
}
channel.Group.SetIncludedHardware(includedHardware.ToArray());
//set the calibration
if (hwChannel is AnalogInputDASChannel aic)
{
foreach (var e in sd.SupportedExcitation)
{
if (!aic.IsSupported(e))
{
continue;
}
var sc = param.GetCalibrationAction(sd, e);
if (null == sc) { continue; }
param.SetSensorCalibrationAction(sd, sc);
break;
}
}
}
private void AddUnresolvedChannel(IGroupChannel ch,
bool bChannelConflict,
IGroupChannel conflictingChannel,
bool EIDOutOfPlace,
bool hwChannelIncompatible,
bool hwNotFound,
bool missingId,
bool missingSensor)
{
//remove any possible duplicates
_resolvedChannels.RemoveAll(channel => channel.Channel == ch);
_unresolvedChannels.RemoveAll(channel => channel.Channel == ch);
var meta = new GroupChannelWithMeta()
{
Channel = ch,
ChannelConflict = bChannelConflict,
ConflictingChannel = conflictingChannel,
DASChannel = null,
EIDOutOfPlace = EIDOutOfPlace,
HWChannelIncompatible = hwChannelIncompatible,
HWNotFound = hwNotFound,
MissingID = missingId,
MissingSensor = missingSensor
};
_unresolvedChannels.Add(meta);
}
private bool IsHWChannelResolved(IDASChannel channel)
{
return _resolvedChannels.Exists(ch => ch.DASChannel == channel);
}
public void ManuallyUnresolveChannel(IGroupChannel channel)
{
if (!IsResolved(channel))
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.ChannelNotAssigned);
}
var resolvedChannel = _resolvedChannels.First(ch => ch.Channel == channel);
if (resolvedChannel.AssignedByEID)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.EID_Locked);
}
AddUnresolvedChannel(channel, false, null, false, false, resolvedChannel.HWNotFound, resolvedChannel.MissingID,
false);
}
public void ManuallyResolveChannel(IGroupChannel channel, IDASChannel hardwareChannel)
{
var param = States.Instance.ConfigureStart.Status.ConfigureParameters;
GetIdToHardwareLookup(param, out var lookupIDToHardware, out var lookupChannelToDAS,
out var lookupDAStoChannel);
if (channel.IsBlank())
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.BlankChannel);
}
if (channel.IsDisabled)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.ChannelDisabled);
}
if (channel.SensorId < 0)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.NoSensor);
}
var sd = param.GetSensorAction(channel);
if (null == sd)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.SensorNotFound);
}
var compatible = IsSensorHwCompatible(hardwareChannel, sd);
if (SensorCompatiblilityResponse.Compatible != compatible)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.IncompatibleHardware);
}
//alright, looks ok ... unless there's something on that channel already ...
if (IsHWChannelResolved(hardwareChannel))
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.ChannelAlreadyAssigned);
}
//ok, well make sure it's we didn't find the EID for the channel already on a channel, if so ... it's locked on
//that channel
bool bFoundEID = false;
if (IsResolved(channel))
{
var resolvedChannel = _resolvedChannels.First(ch => ch.Channel == channel);
if (resolvedChannel.AssignedByEID)
{
if (resolvedChannel.DASChannel != hardwareChannel)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.EID_Locked);
}
//nothing to do ...
bFoundEID = true;
}
}
//determine if we should have found an EID and didn't
var bMissingEID = !string.IsNullOrWhiteSpace(sd.EID) && !bFoundEID;
//if we are missing an ID, and aren't allowed to assign to a channel without an id, then don't allow it
if (bMissingEID && !param.AllowSensorIdToBlankChannel)
{
throw new InvalidAssignmentException(channel, InvalidAssignmentException.Reasons.EIDRequiredAndMissing);
}
//add as a resolved channel
AddResolvedChannel(channel, hardwareChannel, false, param, lookupChannelToDAS, sd, bMissingEID);
AllChannelsResolved = _unresolvedChannels.Any();
}
/// <summary>
/// thrown by manually resolve/unresolve functions
/// </summary>
public class InvalidAssignmentException : Exception
{
public enum Reasons
{
BlankChannel,
NoSensor,
ChannelDisabled,
SensorNotFound,
IncompatibleHardware,
ChannelAlreadyAssigned,
EID_Locked, //sensor is already locked by EID to a different channel
EIDRequiredAndMissing,
ChannelNotAssigned
}
public IGroupChannel GroupChannel { get; private set; }
public Reasons Reason { get; private set; }
public InvalidAssignmentException(IGroupChannel channel, Reasons reason)
: base($"{reason.ToString()} - {channel}")
{
Reason = reason;
GroupChannel = channel;
}
}
/// <summary>
/// sensor is not compatible with hardware, but maybe in the future we'll want to know why
/// even though right now it's just "hw incompatible"
/// </summary>
internal enum SensorCompatiblilityResponse
{
Compatible,
DigitalInputNotSupportedOnHWChannel,
DigitalInputModeNotSupportedOnHWChannel,
BridgeModeNotSupportedOnHWChannel,
SquibFireModeNotSupportedOnHWChannel,
NoSupportedExcitationOnHWChannel
}
/// <summary>
/// check if sensor can run on the channel in question
/// checks excitation, bridge, sensor type, etc
/// </summary>
/// <param name="hwid"></param>
/// <param name="sd"></param>
/// <returns></returns>
private SensorCompatiblilityResponse IsSensorHwCompatible(IDASChannel hwChannel, ISensorData sd)
{
switch (hwChannel)
{
case AnalogInputDASChannel aic:
if (aic.SupportedBridges.Contains(sd.Bridge))
{
if (aic.DigitalInputChannel)
{
if (!sd.IsDigitalInput())
{
//hardware is digital, sensor is not, it's not compatible
return SensorCompatiblilityResponse.DigitalInputNotSupportedOnHWChannel;
}
else if (!aic.SupportedDigitalInputModes.Contains(sd.InputMode))
{
//sensor and hardware are digital, but the input mode is not supported
return SensorCompatiblilityResponse.DigitalInputModeNotSupportedOnHWChannel;
}
}
else
{
var param = States.Instance.ConfigureStart.Status.ConfigureParameters;
var excitationPassed = (from e in sd.SupportedExcitation
where aic.SupportedExcitation.Contains(e)
select param.GetCalibrationAction(sd, e)).Any(sc => null != sc);
if (!excitationPassed)
{
//there's no supported excitation match
return SensorCompatiblilityResponse.NoSupportedExcitationOnHWChannel;
}
}
}
else
{
//the hardware does not support the bridge mode of the sensor ...
return SensorCompatiblilityResponse.BridgeModeNotSupportedOnHWChannel;
}
break;
case OutputSquibChannel _:
var squibChannel = (OutputSquibChannel)hwChannel;
if (!sd.IsSquib())
{
//hardware channel is squib, sensor is not
return SensorCompatiblilityResponse.BridgeModeNotSupportedOnHWChannel;
}
else if (!squibChannel.SupportedSquibFireModes.Contains(sd.SquibFireMode))
{
//hardware and sensor are squib, but fire mode not supported by hardware
return SensorCompatiblilityResponse.SquibFireModeNotSupportedOnHWChannel;
}
break;
case OutputTOMDigitalChannel _:
if (!sd.IsDigitalOutput())
{
//hardware channel is digital output, sensor is not
return SensorCompatiblilityResponse.BridgeModeNotSupportedOnHWChannel;
}
break;
}
return SensorCompatiblilityResponse.Compatible;
}
/// <summary>
/// returns sensor EID to hardware channel mapping as determined from connected units
/// </summary>
/// <param name="param"></param>
/// <param name="lookupChannelToDAS">hardware channel to IDASCommunication</param>
/// <param name="lookupIDToHardware">eid to hardware channel</param>
/// <returns></returns>
private void GetIdToHardwareLookup(ConfigureStatusParameters param,
out IDictionary<string, IDASChannel> lookupIDToHardware,
out IDictionary<IDASChannel, IDASCommunication> lookupChannelToDAS,
out IDictionary<int, IDictionary<int, IDASChannel>> lookupDASToChannel
)
{
lookupIDToHardware = new Dictionary<string, IDASChannel>();
lookupChannelToDAS = new Dictionary<IDASChannel, IDASCommunication>();
lookupDASToChannel = new Dictionary<int, IDictionary<int, IDASChannel>>();
foreach (var unit in param.UnitsToConfigure)
{
//why is there no config data?
if (null == unit.ConfigData)
{
continue;
}
var dasid = param.GetDatabaseIdAction(unit);
if (!lookupDASToChannel.ContainsKey(dasid))
{
lookupDASToChannel.Add(dasid, new Dictionary<int, IDASChannel>());
}
foreach (var module in unit.ConfigData.Modules)
{
foreach (var channel in module.Channels)
{
lookupChannelToDAS[channel] = unit;
lookupDASToChannel[dasid][channel.Number] = channel;
if (null != channel.IDs && channel.IDs.Length > 0)
{
var id = channel.IDs[0].ID;
if (!string.IsNullOrWhiteSpace(id))
{
lookupIDToHardware[id] = channel;
}
}
}
}
}
}
/// <summary>
/// resets all status to defaults
/// </summary>
public void Reset()
{
NoChannelsAssigned = true;
AllChannelsResolved = false;
ChannelsOutOfPosition = false;
HaveAppliedConfigAllUnits = false;
StatusAction = null;
CompleteAction = null;
ProgressAction = null;
UnitsConfigured = new IDASCommunication[0];
_unresolvedChannels.Clear();
_resolvedChannels.Clear();
_ExcitationTask = null;
_ApplyConfigTask = null;
}
}
}

View File

@@ -0,0 +1,238 @@
using DTS.Common.Classes.DSP;
using DTS.Common.Enums;
using DTS.Common.Interface.Channels;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.Sensors;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Interface.TestSetups.TestSetupsList;
using System.Collections.Generic;
using System.Text;
namespace DTS.DASLib.Service.StateMachine
{
public class ConfigureStatusParameters : IStatusParameters
{
#region ResolveChannels
public bool RequireIdFoundForSensorsWithIds { get; set; } = true;
public bool AllowMissingSensors { get; set; } = false;
public bool AllowSensorsOutOfPosition { get; set; } = true;
public ITestSetup TestSetupConfiguration { get; set; }
public delegate ISensorData GetSensorDelegate(IGroupChannel groupChannel);
public delegate ISensorCalibration GetSensorCalibrationDelegate(ISensorData sensor,
ExcitationVoltageOptions.ExcitationVoltageOption excitation);
/// <summary>
/// delegate allowing us to retrieve the latest sensor calibration given a sensor and an excitation
/// </summary>
public GetSensorCalibrationDelegate GetCalibrationAction { get; set; }
/// <summary>
/// retrieves a sensor given a group channel
/// this is handled in DataPRO by DataModel.TestTemplate
/// but since that exists in UI land, we'll just define a delegate
/// and allow it to be provided by the consumer by whatever mechanism it chooses
/// for run test this should be testtemplate, but for testing it could equally
/// just come from the db or directly provided
/// </summary>
public GetSensorDelegate GetSensorAction { get; set; }
public bool AllowSensorIdToBlankChannel { get; set; }
public delegate int GetDatabaseIdDelegate(IDASCommunication das);
public GetDatabaseIdDelegate GetDatabaseIdAction { get; set; }
public delegate void SetSensorCalibrationDelegate(ISensorData sd, ISensorCalibration sc);
public SetSensorCalibrationDelegate SetSensorCalibrationAction { get; set; }
#endregion
#region Configure
/// <summary>
/// Excitation should be turned off, this flag is reverted as soon as the process to turn off excitation starts
/// </summary>
public bool TurnOffExcitation { get; set; } = false;
/// <summary>
/// defines an array of units which configuration should be applied to
/// </summary>
public IDASCommunication[] UnitsToConfigure { get; set; } = new IDASCommunication[0];
/// <summary>
/// whether strict check should be used when applying configuration
/// </summary>
public bool DoStrictCheck { get; set; } = true;
/// <summary>
/// whether configuration should be written to even or diagnostic file stores
/// (this is a legacy feature of sliceware, not all units support it, but SLICE units have multiple file stores
/// so configuration could be written to different ones (diagnostic/event)
/// </summary>
public bool EventConfig { get; set; } = true;
/// <summary>
/// whether we are configuring units for data collection or just dummy collection (not collecting data)
/// </summary>
public bool DummyConfig { get; set; } = false;
/// <summary>
/// the MAX AAF for SLICE and TDAS
/// </summary>
public double[] MaxAAF { get; set; } = new double[0];
/// <summary>
/// whether digital outputs should be applied or not when applying configuration
/// this allows digital outputs to not be configured for say trigger check
/// </summary>
public bool ConfigureDigitalOutputs { get; set; } = true;
/// <summary>
/// the current configure process can occasionally require user input
/// [probably to acknowledge AAF errors?] this allows a method of interacting
/// during this process
/// </summary>
public ErrorCallback ErrorRequiringActionAction { get; set; }
/// <summary>
/// whether AAF should be turned off for realtime or not
/// AAF takes up a lot of time for slice realtime, so it's usually desirable to turn off
/// </summary>
public bool TurnOffAAFRealtime { get; set; } = true;
/// <summary>
/// whether to reset the hardware event lines before setting the config
/// </summary>
public bool ResetHardwareEventLines { get; set; } = false;
/// <summary>
/// whether to prepare for diagnostics (turn on excitation, switches)
/// </summary>
public bool PrepareForDiagnostics { get; set; } = false;
/// <summary>
/// lookup serial number to data collection rate
/// </summary>
public IReadOnlyDictionary<string, double> SampleRateLookup { get; set; } = new Dictionary<string, double>();
/// <summary>
/// lookup serial number to Anti Alias Filter rate
/// </summary>
public IReadOnlyDictionary<string, float> AAFRateLookup { get; set; } = new Dictionary<string, float>();
/// <summary>
/// whether turning on power should be skipped or not
/// this can allow excitation to remain off which is sometimes used to keep the units in low power state
/// until the user explicitly turns on power
/// </summary>
public bool SkipTurnOnPower { get; set; } = false;
/// <summary>
/// whether to apply configuration or not
/// </summary>
public bool SetConfiguration { get; set; } = true;
public DSPFilterType DSPFilterType { get; set; }
/// <summary>
/// whether to discard diagnostics when setting configuration
/// </summary>
public bool DiscardDiagnostics { get; set; } = true;
#endregion
public ConfigureStatusParameters()
{
ResetDSPFilterType();
}
private void ResetDSPFilterType()
{
var dsp = DSPFilterCollection.GetDSPFilterCollection();
DSPFilterType = dsp.GetFilter(string.Empty);
}
/// <summary>
/// resets all parameters back to defaults
/// </summary>
public void Reset()
{
RequireIdFoundForSensorsWithIds = true;
AllowMissingSensors = false;
AllowSensorsOutOfPosition = true;
DoStrictCheck = true;
EventConfig = true;
DummyConfig = false;
MaxAAF = new double[0];
ConfigureDigitalOutputs = true;
ErrorRequiringActionAction = null;
TestSetupConfiguration = null;
GetSensorAction = null;
AllowSensorIdToBlankChannel = false;
ResetHardwareEventLines = false;
PrepareForDiagnostics = false;
UnitsToConfigure = new IDASCommunication[0];
SampleRateLookup = new Dictionary<string, double>();
AAFRateLookup = new Dictionary<string, float>();
SkipTurnOnPower = false;
SetConfiguration = true;
TurnOffExcitation = false;
DiscardDiagnostics = true;
ResetDSPFilterType();
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"RequireIdFoundForSensorsWithIds={RequireIdFoundForSensorsWithIds.ToString()}");
sb.AppendLine($"AllowMissingSensors={AllowMissingSensors.ToString()}");
sb.AppendLine($"AllowSensorsOutOfPosition={AllowSensorsOutOfPosition.ToString()}");
sb.AppendLine($"DoStrictCheck={DoStrictCheck.ToString()}");
sb.AppendLine($"EventConfig={EventConfig.ToString()}");
sb.AppendLine($"DummyConfig={DummyConfig.ToString()}");
sb.Append("MaxAAF=");
for (int i = 0; i < MaxAAF.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.Append(MaxAAF[i].ToString());
}
sb.AppendLine();
sb.AppendLine($"ConfigureDigitalOutputs={ConfigureDigitalOutputs.ToString()}");
sb.AppendLine($"AllowSensorIdToBlankChannel={AllowSensorIdToBlankChannel.ToString()}");
sb.AppendLine($"ResetHardwareEventLines={ResetHardwareEventLines.ToString()}");
sb.AppendLine($"PrepareForDiagnostics={PrepareForDiagnostics.ToString()}");
sb.Append("UnitsToConfigure=");
for (var i = 0; i < UnitsToConfigure.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.Append(UnitsToConfigure[i].SerialNumber);
}
sb.AppendLine();
sb.Append("SampleRateLookup=");
var first = true;
using (var enumSampleRate = SampleRateLookup.GetEnumerator())
{
while (enumSampleRate.MoveNext())
{
if (!first)
{
sb.Append(", ");
}
sb.Append($"{enumSampleRate.Current.Key}={enumSampleRate.Current.Value.ToString()}");
first = false;
}
}
sb.AppendLine();
sb.Append("AAFLookup=");
first = true;
using (var enumAAF = AAFRateLookup.GetEnumerator())
{
while (enumAAF.MoveNext())
{
if (!first)
{
sb.Append(", ");
}
sb.Append($"{enumAAF.Current.Key}={enumAAF.Current.Value.ToString()}");
first = false;
}
}
sb.AppendLine();
sb.AppendLine($"SkipTurnOnPower={SkipTurnOnPower.ToString()}");
sb.Append("ErrorRequiringActionAction=");
sb.AppendLine(null == ErrorRequiringActionAction ? "[null]" : "[defined]");
sb.AppendLine($"SetConfiguration={SetConfiguration.ToString()}");
sb.AppendLine($"TurnOffExcitation={TurnOffExcitation.ToString()}");
sb.AppendLine($"DiscardDiagnostics={DiscardDiagnostics.ToString()}");
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,24 @@
using DTS.Common.Interface.Channels;
using DTS.Common.Interface.DASFactory.Config;
using DTS.Common.Interface.Groups.GroupList;
namespace DTS.DASLib.Service.StateMachine.StatusAndParameters.Configure
{
/// <summary>
/// helper class for use when resolving channels
/// </summary>
internal class GroupChannelWithMeta
{
public bool ChannelConflict { get; set; }
public IGroupChannel ConflictingChannel { get; set; }
public IGroupChannel Channel { get; set; }
public IGroup Group { get; set; }
public bool MissingID { get; set; }
public bool MissingSensor { get; set; }
public bool EIDOutOfPlace { get; set; }
public bool HWNotFound { get; set; }
public bool HWChannelIncompatible { get; set; }
public IDASChannel DASChannel { get; set; }
public bool AssignedByEID { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTS.DASLib.Service.StateMachine
{
public class DiagnoseParameters : IStatusParameters
{
/// <summary>
/// controls whether we should remain in state or not
/// </summary>
public bool ProceedToRealtimeWhenDone { get; set; }
public bool RequireAllUnitsPassDiagnostic { get; set; }
public bool AllUnitsPassedDiagnostic { get; set; }
public void Reset()
{
ProceedToRealtimeWhenDone = true;
RequireAllUnitsPassDiagnostic = false;
AllUnitsPassedDiagnostic = false;
}
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.StatusAndProgressBar;
namespace DTS.DASLib.Service.StateMachine
{
public class DownloadParameters : IStatusParameters
{
#region general parameters
/// <summary>
/// controls whether we should remain in state or not
/// </summary>
public bool ProceedWhenDone { get; set; } = false;
/// <summary>
/// whether all das are required to finish their download
/// </summary>
public bool RequireAllDASFinish { get; set; } = false;
#endregion
#region config file parameters
/// <summary>
/// holds the Download Folder setting from our config file
/// </summary>
public string DefaultDownloadFolder { get; set; } = string.Empty;
/// <summary>
/// holds the upload binaries setting from our config file
/// </summary>
public bool DefaultUploadBinaries { get; set; } = false;
/// <summary>
/// holds the upload exports setting from our config file
/// </summary>
public bool DefaultUploadExports { get; set; } = false;
/// <summary>
/// holds the upload logs setting from our config file
/// </summary>
public bool DefaultUploadLogs { get; set; } = false;
/// <summary>
/// holds the upload reports setting from our config file
/// </summary>
public bool DefaultUploadReports { get; set; } = false;
/// <summary>
/// holds the upload test setups setting from our config file
/// </summary>
public bool DefaultUploadSetups { get; set; } = false;
#endregion
#region download function parameters
/// <summary>
/// defines an array of DAS which should be downloaded from
/// </summary>
public IDASCommunication[] DASList { get; set; } = new IDASCommunication[0];
/// <summary>
/// holds the test id of our current test
/// </summary>
public string CurrentTestTestId { get; set; } = string.Empty;
/// <summary>
/// holds the test id node of our current test
/// </summary>
public string CurrentTestTestIdNode { get; set; } = string.Empty;
/// <summary>
/// holds the test directory location of our current test
/// </summary>
public string CurrentTestTestDirectory { get; set; } = string.Empty;
/// <summary>
/// holds the original test directory location of our current test
/// </summary>
public string CurrentTestOriginalTestDirectory { get; set; } = string.Empty;
/// <summary>
/// holds whether we're in ROI mode or not
/// </summary>
public bool ROI { get; set; } = false;
/// <summary>
/// holds whether we're in Recovery mode or not
/// </summary>
public bool Recovery { get; set; } = false;
/// <summary>
/// Used to prevent unnecessarily re-copying the folders in the Test Id folder
/// (DASConfigs, SETUP, etc.) when running from the Download tile.
/// </summary>
public bool FoldersCopied { get; set; }
#endregion
public ErrorCallback ErrorCallback { get; set; }
/// <summary>
/// resets all parameters back to defaults
/// </summary>
public void Reset()
{
DASList = new IDASCommunication[0];
CurrentTestTestId = string.Empty;
CurrentTestTestIdNode = string.Empty;
DefaultDownloadFolder = string.Empty;
DefaultUploadBinaries = false;
DefaultUploadExports = false;
DefaultUploadLogs = false;
DefaultUploadReports = false;
DefaultUploadSetups = false;
ErrorCallback = null;
Recovery = false;
ROI = false;
}
public override string ToString()
{
var sb = new StringBuilder();
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,410 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DASFactory.Download;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Utilities.Logging;
namespace DTS.DASLib.Service.StateMachine
{
public class DownloadStatusInformation : IStatusInfo
{
/// <summary>
/// all possible status that can be relayed to consumers
/// </summary>
public enum StatusValues
{
Preparing,
Downloading,
CaptureAttributes,
Failed,
ROIFailed,
Completed,
Cancelling,
Cancelled,
CancelledPartial,
DownloadDirectory,
MissingHardware,
NoDataToDownload,
NotAllChannelsDownloaded,
ExistingFiles,
CleaningUp,
QueryEventData
}
/// <summary>
/// signals cancel was requested
/// </summary>
public ManualResetEvent CancelEvent = new ManualResetEvent(false);
/// <summary>
/// signals when ping and connect is finished
/// </summary>
public ManualResetEvent DoneEvent = new ManualResetEvent(false);
/// <summary>
/// action to take on completion of ping and connect
/// </summary>
public ActionCompleteDelegate CompleteAction { get; set; }
/// <summary>
/// action to take on progress notification
/// </summary>
public SetProgressValueDelegate ProgressAction { get; set; }
/// <summary>
/// action to take on a status notification
/// </summary>
public StatusIntDelegate StatusAction { get; set; }
/// <summary>
/// action to take on extended status notification
/// </summary>
public StatusExIntDelegate StatusExAction { get; set; }
/// <summary>
/// if we were told of DAS to download from,
/// this will be true if all the das were downloaded
/// </summary>
public bool AllDASFinished { get; set; } = false;
public void Reset()
{
AllDASFinished = false;
CompleteAction = null;
StatusAction = null;
StatusExAction = null;
ProgressAction = null;
}
/// <summary>
/// starts Downloading, returns immediately
/// </summary>
public void Download()
{
_DownloadTask = Task.Run(() =>
{
AllDASFinished = false;
var param = States.Instance.DownloadStart.Status.DownloadParams;
var global = States.Instance.DownloadStart.Status.GlobalStatusParameters;
var dasFactory = States.Instance.DownloadStart.DASFactory;
var errorCallback = param.ErrorCallback;
//get a list of all the das to download from
var dasList = param.DASList.ToList();
StatusAction?.Invoke((int)StatusValues.Preparing);
//check if we are supposed to download from a unit, but it's not available
var activeDASList = dasFactory.GetDASList();
if (dasList.Exists(das => !activeDASList.Contains(das)))
{
var missingList = dasList.Where(das => !activeDASList.Contains(das)).ToList();
var dr = errorCallback?.Invoke(StatusValues.MissingHardware.ToString(), string.Join(Environment.NewLine, missingList.Select(das => das.SerialNumber).ToList()));
if (DialogResult.OK != dr)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
}
//check if we have any das left to download from
if (!dasList.Any())
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
//check that all units have config data
if (dasList.Exists(das => das.ConfigData == null))
{
//throw error. by state machine definition we should have gone through Config first
StatusExAction?.Invoke((int)StatusValues.NoDataToDownload, dasList.Where(das => das.ConfigData == null).ToArray());
}
//we have das, they have config data. get the event
var oneEvent = GetOneEvent(dasList);
if (null == oneEvent)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
//create folder environment
var eventId = oneEvent.EventId;
var currentTestTestId = param.CurrentTestTestId;
var currentTestTestIdNode = param.CurrentTestTestIdNode;
var currentTestTestDirectory = param.CurrentTestTestDirectory;
var currentTestOriginalTestDirectory = param.CurrentTestOriginalTestDirectory;
var defaultDownloadFolder = param.DefaultDownloadFolder;
var DownloadFolder = Path.Combine(Environment.CurrentDirectory, defaultDownloadFolder);
var ROI = param.ROI;
var recovery = param.Recovery;
var foldersCopied = param.FoldersCopied;
var directory = Path.Combine(DownloadFolder, eventId, currentTestTestId, "Binary");
if (!Directory.Exists(directory))
{
try
{
Directory.CreateDirectory(directory);
}
catch (Exception ex)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
}
StatusAction?.Invoke((int)StatusValues.DownloadDirectory);
var DownloadDirectory = Path.Combine(directory, ROI ? "ROI" : "ALL");
var TestItem = $"{defaultDownloadFolder}\\{eventId}\\{currentTestTestId}\\Binary\\{(ROI ? "ROI" : "ALL")}"; //optimize this
if (recovery && Directory.Exists(directory))
{
if (currentTestTestId != currentTestTestIdNode)
{
//We want to keep ROI and ALL "recovery" downloads in the same path, so use the same "Test Id" node in the path
//Since the .../Binary folder exists, we know that this test has downloaded before, so create a recovery folder
DownloadDirectory = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "Binary", ROI ? "ROI" : "ALL");
}
if ((currentTestTestId != currentTestTestIdNode || currentTestTestDirectory != currentTestOriginalTestDirectory) &&
!foldersCopied)
{
//Either this is a "recovery" folder or the Test Id field was changed in Basic info.
//Either way, the following files need to be copied from the original folder
var copied = false;
var originalTestIdNode = Path.GetFileName(currentTestOriginalTestDirectory);
var sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, Common.Constants.DAS_CONFIGS);
var destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, Common.Constants.DAS_CONFIGS);
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, "Logs");
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "Logs");
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, Common.Constants.REPORT_DIR_NAME);
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, Common.Constants.REPORT_DIR_NAME);
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, "SETUP");
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "SETUP");
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
}
}
var mre = new ManualResetEvent(false);
mre.WaitOne();
if (!CancelEvent.WaitOne(1, false))
{
//check that all the units that should have been configured were
var allDownloaded = Array.TrueForAll(param.DASList, d => dasList.Contains(d));
AllDASFinished = allDownloaded;
CompleteAction?.Invoke();
}
StatusAction?.Invoke((int)StatusValues.Completed);
DoneEvent.Set();
});
}
private Task _DownloadTask = null;
public async Task Cancel()
{
CancelEvent.Set();
StatusAction?.Invoke((int)StatusValues.Cancelling);
//simulate time spent waiting for cancel to be acknowledged
Thread.Sleep(100);
DoneEvent.WaitOne();
StatusAction?.Invoke((int)StatusValues.Cancelled);
}
#region helpers
private static string GetHash(DownloadReport.EventInfo myEvent)
{
return $"{myEvent.TestID}_{myEvent.EventNumber:00}".ToUpper();
}
private IEventInfoAggregate GetOneEvent(List<IDASCommunication> dasList)
{
var eventInfo = new DownloadReport.EventInfo();
var eventIds = new List<string>();
var events = new Dictionary<string, IEventInfoAggregate>();
foreach (var das in dasList)
{
if (null == das.EventInfo || !das.EventInfo.Events.Any()) { continue; }
eventInfo = (DownloadReport.EventInfo)das.EventInfo.Events[0];
var key = GetHash(eventInfo);
if (!eventIds.Contains(key))
{
eventIds.Add(key);
//TODO: Add Events, etc to
//events.Add(key, new IEventInfoAggregate(eventInfo));
}
else
{
events[key].Add(eventInfo);
}
}
IEventInfoAggregate oneEvent = null;
if (events.Count > 0)
{
oneEvent = events[GetHash(eventInfo)];
}
return oneEvent;
}
/// <summary>
/// we don't use move as we don't want to necessarily disturb directories already in the target location, and any files that are already there
/// [but we will overwrite if a source file exists in the destination]
/// </summary>
/// <param name="sourceDirName"></param>
/// <param name="destDirName"></param>
/// <param name="copySubDirs"></param>
/// <param name="param"></param>
/// /// <param name="copied"></param>
/// /// <param name="uploadingData"></param>
public bool DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, DownloadParameters param, ref bool copied, bool uploadingData)
{
try
{
var fileLocations = new List<string>();
var destFileLocations = new List<string>();
var dirs = new List<string>();
dirs.Add(sourceDirName);
while (dirs.Count > 0)
{
var currentDir = dirs[0];
dirs.RemoveAt(0);
var dir = new System.IO.DirectoryInfo(currentDir);
if (!dir.Exists) { continue; }
var files = dir.GetFiles();
fileLocations.AddRange(files.Select(file => file.FullName));
var subDirs = dir.GetDirectories();
dirs.AddRange(subDirs.Select(thisdir => thisdir.FullName));
}
//uri requires folders end with \\
if (!sourceDirName.EndsWith("\\")) { sourceDirName += "\\"; }
var diSourceDir = new System.IO.DirectoryInfo(sourceDirName);
if (!destDirName.EndsWith("\\")) { destDirName += "\\"; }
var curFileUndex = 1;
var existingFiles = new List<string>();
for (var i = fileLocations.Count - 1; i >= 0; i--)
{
var curFile = fileLocations[i];
if (uploadingData)
{
var tokens = curFile.Split('\\');
if (tokens.Contains("Binary"))
{
if (!param.DefaultUploadBinaries)
{
continue;
}
}
else if (tokens.Contains("DASConfigs"))
{
if (!param.DefaultUploadSetups)
{
continue;
}
}
else if (tokens.Contains("SETUP"))
{
if (!param.DefaultUploadSetups)
{
continue;
}
}
else if (tokens.Contains("Reports"))
{
if (!param.DefaultUploadReports)
{
continue;
}
}
else if (tokens.Contains("Logs"))
{
if (!param.DefaultUploadLogs)
{
continue;
}
}
else if (tokens.Contains("Exports"))
{
if (!param.DefaultUploadExports)
{
continue;
}
}
}
var fiCurFile = new System.IO.FileInfo(curFile);
var path1 = new Uri("file:\\\\" + diSourceDir.FullName);
var path2 = new Uri("file:\\\\" + fiCurFile.FullName);
var diff = path1.MakeRelativeUri(path2);
var relPath = diff.OriginalString;
path2 = new Uri("file:\\\\" + destDirName);
path2 = new Uri(path2, relPath);
destFileLocations.Add(path2.LocalPath);
if (System.IO.File.Exists(path2.LocalPath))
{
existingFiles.Add(path2.LocalPath);
}
}
if (existingFiles.Count > 0)
{
var temp = new Uri("file:\\\\" + destDirName);
var dr = param.ErrorCallback?.Invoke(StatusValues.ExistingFiles.ToString(), string.Format(Resources.UploadData_Files_Exist, temp.LocalPath, "\n\n"));
if (DialogResult.OK != dr)
{
return false;
}
}
try
{
for (var i = 0; i < fileLocations.Count && i < destFileLocations.Count; i++)
{
var fiSource = new System.IO.FileInfo(fileLocations[i]);
var fiDest = new System.IO.FileInfo(destFileLocations[destFileLocations.Count - 1 - i]);
if (!System.IO.Directory.Exists(fiDest.Directory.FullName)) { System.IO.Directory.CreateDirectory(fiDest.Directory.FullName); }
System.IO.File.Copy(fiSource.FullName, fiDest.FullName, true);
var percent = 100D * curFileUndex++ / fileLocations.Count;
}
}
catch (Exception ex)
{
APILogger.Log("Upload Failure from ", sourceDirName, " to ", destDirName, ex);
return false;
}
if (!uploadingData) return false;
APILogger.Log("Upload Success from ", sourceDirName, " to ", destDirName);
copied = true;
Thread.Sleep(10);
}
catch (Exception ex)
{
APILogger.Log("Upload Failure from ", sourceDirName, " to ", destDirName, ex);
return false;
}
return true;
}
#endregion
}
}

View File

@@ -0,0 +1,142 @@
using DTS.Common.Interface.DASFactory;
using System.Collections.Generic;
namespace DTS.DASLib.Service.StateMachine
{
public class GlobalStatusInformation : IStatusInfo
{
private static readonly object MyLock = new object();
private List<IDASCommunication> _unitsInRealtime = new List<IDASCommunication>();
/// <summary>
/// returns an array of all devices that are known to be in realtime
/// </summary>
/// <returns></returns>
public IDASCommunication[] GetUnitsInRealtime()
{
lock (MyLock)
{
return _unitsInRealtime.ToArray();
}
}
/// <summary>
/// adds a device to the list of units known to be in realtime
/// </summary>
/// <param name="device"></param>
public void AddUnitInRealtime(IDASCommunication device)
{
lock (MyLock)
{
if (_unitsInRealtime.Contains(device)) { return; }
_unitsInRealtime.Add(device);
}
}
private List<IDASCommunication> _unitsInArm = new List<IDASCommunication>();
/// <summary>
/// returns an array of units known to be in arm
/// </summary>
/// <returns></returns>
public IDASCommunication[] GetUnitsInArm()
{
lock (MyLock)
{
return _unitsInArm.ToArray();
}
}
/// <summary>
/// adds a device to the list of devices known to be in arm
/// </summary>
/// <param name="device"></param>
public void AddUnitInArm(IDASCommunication device)
{
lock (MyLock)
{
if (_unitsInArm.Contains(device))
{
return;
}
_unitsInArm.Add(device);
}
}
private List<IDASCommunication> _unitsAtLowPower = new List<IDASCommunication>();
/// <summary>
/// returns an array of all known devices that are at low power
/// </summary>
/// <returns></returns>
public IDASCommunication[] GetUnitsAtLowPower()
{
lock (MyLock)
{
return _unitsAtLowPower.ToArray();
}
}
/// <summary>
/// adds a unit to the list of units currently at low power
/// removes from high power list if present in it
/// </summary>
/// <param name="das"></param>
public void AddUnitAtLowPower(IDASCommunication das)
{
lock (MyLock)
{
if (_unitsAtLowPower.Contains(das)) { return; }
_unitsAtLowPower.Add(das);
if (!_unitsAtHighPower.Contains(das)) { return; }
_unitsAtHighPower.Remove(das);
}
}
private List<IDASCommunication> _unitsAtHighPower = new List<IDASCommunication>();
/// <summary>
/// returns an array of all units that are known to be at high power
/// </summary>
/// <returns></returns>
public IDASCommunication[] GetUnitsAtHighPower()
{
lock (MyLock)
{
return _unitsAtHighPower.ToArray();
}
}
/// <summary>
/// adds unit to the list of devices known to be at high power
/// removes from the low power list if present
/// </summary>
/// <param name="das"></param>
public void AddUnitAtHighPower(IDASCommunication das)
{
lock (MyLock)
{
if (_unitsAtHighPower.Contains(das)) { return; }
_unitsAtHighPower.Add(das);
if (!_unitsAtLowPower.Contains(das)) { return; }
_unitsAtLowPower.Remove(das);
}
}
/// <summary>
/// whether excitation is on or off, currently this is just
/// controlled by Configure->TurnOffExcitation and Configure->PrepareForDiagnostics
/// the status is only changed currently if all units successfully switch to low power or vice versa
/// </summary>
public bool ExcitationOn { get; set; } = false;
/// <summary>
/// resets all status back to defaults
/// </summary>
public void Reset()
{
ExcitationOn = false;
lock (MyLock)
{
_unitsAtHighPower.Clear();
_unitsAtLowPower.Clear();
_unitsInRealtime.Clear();
_unitsInArm.Clear();
}
}
}
}

View File

@@ -0,0 +1,16 @@
using DTS.Common.Enums.Sensors;
namespace DTS.DASLib.Service.StateMachine
{
public class GlobalStatusParameters : IStatusParameters
{
public bool AllowUDPMulticast { get; set; } = true;
public bool DisableAutoSense { get; set; } = SensorConstants.DisableAutoSense;
public void Reset()
{
AllowUDPMulticast = true;
DisableAutoSense = SensorConstants.DisableAutoSense;
}
}
}

View File

@@ -0,0 +1,185 @@
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DataRecorders;
using System;
using System.Text;
namespace DTS.DASLib.Service.StateMachine
{
public class HardwareDiscoveryParameters
{
public bool ReadIds { get; set; } = false;
/// <summary>
/// ip addresses that we expliticly want to connect to
/// if not specified in known tdas or known slice ip addresses will try connecting as both slice and tdas
/// </summary>
public string[] Addresses { get; set; } = new string[0];
/// <summary>
/// range of address to ping
/// items must be in the form of aaa.bbb.ccc.ddd
/// will ping all addresses iterating from item1 upto the 4th byte on item 2
/// any ip that responds will be added to ips to connect to
/// if it's not known whether it's a SLICE or TDAS then will be added to both lists to connect to
/// </summary>
public Tuple<string, string>[] AddressRanges { get; set; } = new Tuple<string, string>[0];
/// <summary>
/// ip address that are known to be TDAS, so we don't have to try connecting to as a slice
/// </summary>
public string[] KnownTDASIPAddresses { get; set; } = new string[0];
/// <summary>
/// ip addresses that are known to be SLICE, so we don't have to try connecting to as a TDAS
/// </summary>
public string[] KnownSLICEIPAddresses { get; set; } = new string[0];
/// <summary>
/// controls whether we should remain in state or not
/// sometimes the user just wants to see the hardware detected and not go onto resolve channels or any additional
/// actions
/// </summary>
public bool ProceedWhenDone { get; set; } = false;
/// <summary>
/// whether all das are required to be found
/// </summary>
public bool RequireAllDASFound { get; set; } = false;
/// <summary>
/// whether to proceed to download state after completion
/// </summary>
public bool GoToDownload { get; set; } = false;
/// <summary>
/// controls whether we are ping and connecting or just pinging ...
/// </summary>
public bool Connect { get; set; } = false;
/// <summary>
/// controls whether UDP/Multicast discovery should be performed
/// </summary>
public bool UseMulticastDiscover { get; set; } = false;
public int ConnectTimeoutMS { get; set; } = 30000; //30 seconds
/// <summary>
/// whether to run auto-sense or not
/// note that DisableAutoSense overrules this setting
/// </summary>
public bool RunAutoSense { get; set; } = true;
/// <summary>
/// specific device to query, when present
/// this is used to signal that there's a device already attached that
/// we want to requery
/// this is done when the device configuration is changed
/// </summary>
public IDASCommunication RequeryDevice { get; set; } = null;
/// <summary>
/// serial numbers that are required to connect
/// </summary>
public string[] RequiredSerials { get; set; } = new string[0];
/// <summary>
/// controls whether to perform hardware checks (voltage/memory/firmware/etc)
/// after connecting to hardware
/// </summary>
public bool DoHardwareChecks { get; set; } = false;
/// <summary>
/// hardware that we expect to be connected once ping and connect completes
/// used as part of DoHardwareChecks
/// </summary>
public IDASHardware[] ExpectedHardware { get; set; } = new IDASHardware[0];
public delegate bool UnitIsInDbDelegate(IDASCommunication das);
public UnitIsInDbDelegate UnitIsInDbQuery { get; set; } = null;
public delegate string FirmwareExpectedVersionDelegate(IDASCommunication das);
public FirmwareExpectedVersionDelegate UnitExpectedFirmwareQuery { get; set; } = null;
public delegate bool IsCalDateExpiredDelegate(IDASCommunication das);
public IsCalDateExpiredDelegate CalDateExpiredQuery { get; set; } = null;
public delegate long UnitExpectedMaxMemoryDelegate(IDASCommunication das);
public UnitExpectedMaxMemoryDelegate UnitExpectedMaxMemoryQuery { get; set; } = null;
public delegate void UpdateMaxMemoryDelegate(IDASCommunication das);
public UpdateMaxMemoryDelegate UpdateMaxMemoryAction { get; set; } = null;
/// <summary>
/// whether to check the input/battery voltage for units or not
/// </summary>
public bool DoVoltageChecks { get; set; } = true;
public void Reset()
{
ReadIds = false;
Addresses = new string[0];
AddressRanges = new Tuple<string, string>[0];
ProceedWhenDone = false;
RequireAllDASFound = false;
KnownSLICEIPAddresses = new string[0];
KnownTDASIPAddresses = new string[0];
RequeryDevice = null;
RequiredSerials = new string[0];
DoHardwareChecks = false;
ExpectedHardware = new IDASHardware[0];
UnitIsInDbQuery = null;
UnitExpectedFirmwareQuery = null;
CalDateExpiredQuery = null;
UnitExpectedMaxMemoryQuery = null;
UpdateMaxMemoryAction = null;
DoVoltageChecks = true;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"ReadIds={ReadIds.ToString()}");
sb.AppendLine($"Addresses={string.Join(",", Addresses)}");
sb.Append("AddressRanges=");
for (int i = 0; i < AddressRanges.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.Append($"({AddressRanges[i].Item1},{AddressRanges[i].Item2})");
}
sb.AppendLine();
sb.AppendLine($"ProceedWhenDone={ProceedWhenDone.ToString()}");
sb.AppendLine($"RequireAllDASFound={RequireAllDASFound.ToString()}");
sb.AppendLine($"KnownSLICEIPAddresses={string.Join(",", KnownSLICEIPAddresses)}");
sb.AppendLine($"KnownTDASIPAddresses={string.Join(",", KnownTDASIPAddresses)}");
sb.Append("RequiryDevice=");
if (null == RequeryDevice)
{
sb.AppendLine("null");
}
else
{
sb.AppendLine(RequeryDevice.SerialNumber);
}
sb.AppendLine($"RequiredSerials={string.Join(",", RequiredSerials)}");
sb.AppendLine($"DoHardwareChecks={DoHardwareChecks.ToString()}");
sb.Append("ExpectedHardware=");
for (var i = 0; i < ExpectedHardware.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.Append(ExpectedHardware[i].SerialNumber);
}
sb.AppendLine();
sb.Append("UnitIsInDbQuery=");
sb.AppendLine(null == UnitIsInDbQuery ? "[null]" : "[defined]");
sb.Append("UnitExpectedFirmwareQuery=");
sb.AppendLine(null == UnitExpectedFirmwareQuery ? "[null]" : "[defined]");
sb.Append("CalDateExpiredQuery=");
sb.AppendLine(null == CalDateExpiredQuery ? "[null]" : "[defined]");
sb.Append("UnitExpectedMaxMemoryQuery=");
sb.AppendLine(null == UnitExpectedMaxMemoryQuery ? "[null]" : "[defined]");
sb.Append("UpdateMaxMemoryAction=");
sb.AppendLine(null == UpdateMaxMemoryAction ? "[null]" : "[defined]");
sb.Append("DoVoltageChecks=");
sb.AppendLine(DoVoltageChecks.ToString());
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace DTS.DASLib.Service.StateMachine
{
public interface IStatusInfo
{
void Reset();
}
}

View File

@@ -0,0 +1,7 @@
namespace DTS.DASLib.Service.StateMachine
{
public interface IStatusParameters
{
void Reset();
}
}

View File

@@ -0,0 +1,36 @@
using DTS.Common.Interface.DASFactory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static DTS.DASLib.Service.ServiceBase;
namespace DTS.DASLib.Service.StateMachine
{
public class RealtimeParameters : IStatusParameters
{
public List<IDASCommunication> UnitsToStartRealtime { get; set; }
public int RealtimeDelayBetweenPollsInMilliSecond { get; set; }
public bool AllowMultipleSampleRealtime { get; set; }
public bool UseSingleSampleMode { get; set; }
public List<int> ModuleIndices { get; set; }
public Dictionary<IDASCommunication, byte[]> IdasToActiveChannels { get; set; }
public double RealtimeSampleRate { get; set; }
public byte RealtimeSampleRateAAFilterRatio { get; set; }
public bool SliceTurnOffAAFRealtime { get; set; }
public void Reset()
{
UnitsToStartRealtime = new List<IDASCommunication>();
RealtimeDelayBetweenPollsInMilliSecond = 0;
AllowMultipleSampleRealtime = false;
UseSingleSampleMode = false;
ModuleIndices = new List<int>();
IdasToActiveChannels = new Dictionary<IDASCommunication, byte[]>();
RealtimeSampleRate = 0.0;
RealtimeSampleRateAAFilterRatio = 0;
SliceTurnOffAAFRealtime = false;
}
}
}

View File

@@ -0,0 +1,171 @@
using DTS.Common.Enums.DASFactory;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static DTS.DASLib.Service.ServiceBase;
namespace DTS.DASLib.Service.StateMachine
{
public class RealtimeStatusInformation : IStatusInfo
{
private readonly ManualResetEvent stopRealtimeEvent = new ManualResetEvent(true);
//this is a wait handle that is set when we get a callback from realtime service saying it's done
private readonly ManualResetEvent realtimeDoneFromService = new ManualResetEvent(false);
/// <summary>
/// action to take on completion of Realtime start
/// </summary>
public Action CompleteAction { get; set; }
public void Reset()
{
CompleteAction = null;
realtimeDoneFromService.Reset();
stopRealtimeEvent.Set();
CouldNotStartRealtime = false;
}
public Action<double, double> SetRealtimeSampleRateAAF { get; set; }
public bool CouldNotStartRealtime { get; set; }
public Callback StartRealtimeCallback { get; set; }
/// <summary>
/// returns the Anti-Alias Filter (AAF) for the DAS in question
/// this is dependent on the sample rate for the hardware and a ratio in the config file
/// (RealtimeSampleRateAAFilterRatio)
/// </summary>
/// <param name="das"></param>
/// <returns></returns>
private float GetRealtimeAAFForHardware(IDASCommunication idas, double samplerate)
{
var param = States.Instance.Realtime.Status.RealtimeParams;
if (param.SliceTurnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(idas))
{
return Common.Constants.SLICE2_NO_AAF_REALTIME_RATE;
}
//for now there is no additional work done
if (0 == param.RealtimeSampleRateAAFilterRatio) { return Convert.ToSingle(samplerate); }
return (float)samplerate / param.RealtimeSampleRateAAFilterRatio;
}
public bool IsInRealtime
{
get { return !stopRealtimeEvent.WaitOne(2, false); }
}
public void StopRealtime()
{
stopRealtimeEvent.Set();
}
Task _realtimeTask;
public void StartRealtime()
{
_realtimeTask = Task.Run(() =>
{
var param = States.Instance.Realtime.Status.RealtimeParams;
var status = States.Instance.Realtime.Status.RealtimeStatus;
var global = States.Instance.Realtime.Status.GlobalStatusParameters;
StartRealtime(param.UnitsToStartRealtime, param.ModuleIndices, param.UseSingleSampleMode,
param.RealtimeSampleRate, param.RealtimeDelayBetweenPollsInMilliSecond, param.AllowMultipleSampleRealtime, status.SetRealtimeSampleRateAAF, status.CompleteAction,
param.IdasToActiveChannels, status.StartRealtimeCallback);
});
}
public void StartRealtime(List<IDASCommunication> ldas, List<int> moduleArrayIndicies, bool useSingleSampleMode,
double realtimeSampleRate, int realtimeDelayBetweenPolls, bool allowMultipleSampleRealtime, Action<double, double> SetRealtimeSampleRateAAF, Action CompleteAction,
Dictionary<IDASCommunication, byte[]> idasToActiveChannels, Callback StartRealtimeCallback
)
{
var startRealtimeCallback = new Callback(StartRealtimeCallback);
startRealtimeCallback += (data) =>
{
if (data.Status == CallbackData.CallbackStatus.AllFinished)
{
//service is done, mark it
realtimeDoneFromService.Set();
}
if (data.Status == CallbackData.CallbackStatus.Failure)
{
}
};
stopRealtimeEvent.Reset();
using (var realtimeService = new RealtimeService())
{
try
{
if (ldas.Count > 0)
{
if (useSingleSampleMode)
{
realtimeDoneFromService.Reset();
realtimeService.StartActivePolling(ldas, startRealtimeCallback, ldas,
stopRealtimeEvent, idasToActiveChannels);
SetRealtimeSampleRateAAF(double.NaN, double.NaN);
}
else
{
realtimeDoneFromService.Reset();
realtimeService.Start(ldas,
Convert.ToInt32(realtimeSampleRate),
realtimeDelayBetweenPolls,
startRealtimeCallback,
ldas,
allowMultipleSampleRealtime,
moduleArrayIndicies.ToArray(),
stopRealtimeEvent,
idasToActiveChannels,
GetRealtimeAAFForHardware);
if (ldas.Any())
{
SetRealtimeSampleRateAAF(realtimeSampleRate,
GetRealtimeAAFForHardware(ldas[0], realtimeSampleRate));
}
else
{
SetRealtimeSampleRateAAF(double.NaN, double.NaN);
}
}
}
}
catch (Exception ex)
{
APILogger.Log(ex);
CouldNotStartRealtime = true;
//there was an exception , stop the realtime and service
stopRealtimeEvent.Set();
realtimeDoneFromService.Set();
}
finally
{
CompleteAction();
}
var mr = new ManualResetEvent(false);
realtimeService.ServiceAvailable += delegate
{
realtimeService.ServiceAvailable -= delegate { };
mr.Set();
};
while (!stopRealtimeEvent.WaitOne(30, false) && !mr.WaitOne(1, false))
{
Thread.Sleep(1);
}
//either realtime event was signalled to stop (datapro) or the service actually did already stop
//go and set the event handle to stopped to notify service in case it's running
stopRealtimeEvent.Set();
//now wait for the event from the service saying it's stopped
realtimeDoneFromService.WaitOne();
}
}
}
}

View File

@@ -0,0 +1,30 @@
namespace DTS.DASLib.Service.StateMachine
{
public class Status
{
public HardwareDiscoveryParameters HardwareDiscoveryParams = new HardwareDiscoveryParameters();
public HardwareDiscoveryStatusInfo HardwareDiscoveryStatusInfo = new HardwareDiscoveryStatusInfo();
public GlobalStatusInformation GlobalStatusInformation = new GlobalStatusInformation();
public GlobalStatusParameters GlobalStatusParameters = new GlobalStatusParameters();
public ConfigureStatusInformation ConfigureStatus = new ConfigureStatusInformation();
public ConfigureStatusParameters ConfigureParameters = new ConfigureStatusParameters();
public RealtimeStatusInformation RealtimeStatus = new RealtimeStatusInformation();
public RealtimeParameters RealtimeParams = new RealtimeParameters();
public DiagnoseParameters DiagnoseParams = new DiagnoseParameters();
public DownloadParameters DownloadParams = new DownloadParameters();
public DownloadStatusInformation DownloadStatusInfo = new DownloadStatusInformation();
public void Reset()
{
GlobalStatusParameters.Reset();
GlobalStatusInformation.Reset();
HardwareDiscoveryParams.Reset();
HardwareDiscoveryStatusInfo.Reset();
ConfigureStatus.Reset();
ConfigureParameters.Reset();
RealtimeStatus.Reset();
RealtimeParams.Reset();
DiagnoseParams.Reset();
}
}
}