Files
DP44/DataPRO/IService/.svn/pristine/dc/dcdff822cec759c512c2820d625f8075435c767f.svn-base
2026-04-17 14:55:32 -04:00

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];
}
}
}