1608 lines
68 KiB
Plaintext
1608 lines
68 KiB
Plaintext
using DTS.Common.Enums;
|
|
using DTS.Common.Enums.Sensors;
|
|
using DTS.Common.Interface.DASFactory;
|
|
using DTS.Common.Interface.DASFactory.Config;
|
|
using DTS.Common.Interface.DataRecorders;
|
|
using DTS.Common.Interface.StatusAndProgressBar;
|
|
using DTS.Common.Utilities.Logging;
|
|
using DTS.Common.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DTS.DASLib.Service.StateMachine
|
|
{
|
|
public class HardwareDiscoveryStatusInfo : IStatusInfo
|
|
{
|
|
/// <summary>
|
|
/// if we were told of DAS to connect to,
|
|
/// this will be true if all the das were connected
|
|
/// </summary>
|
|
public bool AllDASFound { get; set; } = false;
|
|
/// <summary>
|
|
/// if any of the units we connected to were in Armed state, this will be true
|
|
/// </summary>
|
|
public bool SomeUnitsInArmState { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// list of all devices we successfully connected to
|
|
/// [note all IDASCommunication are references, not copies]
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsConnected { get; private set; } = new IDASCommunication[0];
|
|
|
|
/// <summary>
|
|
/// list of all devices we successfully got the configuration of
|
|
/// [note all IDASCommunication are references, not copies]
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithConfiguration { get; private set; } = new IDASCommunication[0];
|
|
/// <summary>
|
|
/// all possible states the
|
|
/// </summary>
|
|
public enum StatusValues
|
|
{
|
|
Pinging,
|
|
Connecting,
|
|
Cancelling,
|
|
Cancelled,
|
|
Complete,
|
|
UDPUnavailable,
|
|
UnitIsArmed,
|
|
AllUnitsFound,
|
|
NotAllUnitsFound,
|
|
MulticastDiscovery,
|
|
DiscoveredDevice,
|
|
PingStatus,
|
|
Connected,
|
|
FailedToGetConfiguration,
|
|
UnitGotConfiguration,
|
|
GettingConfiguration,
|
|
AutoSense,
|
|
AutoSenseFailed,
|
|
AutoSenseSuccess,
|
|
AutoSenseComplete,
|
|
FailedHardwareChecks,
|
|
PerformingHardwareChecks,
|
|
G5DockLost,
|
|
CheckingDASStates,
|
|
UnitIsAutoArmed,
|
|
UnitIsReadyToStream,
|
|
UnitIsInRealtime,
|
|
UnitIsStreaming,
|
|
UnitNotInDb,
|
|
UnitHasWrongFirmware,
|
|
CalDateExpired,
|
|
UnitNANDIssue,
|
|
UnitWithWrongMaxMemory,
|
|
UnitWithWrongChannelCount,
|
|
UnitWithChannelTypeIssue,
|
|
ExtraUnit,
|
|
UnitIsInArm,
|
|
VoltageCheckFinished,
|
|
VoltageCheckSuccess,
|
|
VoltageCheckFailure
|
|
}
|
|
/// <summary>
|
|
/// signals cancel was requested
|
|
/// </summary>
|
|
private ManualResetEvent CancelEvent = new ManualResetEvent(false);
|
|
/// <summary>
|
|
/// signals when ping and connect is finished
|
|
/// </summary>
|
|
internal 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
|
|
/// </summary>
|
|
public SetProgressValueDelegate ProgressAction { get; set; }
|
|
/// <summary>
|
|
/// action to take when status is notified
|
|
/// </summary>
|
|
public StatusIntDelegate StatusAction { get; set; }
|
|
/// <summary>
|
|
/// action to take when extended status is notified
|
|
/// </summary>
|
|
public StatusExIntDelegate StatusExAction { get; set; }
|
|
|
|
/// <summary>
|
|
/// this is an array of units that were detected without docks that should have had docks
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithLostDocks { get; private set; } = new IDASCommunication[0];
|
|
|
|
/// <summary>
|
|
/// this is an array of units that were expected but not available
|
|
/// </summary>
|
|
public IDASHardware[] UnitsMissing { get; private set; } = new IDASHardware[0];
|
|
private void AddMissingHardware(IDASHardware das)
|
|
{
|
|
if (UnitsMissing.Contains(das)) { return; }
|
|
var list = new List<IDASHardware>();
|
|
list.AddRange(UnitsMissing);
|
|
list.Add(das);
|
|
UnitsMissing = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.NotAllUnitsFound, das);
|
|
}
|
|
|
|
/// <summary>
|
|
/// these are units which are connected but are not in the db
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsNotInDb { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitNotInDb(IDASCommunication das)
|
|
{
|
|
if (UnitsNotInDb.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsNotInDb);
|
|
list.Add(das);
|
|
UnitsNotInDb = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitNotInDb, das);
|
|
}
|
|
|
|
/// <summary>
|
|
/// this is an array of units that were detected with auto arm set
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsInAutoArm { get; private set; } = new IDASCommunication[0];
|
|
/// <summary>
|
|
/// adds a unit into the list of units that are in autoarm state
|
|
/// </summary>
|
|
/// <param name="das"></param>
|
|
private void AddUnitInAutoArm(IDASCommunication das)
|
|
{
|
|
if (UnitsInAutoArm.Contains(das)) { return; }//already present
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsInAutoArm);
|
|
list.Add(das);
|
|
UnitsInAutoArm = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsAutoArmed, das);
|
|
}
|
|
|
|
/// <summary>
|
|
/// this is an array of units that were detected ready to stream (S6A)
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsReadyToStream { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitReadyToStream(IDASCommunication das)
|
|
{
|
|
if (UnitsReadyToStream.Contains(das))
|
|
{
|
|
return; //already contains it
|
|
}
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsReadyToStream);
|
|
list.Add(das);
|
|
UnitsReadyToStream = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsReadyToStream, das);
|
|
}
|
|
|
|
public IDASCommunication[] UnitsStreaming { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitStreaming(IDASCommunication das)
|
|
{
|
|
if (UnitsStreaming.Contains(das))
|
|
{
|
|
return; //already contains it ...
|
|
}
|
|
var list = new List<IDASCommunication>();
|
|
list.Add(das);
|
|
UnitsStreaming = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsStreaming, das);
|
|
}
|
|
|
|
//public IDASCommunication[] UnitsInArm { get; private set; } = new IDASCommunication [0];
|
|
//private void AddUnitInArm(IDASCommunication das)
|
|
//{
|
|
// if( UnitsInArm.Contains(das) ){ return; }
|
|
// var list = new List<IDASCommunication>();
|
|
// list.AddRange(UnitsInArm);
|
|
// list.Add(das);
|
|
// UnitsInArm = list.ToArray();
|
|
// StatusExAction?.Invoke((int) StatusValues.UnitIsArmed, das);
|
|
//}
|
|
|
|
//public IDASCommunication[] UnitsInRealtime { get; private set; } = new IDASCommunication[0];
|
|
//private void AddUnitInRealtime(IDASCommunication das)
|
|
//{
|
|
// if( UnitsInRealtime.Contains(das)){ return; } //already contains it
|
|
// var list = new List<IDASCommunication>();
|
|
// list.AddRange(UnitsInRealtime);
|
|
// list.Add(das);
|
|
// UnitsInRealtime = list.ToArray();
|
|
// StatusExAction?.Invoke((int) StatusValues.UnitIsInRealtime, das);
|
|
//}
|
|
/// <summary>
|
|
/// this array holds any units that have firmware of a different version than expected
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithWrongFirmware { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitWithWrongFirmware(IDASCommunication das)
|
|
{
|
|
if (UnitsWithWrongFirmware.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithWrongFirmware);
|
|
list.Add(das);
|
|
UnitsWithWrongFirmware = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitHasWrongFirmware, das);
|
|
}
|
|
/// <summary>
|
|
/// this array holds any units that have expired cal dates
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithExpiredCalDates { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitWithExpiredCalDate(IDASCommunication das)
|
|
{
|
|
if (UnitsWithExpiredCalDates.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithExpiredCalDates);
|
|
UnitsWithExpiredCalDates = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.CalDateExpired, das);
|
|
}
|
|
/// <summary>
|
|
/// this array holds any units with NAND/Flash issues
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithNANDIssues { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitWithNANDIssue(IDASCommunication das)
|
|
{
|
|
if (UnitsWithNANDIssues.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithNANDIssues);
|
|
list.Add(das);
|
|
UnitsWithNANDIssues = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitNANDIssue, das);
|
|
}
|
|
|
|
public IDASCommunication[] UnitsWithWrongMaxMemory { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitWithWrongMaxMemory(IDASCommunication das)
|
|
{
|
|
if (UnitsWithWrongMaxMemory.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithWrongMaxMemory);
|
|
list.Add(das);
|
|
UnitsWithWrongMaxMemory = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitWithWrongMaxMemory, das);
|
|
}
|
|
|
|
public IDASCommunication[] UnitsWithWrongChannelCount { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitChannelCountIssue(IDASCommunication das)
|
|
{
|
|
if (UnitsWithWrongChannelCount.Contains(das)) { return; }
|
|
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithWrongChannelCount);
|
|
list.Add(das);
|
|
UnitsWithWrongChannelCount = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitWithWrongChannelCount, das);
|
|
}
|
|
|
|
/// <summary>
|
|
/// this array holds units where the channel types don't match expectations,
|
|
/// so either supports different bridges or excitation
|
|
/// </summary>
|
|
public IDASCommunication[] UnitsWithChannelTypeIssue { get; private set; } = new IDASCommunication[0];
|
|
private void AddUnitWithChannelTypeIssue(IDASCommunication das)
|
|
{
|
|
if (UnitsWithChannelTypeIssue.Contains(das)) { return; }
|
|
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(UnitsWithChannelTypeIssue);
|
|
list.Add(das);
|
|
UnitsWithChannelTypeIssue = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.UnitWithChannelTypeIssue, das);
|
|
}
|
|
/// <summary>
|
|
/// indicates there are hardware channels associated with the test that are available
|
|
/// </summary>
|
|
public bool HaveChannels { get; private set; } = false;
|
|
/// <summary>
|
|
/// this is an array of units that are connected which were not expected
|
|
/// </summary>
|
|
public IDASCommunication[] ExtraUnits { get; private set; } = new IDASCommunication[0];
|
|
private void AddExtraUnit(IDASCommunication das)
|
|
{
|
|
if (ExtraUnits.Contains(das)) { return; }
|
|
var list = new List<IDASCommunication>();
|
|
list.AddRange(ExtraUnits);
|
|
list.Add(das);
|
|
ExtraUnits = list.ToArray();
|
|
StatusExAction?.Invoke((int)StatusValues.ExtraUnit, das);
|
|
}
|
|
/// <summary>
|
|
/// this keeps track of the ping task that is run when ping starts
|
|
/// </summary>
|
|
private Task _PingTask = null;
|
|
/// <summary>
|
|
/// this keeps track of the RequeryDevice task taht is running when RequeryDevice starts
|
|
/// </summary>
|
|
private Task _RequeryTask = null;
|
|
/// <summary>
|
|
/// sets the cancel event, waits for done to be signaled,
|
|
/// sets status cancelled
|
|
/// </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>
|
|
/// returns true if multicast should be run
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool DoMultiCast()
|
|
{
|
|
return States.Instance.HardwareDiscoveryStart.Status.GlobalStatusParameters.AllowUDPMulticast &&
|
|
States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams.UseMulticastDiscover;
|
|
}
|
|
|
|
private const int MULTICAST_DISCOVERY_INTERVAL = 100;
|
|
private const int EXPECTED_MULTICAST_TIME = 6000;
|
|
/// <summary>
|
|
/// performs the multi cast discovery via UDP
|
|
/// </summary>
|
|
private void DoMulticastDiscovery()
|
|
{
|
|
_discoveredDevices.Clear();
|
|
StatusAction.Invoke((int)StatusValues.MulticastDiscovery);
|
|
try
|
|
{
|
|
if (null != States.Instance.HardwareDiscoveryStart.DASFactory)
|
|
{
|
|
var mre = new ManualResetEvent(false);
|
|
Task.Run(() =>
|
|
{
|
|
var ct = new CancellationToken();
|
|
var discovered = States.Instance.HardwareDiscoveryStart.DASFactory.AutoDiscoverMulticast(ct);
|
|
if (discovered.Any())
|
|
{
|
|
foreach (var device in discovered)
|
|
{
|
|
AddDevice(device);
|
|
}
|
|
}
|
|
|
|
mre.Set();
|
|
});
|
|
var timeWaited = 0;
|
|
while (!mre.WaitOne(MULTICAST_DISCOVERY_INTERVAL, false))
|
|
{
|
|
timeWaited += MULTICAST_DISCOVERY_INTERVAL;
|
|
var percent = 100D * timeWaited / EXPECTED_MULTICAST_TIME;
|
|
percent = Math.Min(percent, 100);
|
|
ProgressAction?.Invoke(percent);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
StatusAction?.Invoke((int)StatusValues.UDPUnavailable);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// holds all the devices discovered via UDP/multicast
|
|
/// </summary>
|
|
private List<IDiscoveredDevice> _discoveredDevices = new List<IDiscoveredDevice>();
|
|
|
|
/// <summary>
|
|
/// adds a device to the list of multicast discovered devices
|
|
/// signals consumer that a device was discovered
|
|
/// </summary>
|
|
/// <param name="device"></param>
|
|
private void AddDevice(IDiscoveredDevice device)
|
|
{
|
|
_discoveredDevices.Add(device);
|
|
StatusExAction?.Invoke((int)StatusValues.DiscoveredDevice, device);
|
|
}
|
|
/// <summary>
|
|
/// returns a list of all ips that we should try to ping and then potentially connect to
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private IList<string> GetIpList()
|
|
{
|
|
var list = new List<string>();
|
|
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
if (null != param.Addresses)
|
|
{
|
|
list.AddRange(param.Addresses);
|
|
}
|
|
|
|
if (param.AddressRanges.Any())
|
|
{
|
|
foreach (var addressRange in param.AddressRanges)
|
|
{
|
|
try
|
|
{
|
|
var start = GetIPAddress(addressRange.Item1);
|
|
var end = GetIPAddress(addressRange.Item2);
|
|
//for now just assume we only care about the last byte ...?
|
|
for (var lastByte = start[3]; lastByte < end[3]; lastByte++)
|
|
{
|
|
list.Add($"{start[0]}.{start[1]}.{start[2]}.{lastByte}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log($"skipping address range {addressRange.Item1}-{addressRange.Item2} {ex}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
/// <summary>
|
|
/// converts a string in the form of aaa.bbb.ccc.ddd to byte,byte,byte,byte
|
|
/// throws invalidcastexception if not in the right form
|
|
/// </summary>
|
|
/// <param name="ip"></param>
|
|
/// <returns></returns>
|
|
private byte[] GetIPAddress(string ip)
|
|
{
|
|
var tokens = ip.Split(new[] { '.' });
|
|
if (4 == tokens.Length)
|
|
{
|
|
return new[] { Convert.ToByte(tokens[0]), Convert.ToByte(tokens[1]), Convert.ToByte(tokens[2]), Convert.ToByte(tokens[3]) };
|
|
}
|
|
|
|
throw new InvalidCastException($"{ip} is not in the form of aaa.bbb.ccc.ddd");
|
|
}
|
|
/// <summary>
|
|
/// gets list of all known tdas ip addresses
|
|
/// an ip address is known as ip if we've auto discovered it or we were told ahead of time it is TDAS
|
|
/// knowing that an ip address is slice or TDAS can save us having to try connecting as both
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private HashSet<string> GetTDASIPHash()
|
|
{
|
|
var hash = new HashSet<string>();
|
|
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
if (null != param.KnownTDASIPAddresses)
|
|
{
|
|
foreach (var ip in param.KnownTDASIPAddresses)
|
|
{
|
|
hash.Add(ip);
|
|
}
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
/// <summary>
|
|
/// returns all known slice ip addresses
|
|
/// an ip address is known as slice if we were told about it ahead of time or we've auto discovered it
|
|
/// knowning that an ip address is slice or TDAS can save us having to try connecting as both
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private HashSet<string> GetSLICEIPHash()
|
|
{
|
|
var hash = new HashSet<string>();
|
|
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
if (null != param.KnownSLICEIPAddresses)
|
|
{
|
|
foreach (var ip in param.KnownSLICEIPAddresses)
|
|
{
|
|
hash.Add(ip);
|
|
}
|
|
}
|
|
|
|
foreach (var device in _discoveredDevices)
|
|
{
|
|
switch (device.DevClass)
|
|
{
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.Slice6:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.SDB:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.ECM:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.S6DB:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.Slice6Air:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.PowerPro:
|
|
//all these devices fall into the slice communication family
|
|
var ip = device.Ip.ToLower();
|
|
if (!hash.Contains(ip))
|
|
{
|
|
hash.Add(ip);
|
|
}
|
|
break;
|
|
//these are probably slice, but we don't technically know
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.Unknown:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.NextOne:
|
|
case Common.Enums.DASFactory.DFConstantsAndEnums.MultiCastDeviceClasses.Any:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// requires a specific device, returns immediately
|
|
/// </summary>
|
|
internal void RequeryDevice()
|
|
{
|
|
_RequeryTask = Task.Run(() =>
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
var global = States.Instance.HardwareDiscoveryStart.Status.GlobalStatusParameters;
|
|
var dasFactory = States.Instance.HardwareDiscoveryStart.DASFactory;
|
|
|
|
if (!CancelEvent.WaitOne(1, false))
|
|
{
|
|
if (GetConfiguration(dasFactory))
|
|
{
|
|
CompleteAction?.Invoke();
|
|
}
|
|
else
|
|
{
|
|
//getconfiguration failed ...
|
|
StatusAction?.Invoke((int)StatusValues.FailedToGetConfiguration);
|
|
}
|
|
}
|
|
|
|
StatusAction?.Invoke((int)StatusValues.Complete);
|
|
DoneEvent.Set();
|
|
});
|
|
}
|
|
|
|
private void CheckUnitsNotInDb()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var unit in UnitsWithConfiguration)
|
|
{
|
|
if (!param.UnitIsInDbQuery(unit))
|
|
{
|
|
AddUnitNotInDb(unit);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckFirmware()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var unit in UnitsWithConfiguration)
|
|
{
|
|
if (param.UnitIsInDbQuery(unit))
|
|
{
|
|
if (unit.FirmwareVersion != param.UnitExpectedFirmwareQuery(unit))
|
|
{
|
|
AddUnitWithWrongFirmware(unit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckChannelCounts()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (das.IsEthernetDistributor()) { continue; }
|
|
|
|
if (null == das.ConfigData.Modules)
|
|
{
|
|
AddUnitChannelCountIssue(das);
|
|
continue;
|
|
}
|
|
var matches = from hardware in param.ExpectedHardware where hardware.SerialNumber == das.SerialNumber select hardware;
|
|
|
|
if (!matches.Any()) { continue; } //doesn't match any expected hardware in our test
|
|
|
|
var expected = matches.First();
|
|
var channels = expected.GetIHardwareChannels();
|
|
var idaschannels = new List<IDASChannel>();
|
|
foreach (var module in das.ConfigData.Modules)
|
|
{
|
|
idaschannels.AddRange(module.Channels);
|
|
}
|
|
if (channels.Length != das.NumberOfChannels())
|
|
{
|
|
AddUnitChannelCountIssue(das);
|
|
}
|
|
else
|
|
{
|
|
var isOK = true;
|
|
for (var i = 0; i < channels.Length && isOK; i++)
|
|
{
|
|
var idaschannel = idaschannels[i];
|
|
switch (idaschannel)
|
|
{
|
|
case AnalogInputDASChannel _aic:
|
|
if (_aic.DigitalInputChannel)
|
|
{
|
|
if (!channels[i].IsDigitalIn)
|
|
{
|
|
isOK = false;
|
|
}
|
|
else
|
|
{
|
|
isOK = CheckAnalogChannel(_aic, channels[i]);
|
|
}
|
|
}
|
|
break;
|
|
case OutputSquibChannel _squib:
|
|
if (!channels[i].IsSquib) { isOK = false; }
|
|
break;
|
|
case OutputTOMDigitalChannel _dOut:
|
|
break;
|
|
case TimestampDASChannel _time:
|
|
break;
|
|
}
|
|
}
|
|
if (!isOK)
|
|
{
|
|
AddUnitWithChannelTypeIssue(das);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private bool CheckAnalogChannel(AnalogInputDASChannel aic, IHardwareChannel hardwareChannel)
|
|
{
|
|
var analogBridges = new[]
|
|
{
|
|
SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge,
|
|
SensorConstants.BridgeType.HalfBridge_SigPlus, SensorConstants.BridgeType.IEPE,
|
|
SensorConstants.BridgeType.QuarterBridge
|
|
};
|
|
foreach (var bridge in analogBridges)
|
|
{
|
|
var aicSupports = aic.SupportedBridges.Contains(bridge);
|
|
var hardwareChannelSupports = hardwareChannel.IsSupportedBridgeType(bridge);
|
|
if (aicSupports != hardwareChannelSupports) { return false; }
|
|
}
|
|
|
|
var excitations = new[]
|
|
{
|
|
ExcitationVoltageOptions.ExcitationVoltageOption.Volt10,
|
|
ExcitationVoltageOptions.ExcitationVoltageOption.Volt2_5,
|
|
ExcitationVoltageOptions.ExcitationVoltageOption.Volt2,
|
|
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5
|
|
};
|
|
foreach (var excitation in excitations)
|
|
{
|
|
var aicSupports = aic.SupportedExcitation.Contains(excitation);
|
|
var hardwareChannelSupports = hardwareChannel.IsSupportedExcitation(excitation);
|
|
if (aicSupports != hardwareChannelSupports)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
private void CheckCalDates()
|
|
{
|
|
var param = States.Instance.ConfigureStart.Status.HardwareDiscoveryParams;
|
|
foreach (var unit in UnitsWithConfiguration)
|
|
{
|
|
if (param.CalDateExpiredQuery(unit))
|
|
{
|
|
AddUnitWithExpiredCalDate(unit);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckModuleArrayIndices()
|
|
{
|
|
var param = States.Instance.ConfigureStart.Status.HardwareDiscoveryParams;
|
|
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (!(das is EthernetTDAS ethernetTDAS))
|
|
{
|
|
continue;
|
|
}
|
|
var matches = from unit in param.ExpectedHardware where unit.SerialNumber == das.SerialNumber select unit;
|
|
if (!matches.Any())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (null == das.ConfigData || null == das.ConfigData.Modules)
|
|
{
|
|
continue;//caught by other checks we only want to check module array indices
|
|
}
|
|
var expected = matches.First();
|
|
var expectedChannels = expected.GetIHardwareChannels();
|
|
var channelIDX = 0;
|
|
var ok = true;
|
|
foreach (var m in das.ConfigData.Modules)
|
|
{
|
|
if (!ok) { break; }
|
|
foreach (var channel in m.Channels)
|
|
{
|
|
var expectedChannel = expectedChannels[channelIDX];
|
|
if (m.SerialNumber() != expectedChannel.ModuleSerialNumber)
|
|
{
|
|
ok = false;
|
|
break;
|
|
}
|
|
channelIDX++;
|
|
}
|
|
}
|
|
if (!ok)
|
|
{
|
|
AddUnitWithChannelTypeIssue(das);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckNAND()
|
|
{
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (das is EthernetSlice6 || das is EthernetSlice6Air || das is EthernetSlice6AirBridge) { continue; }
|
|
|
|
if (0 == das.MaxMemory())
|
|
{
|
|
AddUnitWithNANDIssue(das);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckStorage()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (!param.UnitIsInDbQuery(das)) { continue; }
|
|
var actualMaxMemory = das.MaxMemory();
|
|
var expectedMaxMemory = param.UnitExpectedMaxMemoryQuery(das);
|
|
if (0 == expectedMaxMemory)
|
|
{
|
|
param.UpdateMaxMemoryAction(das);
|
|
}
|
|
else if (expectedMaxMemory != actualMaxMemory)
|
|
{
|
|
AddUnitWithWrongMaxMemory(das);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckHaveChannels()
|
|
{
|
|
HaveChannels = false;
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var expected in param.ExpectedHardware)
|
|
{
|
|
var matches = from unit in UnitsWithConfiguration where unit.SerialNumber == expected.SerialNumber select unit;
|
|
if (!matches.Any()) { continue; }
|
|
var match = matches.First();
|
|
if (match.NumberOfChannels() > 1)
|
|
{
|
|
HaveChannels = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckExtraUnits()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var unit in UnitsConnected)
|
|
{
|
|
var matches = from expected in param.ExpectedHardware where expected.SerialNumber == unit.SerialNumber select expected;
|
|
if (!matches.Any())
|
|
{
|
|
AddExtraUnit(unit);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// returns true if all hardware passes tests
|
|
/// returns false if any of the hardware fails a test
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool DoHardwareChecks()
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.PerformingHardwareChecks);
|
|
|
|
DoG5LostDockCheck();
|
|
CheckDASStates();
|
|
CheckUnitsNotInDb();
|
|
CheckFirmware();
|
|
CheckCalDates();
|
|
CheckModuleArrayIndices();
|
|
CheckNAND();
|
|
CheckStorage();
|
|
CheckHaveChannels();
|
|
CheckExtraUnits();
|
|
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// goes through all das which have a configuration and checks to see if we have any missing hardware
|
|
/// </summary>
|
|
private void GetMissingHardware()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
foreach (var expected in param.ExpectedHardware)
|
|
{
|
|
if (!Array.Exists(UnitsWithConfiguration, das => das.SerialNumber == expected.SerialNumber))
|
|
{
|
|
AddMissingHardware(expected);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// determines if any units have auto arm set
|
|
/// </summary>
|
|
private void GetAutoArmed()
|
|
{
|
|
var mre = new ManualResetEvent(false);
|
|
try
|
|
{
|
|
using (var armingService = new ArmingService())
|
|
{
|
|
var list = UnitsWithConfiguration.ToList();
|
|
armingService.GetAutoArmStatus(list, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
mre.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsAutoArmed, data.Target,
|
|
data.ErrorException, data.ErrorMessage);
|
|
break;
|
|
}
|
|
}, list);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
mre.Set();
|
|
}
|
|
mre.WaitOne();
|
|
foreach (var unit in UnitsWithConfiguration)
|
|
{
|
|
if (unit.AutoArmed)
|
|
{
|
|
AddUnitInAutoArm(unit);
|
|
if (unit.ConfigData.Modules[0].RecordingMode == Common.Enums.DASFactory.DFConstantsAndEnums
|
|
.RecordingMode.S6A_DeviceStreamingOnly)
|
|
{
|
|
AddUnitReadyToStream(unit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private const int INPUT_VOLTAGE_CUTOFF = 11000; //no idea where this came from ... stole it from the existing call...
|
|
/// <summary>
|
|
/// determines if any units are currently armed
|
|
/// </summary>
|
|
private void GetArmed()
|
|
{
|
|
var mre = new ManualResetEvent(false);
|
|
using (var armService = new ArmingService())
|
|
{
|
|
try
|
|
{
|
|
var list = UnitsWithConfiguration.ToList();
|
|
armService.GetArmStatus(list, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
mre.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
break;
|
|
}
|
|
}, list,
|
|
INPUT_VOLTAGE_CUTOFF);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsArmed, ex);
|
|
mre.Set();
|
|
}
|
|
}
|
|
|
|
mre.WaitOne();
|
|
var global = States.Instance.HardwareDiscoveryStart.Status.GlobalStatusInformation;
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (das.DASArmStatus.IsArmed)
|
|
{
|
|
global.AddUnitInArm(das);
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsArmed, das);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// checks the das state for all das
|
|
/// </summary>
|
|
private void CheckDASStates()
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.CheckingDASStates);
|
|
if (!UnitsWithConfiguration.Any()) { return; }
|
|
|
|
ProgressAction?.Invoke(0);
|
|
GetMissingHardware();
|
|
ProgressAction?.Invoke(25);
|
|
GetAutoArmed();
|
|
ProgressAction?.Invoke(50);
|
|
GetArmed();
|
|
ProgressAction?.Invoke(75);
|
|
GetRealtime();
|
|
ProgressAction?.Invoke(100);
|
|
}
|
|
/// <summary>
|
|
/// determines if any units are in realtime
|
|
/// </summary>
|
|
private void GetRealtime()
|
|
{
|
|
var global = States.Instance.HardwareDiscoveryStart.Status.GlobalStatusInformation;
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
if (null != das.DASArmStatus && das.DASArmStatus.IsInRealtime)
|
|
{
|
|
global.AddUnitInRealtime(das);
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsInRealtime, das);
|
|
if (das.ConfigData.Modules[0].RecordingMode == Common.Enums.DASFactory.DFConstantsAndEnums
|
|
.RecordingMode.S6A_DeviceStreamingOnly)
|
|
{
|
|
AddUnitStreaming(das);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// checks if any G5 have lost docks, this is a condition where the G5 does not respond with dock, but we
|
|
/// expect it should ...
|
|
/// </summary>
|
|
private void DoG5LostDockCheck()
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
var indummyG5s = new List<IDASCommunication>();
|
|
foreach (var das in UnitsWithConfiguration)
|
|
{
|
|
var type = das.GetHardwareType();
|
|
if (type == Common.Enums.Hardware.HardwareTypes.G5INDUMMY)
|
|
{
|
|
indummyG5s.Add(das);
|
|
}
|
|
}
|
|
|
|
if (indummyG5s.Any())
|
|
{
|
|
foreach (var g5 in indummyG5s)
|
|
{
|
|
var matches = from das in param.ExpectedHardware where das.SerialNumber == g5.SerialNumber select das;
|
|
if (matches.Any())
|
|
{
|
|
if (matches.First().DASTypeEnum != Common.Enums.Hardware.HardwareTypes.G5INDUMMY)
|
|
{
|
|
//it was supposed to be vds, but is in dummy, conclude the dock is lost
|
|
StatusExAction?.Invoke((int)StatusValues.G5DockLost, g5);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// returns true as long as all voltages were retrieved, returns false if we failed to get
|
|
/// a voltage (error/exception, we don't make judgments on the values here)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool DoVoltageChecks()
|
|
{
|
|
var units = UnitsWithConfiguration.ToList();
|
|
try
|
|
{
|
|
bool anyFailed = false;
|
|
var mre = new ManualResetEvent(false);
|
|
using (var ds = new DiagnosticsService())
|
|
{
|
|
ds.PerformVoltageCheck(units, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
StatusAction?.Invoke((int)StatusValues.VoltageCheckFinished);
|
|
mre.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
anyFailed = true;
|
|
StatusExAction?.Invoke((int)StatusValues.VoltageCheckFailure, data.Target);
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Success:
|
|
StatusExAction?.Invoke((int)StatusValues.VoltageCheckSuccess, data.Target);
|
|
break;
|
|
}
|
|
}, units);
|
|
}
|
|
|
|
while (!mre.WaitOne(VOLTAGE_CHECK_SPIN_TIME_MS, false))
|
|
{
|
|
//we could feedback progress here..
|
|
}
|
|
return !anyFailed;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private const int VOLTAGE_CHECK_SPIN_TIME_MS = 200;
|
|
/// <summary>
|
|
/// starts pinging, returns immediately
|
|
/// </summary>
|
|
internal void Ping()
|
|
{
|
|
_PingTask = Task.Run(() =>
|
|
{
|
|
var param = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams;
|
|
var global = States.Instance.HardwareDiscoveryStart.Status.GlobalStatusParameters;
|
|
var dasFactory = States.Instance.HardwareDiscoveryStart.DASFactory;
|
|
if (null != dasFactory && DoMultiCast())
|
|
{
|
|
DoMulticastDiscovery();
|
|
}
|
|
|
|
//get list of all ips requested to ping
|
|
var ipList = GetIpList();
|
|
//eliminate any local ip addresses
|
|
var host = System.Net.Dns.GetHostName();
|
|
var ipEntry = System.Net.Dns.GetHostEntry(host);
|
|
foreach (var entry in ipEntry.AddressList)
|
|
{
|
|
ipList.Remove(entry.ToString().ToLower());
|
|
}
|
|
//notify consumer we having started pinging
|
|
StatusAction?.Invoke((int)StatusValues.Pinging);
|
|
|
|
//only ping ... if we have something to ping ...
|
|
if (ipList.Any())
|
|
{
|
|
var p = new PingUtils.PingDevice();
|
|
|
|
|
|
var pinged = new HashSet<string>();
|
|
var ptd = new PingThreadData()
|
|
{
|
|
CancelEvent = CancelEvent, //any external cancel will cancel the ping process
|
|
DoneEvent = new ManualResetEvent(false), //we aren't running as a separate task, so this is just for the ping func benefit
|
|
AvoidConnectPortion = false, //this doesn't appear to actually do anything?
|
|
entryProgressCallback = (Entry entry, PingProgressStates state, long msRoundTrip) =>
|
|
{
|
|
StatusExAction?.Invoke((int)StatusValues.PingStatus, entry, state, msRoundTrip);
|
|
switch (state)
|
|
{
|
|
case PingProgressStates.Ping_Good:
|
|
//make a note that we successfully pinged this ip
|
|
pinged.Add(entry.IPAddress.ToLower());
|
|
break;
|
|
}
|
|
},
|
|
completeCallback = () => { }, //we don't need this
|
|
progressCallback = (double d) => { ProgressAction?.Invoke(d); }
|
|
|
|
};
|
|
//build the list of ips to ping to provide to the ping func
|
|
var list = new List<Entry>(ipList.Count);
|
|
foreach (var ip in ipList)
|
|
{
|
|
list.Add(new Entry() { IPAddress = ip });
|
|
}
|
|
ptd.Entries = list.ToArray();
|
|
//call the ping func
|
|
p.PingFunction(ptd);
|
|
//we are done pinging, remove any ip addresses which did not respond
|
|
for (var i = ipList.Count - 1; i >= 0; i--)
|
|
{
|
|
if (!pinged.Contains(ipList[i]))
|
|
{
|
|
ipList.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (param.Connect)
|
|
{
|
|
if (!CancelEvent.WaitOne(1, false))
|
|
{
|
|
//if we aren't cancelled move on to connecting
|
|
Connect(ipList, dasFactory);
|
|
}
|
|
|
|
if (!CancelEvent.WaitOne(1, false))
|
|
{
|
|
//we have now connected to all ips we should have, however note
|
|
//with ECMs that may mean we've connected to the ECM and not the SPS yet
|
|
//if we are connected to any distributors, ECM or not, give it some additional time
|
|
//to allow connecting to any children
|
|
WaitIfAnyECMsConnected(dasFactory);
|
|
}
|
|
|
|
// if autosense fails for some reason, don't try to get configuration
|
|
var autoSenseOK = true;
|
|
if (!CancelEvent.WaitOne(1, false))
|
|
{
|
|
var autoSenseUnits = from unit in UnitsConnected where unit.SupportsAutoDetect select unit;
|
|
if (param.ReadIds && param.RunAutoSense && !global.DisableAutoSense && autoSenseUnits.Any())
|
|
{
|
|
//we've determined we want to run autosense, so do so, then notify of success/fail
|
|
autoSenseOK = AutoSense(dasFactory, autoSenseUnits.ToList());
|
|
if (autoSenseOK)
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.AutoSenseSuccess);
|
|
}
|
|
else
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.AutoSenseFailed);
|
|
}
|
|
|
|
StatusExAction?.Invoke((int)StatusValues.AutoSenseComplete);
|
|
}
|
|
}
|
|
|
|
if (!CancelEvent.WaitOne(1, false) && autoSenseOK)
|
|
{
|
|
if (GetConfiguration(dasFactory))
|
|
{
|
|
var voltageChecksFailed = false;
|
|
if (param.DoVoltageChecks)
|
|
{
|
|
voltageChecksFailed = DoVoltageChecks();
|
|
}
|
|
//for now we don't really care much if the voltage check failed, it's
|
|
//not fatal, but hold onto the value incase we change our mind in the future
|
|
if (param.DoHardwareChecks)
|
|
{
|
|
if (DoHardwareChecks())
|
|
{
|
|
CompleteAction?.Invoke();
|
|
}
|
|
else
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.FailedHardwareChecks);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CompleteAction?.Invoke();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//getconfiguration failed ... this is only ok if there were no units were were trying to get...
|
|
if (!param.Addresses.Any())
|
|
{
|
|
CompleteAction?.Invoke();
|
|
}
|
|
else
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.FailedToGetConfiguration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CompleteAction?.Invoke();
|
|
}
|
|
|
|
StatusAction?.Invoke((int)StatusValues.Complete);
|
|
DoneEvent.Set();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// runs autosense on units that support it
|
|
/// autosense is needed if we have to check ids and the channel type may have been switched (iepe/analog)
|
|
/// </summary>
|
|
/// <param name="factory"></param>
|
|
private bool AutoSense(IDASFactory factory, List<IDASCommunication> autoSenseUnits)
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.AutoSense);
|
|
bool ranOK = true;
|
|
//we want to make sure all units have ConfigData before calling autosense, so complete for any units
|
|
//without ConfigData
|
|
var unitsNeedingConfig = from unit in autoSenseUnits where null == unit.ConfigData select unit;
|
|
if (unitsNeedingConfig.Any())
|
|
{
|
|
ManualResetEvent mre = new ManualResetEvent(false);
|
|
try
|
|
{
|
|
using (var configService = new ConfigurationService())
|
|
{
|
|
var list = unitsNeedingConfig.ToList();
|
|
configService.GetConfiguration(list, false, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
mre.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
ranOK = false;
|
|
StatusExAction?.Invoke((int)StatusValues.AutoSenseFailed, data.Target,
|
|
data.ErrorMessage, data.ErrorException);
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Success:
|
|
break;
|
|
}
|
|
|
|
}, list);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
return false;
|
|
}
|
|
mre.WaitOne();
|
|
}
|
|
|
|
if (CancelEvent.WaitOne(1, false))
|
|
{
|
|
//we were cancelled, we are done
|
|
return false;
|
|
}
|
|
//some unit failed, don't bother going on
|
|
if (!ranOK) { return false; }
|
|
|
|
try
|
|
{
|
|
var mre = new ManualResetEvent(false);
|
|
using (var configService = new ConfigurationService())
|
|
{
|
|
configService.AutoDetect(autoSenseUnits, false, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
mre.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
ranOK = false;
|
|
StatusExAction?.Invoke((int)StatusValues.AutoSenseFailed, data.Target,
|
|
data.ErrorMessage, data.ErrorException);
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Success:
|
|
StatusExAction?.Invoke((int)StatusValues.AutoSenseSuccess, data.Target);
|
|
break;
|
|
}
|
|
},
|
|
autoSenseUnits);
|
|
}
|
|
mre.WaitOne();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
return false;
|
|
}
|
|
|
|
return ranOK;
|
|
}
|
|
/// <summary>
|
|
/// retrieves the configuration from any units
|
|
/// returns false if configuration could not be obtained or an exception was thrown
|
|
/// </summary>
|
|
/// <param name="factory"></param>
|
|
/// <returns></returns>
|
|
private bool GetConfiguration(IDASFactory factory)
|
|
{
|
|
var param = States.Instance.ConfigureStart.Status.HardwareDiscoveryParams;
|
|
var global = States.Instance.ConfigureStart.Status.GlobalStatusInformation;
|
|
var bReturnValue = true;
|
|
var devices = factory.GetDASList();
|
|
|
|
//eliminate devices we've already queried
|
|
foreach (var device in UnitsWithConfiguration)
|
|
{
|
|
if (devices.Contains(device))
|
|
{
|
|
devices.Remove(device);
|
|
}
|
|
}
|
|
//eliminate any devices which are in arm or realtime state ...
|
|
var deviceArray = devices.ToArray();
|
|
foreach (var device in deviceArray)
|
|
{
|
|
if (device.GetIsInArm())
|
|
{
|
|
devices.Remove(device);
|
|
global.AddUnitInArm(device);
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsInArm, device);
|
|
}
|
|
else if (device.GetIsInRealtime())
|
|
{
|
|
devices.Remove(device);
|
|
global.AddUnitInRealtime(device);
|
|
StatusExAction?.Invoke((int)StatusValues.UnitIsInRealtime, device);
|
|
}
|
|
}
|
|
|
|
//a specific device is to be queried, query just that device
|
|
if (null != param.RequeryDevice)
|
|
{
|
|
devices.Clear();
|
|
devices.Add(param.RequeryDevice);
|
|
}
|
|
|
|
if (!devices.Any())
|
|
{
|
|
return UnitsWithConfiguration.Any();
|
|
}
|
|
|
|
//this will hold the wait handle for the getconfig which will spawn off into multiple threads
|
|
var waitHandle = new ManualResetEvent(false);
|
|
//notify consumer we've started getting configurations
|
|
StatusAction?.Invoke((int)StatusValues.GettingConfiguration);
|
|
//set status back to 0 since this is a new process
|
|
ProgressAction?.Invoke(0D);
|
|
try
|
|
{
|
|
var bReadIds = States.Instance.ConfigureStart.Status.HardwareDiscoveryParams.ReadIds;
|
|
|
|
using (var configService = new ConfigurationService())
|
|
{
|
|
//spawn off the work threads
|
|
configService.GetConfiguration(devices, bReadIds, (ServiceBase.CallbackData data) =>
|
|
{
|
|
switch (data.Status)
|
|
{
|
|
case ServiceBase.CallbackData.CallbackStatus.AllFinished:
|
|
//everybody is done, continue on your way
|
|
waitHandle.Set();
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Failure:
|
|
//record the failure, notify consumer
|
|
bReturnValue = false;
|
|
StatusExAction?.Invoke((int)StatusValues.FailedToGetConfiguration, data.Target,
|
|
data.ErrorMessage, data.ErrorException);
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Success:
|
|
//this unit is done, notify consumer
|
|
StatusExAction?.Invoke((int)StatusValues.UnitGotConfiguration, data.Target);
|
|
AddUnitWithConfiguration(data.Target);
|
|
break;
|
|
case ServiceBase.CallbackData.CallbackStatus.Progress:
|
|
//there's progress, notify consumer (assuming this is already aggregated, but we
|
|
//might need to notify for individual units as well)
|
|
ProgressAction?.Invoke(data.ProgressValue);
|
|
break;
|
|
}
|
|
},
|
|
devices);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
bReturnValue = false;
|
|
//notify of the exception
|
|
StatusExAction?.Invoke((int)StatusValues.FailedToGetConfiguration, ex);
|
|
}
|
|
|
|
//wait for everybody to finish
|
|
waitHandle.WaitOne();
|
|
return bReturnValue;
|
|
}
|
|
/// <summary>
|
|
/// this is the amount of time after we've connected to a distributor to wait
|
|
/// some distributors will notify of connected devices so we have to wait for additional connections
|
|
/// sometimes
|
|
/// </summary>
|
|
private const int DISTRIBUTOR_WAIT_TIME_MS = 2000;
|
|
/// <summary>
|
|
/// waits for a little while if there were any distributors that connected
|
|
/// </summary>
|
|
/// <param name="factory"></param>
|
|
private void WaitIfAnyECMsConnected(IDASFactory factory)
|
|
{
|
|
var devices = factory.GetDASList();
|
|
var bAnyDistributors = devices.Exists(d => d.IsEthernetDistributor());
|
|
if (bAnyDistributors)
|
|
{
|
|
Thread.Sleep(DISTRIBUTOR_WAIT_TIME_MS);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// connects to the given list of ips
|
|
/// </summary>
|
|
/// <param name="ipList"></param>
|
|
/// <param name="dasFactory"></param>
|
|
private void Connect(IList<string> ipList, IDASFactory dasFactory)
|
|
{
|
|
StatusAction?.Invoke((int)StatusValues.Connecting);
|
|
var listTDAS = new List<string>();
|
|
var listSLICE = new List<string>();
|
|
|
|
//to cut down on the number of connections we have to make
|
|
//if we know a device is SLICE or TDAS, only connect as SLICE or TDAS
|
|
//otherwise try to connect as both
|
|
var tdasIPHash = GetTDASIPHash();
|
|
var sliceIPHash = GetSLICEIPHash();
|
|
|
|
//remove any duplicates for sanity sake
|
|
ipList = ipList.Distinct().ToList();
|
|
|
|
//this will hold any ips that we are still waiting for
|
|
//while we are still waiting for an ip to connect we'll spin around and wait
|
|
var hashWaitingFor = new HashSet<string>();
|
|
//this will hold devices we've notified that we've connected to
|
|
//so that we only send one notification to consumer
|
|
var hashDevicesWeveNotifiedOn = new HashSet<IDASCommunication>();
|
|
foreach (var ip in ipList)
|
|
{
|
|
hashWaitingFor.Add(ip);
|
|
StatusExAction?.Invoke((int)StatusValues.Connecting, ip);
|
|
if (tdasIPHash.Contains(ip))
|
|
{
|
|
listTDAS.Add(ip);
|
|
}
|
|
else if (sliceIPHash.Contains(ip))
|
|
{
|
|
listSLICE.Add(ip);
|
|
}
|
|
else
|
|
{
|
|
//we don't know if it's slice or tdas, we'll have to try to connect both ways
|
|
listTDAS.Add(ip);
|
|
listSLICE.Add(ip);
|
|
}
|
|
}
|
|
|
|
if (null != dasFactory)
|
|
{
|
|
dasFactory.SliceDBHostNames = listSLICE.ToArray();
|
|
dasFactory.TDASHostNames = listTDAS.ToArray();
|
|
var timeWaited = 0;
|
|
var maxTime = States.Instance.HardwareDiscoveryStart.Status.HardwareDiscoveryParams.ConnectTimeoutMS;
|
|
var bDone = false;
|
|
dasFactory.Refresh(() => { });
|
|
|
|
ProgressAction?.Invoke(0);
|
|
var sliceEthernetWaitNeeded = false;
|
|
var ecmWaitTime = 0;
|
|
bool bWaitingForECMConnects = false;
|
|
while (timeWaited < maxTime && !bDone && !CancelEvent.WaitOne(0, false))
|
|
{
|
|
Thread.Sleep(TIME_WAIT_CONNECT);
|
|
var devices = dasFactory.GetDASList();
|
|
foreach (var device in devices)
|
|
{
|
|
var connection = ((ICommunication)device).Transport.ConnectString;
|
|
var index = connection.IndexOf(':');
|
|
var ip = connection;
|
|
if (index > 0)
|
|
{
|
|
ip = connection.Substring(0, index);
|
|
}
|
|
|
|
if (hashWaitingFor.Contains(ip))
|
|
{
|
|
//we've got a new ip connected, notify consumer
|
|
hashWaitingFor.Remove(ip);
|
|
}
|
|
|
|
if (!hashDevicesWeveNotifiedOn.Contains(device))
|
|
{
|
|
if (device.IsEthernetDistributor()) { sliceEthernetWaitNeeded = true; }
|
|
StatusExAction?.Invoke((int)StatusValues.Connected, connection, device);
|
|
AddUnitConnected(device);
|
|
}
|
|
}
|
|
timeWaited += TIME_WAIT_CONNECT;
|
|
if (bWaitingForECMConnects)
|
|
{
|
|
//we've connected to all the ips we connect, we just have to wait for any
|
|
//devices connected to the ecm to connect
|
|
ecmWaitTime += timeWaited;
|
|
if (ecmWaitTime >= SLICE_ETHERNET_WAIT_PERIOD)
|
|
{
|
|
bDone = true;
|
|
}
|
|
ProgressAction?.Invoke(100D * timeWaited / maxTime);
|
|
}
|
|
else if (!hashWaitingFor.Any())
|
|
{
|
|
//if there's no ips we are waiting for, but
|
|
//ecms take some amount of time to connect to notify and connect to modules
|
|
//so we might not be done yet ...
|
|
//to make things worse how many modules are connected to the can be different than expected
|
|
//and can even change (usb events..)
|
|
if (sliceEthernetWaitNeeded)
|
|
{
|
|
bWaitingForECMConnects = true;
|
|
}
|
|
else
|
|
{
|
|
//there were no ecms, so we are officially done
|
|
ProgressAction?.Invoke(100D);
|
|
bDone = true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//we are still waiting for some ips to connect
|
|
ProgressAction?.Invoke(100D * timeWaited / maxTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// when we connect to an ECM/DB it takes a while before all the units are actually connected
|
|
/// to fix this we allow a wait period after all ips are connected as long as one of the ips is slice db
|
|
/// </summary>
|
|
private const int SLICE_ETHERNET_WAIT_PERIOD = 7000;
|
|
private static object LocalLock = new object();
|
|
private void AddUnitConnected(IDASCommunication device)
|
|
{
|
|
lock (LocalLock)
|
|
{
|
|
if (UnitsConnected.Contains(device)) { return; }
|
|
|
|
var units = new List<IDASCommunication>(UnitsConnected);
|
|
if (units.Exists(d => d.SerialNumber == device.SerialNumber))
|
|
{
|
|
//device disconnect and reconnected ... throw out the old reference
|
|
units.RemoveAll(d => d.SerialNumber == device.SerialNumber);
|
|
var unitsWithConfig = new List<IDASCommunication>();
|
|
unitsWithConfig.AddRange(UnitsWithConfiguration);
|
|
unitsWithConfig.RemoveAll(d => d.SerialNumber == device.SerialNumber);
|
|
UnitsWithConfiguration = unitsWithConfig.ToArray();
|
|
}
|
|
|
|
units.Add(device);
|
|
UnitsConnected = units.ToArray();
|
|
}
|
|
}
|
|
|
|
private void AddUnitWithConfiguration(IDASCommunication device)
|
|
{
|
|
lock (LocalLock)
|
|
{
|
|
if (UnitsWithConfiguration.Contains(device)) { return; }
|
|
var units = new List<IDASCommunication>(UnitsWithConfiguration);
|
|
units.Add(device);
|
|
UnitsWithConfiguration = units.ToArray();
|
|
}
|
|
}
|
|
private const int TIME_WAIT_CONNECT = 500;//500ms
|
|
public void Reset()
|
|
{
|
|
AllDASFound = false;
|
|
SomeUnitsInArmState = false;
|
|
CancelEvent.Reset();
|
|
DoneEvent.Reset();
|
|
CompleteAction = null;
|
|
StatusAction = null;
|
|
ProgressAction = null;
|
|
StatusExAction = null;
|
|
_discoveredDevices.Clear();
|
|
UnitsConnected = new IDASCommunication[0];
|
|
UnitsWithConfiguration = new IDASCommunication[0];
|
|
UnitsWithLostDocks = new IDASCommunication[0];
|
|
UnitsMissing = new IDASHardware[0];
|
|
UnitsInAutoArm = new IDASCommunication[0];
|
|
UnitsReadyToStream = new IDASCommunication[0];
|
|
UnitsStreaming = new IDASCommunication[0];
|
|
UnitsNotInDb = new IDASCommunication[0];
|
|
UnitsWithWrongFirmware = new IDASCommunication[0];
|
|
UnitsWithExpiredCalDates = new IDASCommunication[0];
|
|
UnitsWithNANDIssues = new IDASCommunication[0];
|
|
UnitsWithWrongChannelCount = new IDASCommunication[0];
|
|
UnitsWithChannelTypeIssue = new IDASCommunication[0];
|
|
HaveChannels = false;
|
|
ExtraUnits = new IDASCommunication[0];
|
|
}
|
|
}
|
|
}
|