453 lines
18 KiB
C#
453 lines
18 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Threading;
|
|||
|
|
using DTS.Common.Utilities;
|
|||
|
|
using DTS.DASLib.DASFactory;
|
|||
|
|
using DTS.DASLib.Service;
|
|||
|
|
using DTS.Common.Utilities.Logging;
|
|||
|
|
using DTS.Common.Interface.DASFactory;
|
|||
|
|
using DTS.Common.Enums.DASFactory;
|
|||
|
|
using DTS.Common.DataModel;
|
|||
|
|
|
|||
|
|
namespace DataPROWin7.DataModel
|
|||
|
|
{
|
|||
|
|
public class DASFactory
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// starts the auto discovery process if it's not running yet
|
|||
|
|
/// </summary>
|
|||
|
|
public void StartMulticastAutoDiscovery()
|
|||
|
|
{
|
|||
|
|
_dasFactory.StartMulticastAutoDiscovery();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// stops the auto discovery process if it is running
|
|||
|
|
/// </summary>
|
|||
|
|
public void StopMulticastAutoDiscovery()
|
|||
|
|
{
|
|||
|
|
_dasFactory.StopMulticastAutoDiscovery();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns any discovered devices
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public IDiscoveredDevice [] GetDiscoveredDevices()
|
|||
|
|
{
|
|||
|
|
return _dasFactory.GetDiscoveredDevices();
|
|||
|
|
}
|
|||
|
|
private DTS.DASLib.DASFactory.DASFactory _dasFactory;
|
|||
|
|
public IDASFactory GetDASFactory() { return _dasFactory; }
|
|||
|
|
public DASFactory()
|
|||
|
|
{
|
|||
|
|
//10285 Unhandled exception during diagnostics.
|
|||
|
|
//this initializes the interval value used in CheckUnitsAvailable to make the sleep time configurable.
|
|||
|
|
DTS.DASLib.Service.ServiceBase.InitializeCheckUnitsInterval(DataModelSettings.CheckUnitsIntervalMillisecond);
|
|||
|
|
//10843 TDAS communication needs to be throttled
|
|||
|
|
//this initializes the throttling of TDAS devices
|
|||
|
|
DTS.DASLib.Command.TDAS.CommandBase.InitializeSemaphore(
|
|||
|
|
DataModelSettings.SemaphoreDelay,
|
|||
|
|
DataModelSettings.SemaphoreSpots);
|
|||
|
|
//initialize SLICESemaphore before any communication starts
|
|||
|
|
//10852 Add semaphore to SLICE communication to improve SLICE 6 multiple IP performance
|
|||
|
|
DTS.DASLib.Command.SliceCommandBase.Initialize(DataModelSettings.SLICEConcurrentSpots,
|
|||
|
|
DataModelSettings.SLICEConcurrentDelayMs);
|
|||
|
|
_dasFactory = new DTS.DASLib.DASFactory.DASFactory(false, false);
|
|||
|
|
_dasFactory.DetachAllDevices();
|
|||
|
|
var mre = new ManualResetEvent(false);
|
|||
|
|
_dasFactory.Refresh(delegate { mre.Set(); });
|
|||
|
|
mre.WaitOne();
|
|||
|
|
_dasFactory.MultiCastAutoDiscoveryDefaultTimeoutMS = DataModelSettings.MulticastAutoDiscoveryReceiveTimeoutMS;
|
|||
|
|
|
|||
|
|
//16053 Implement KeepAliveSeconds and KeepAliveRetrySeconds in the .config file
|
|||
|
|
DFConstantsAndEnums.LocalKeepAliveRetryIntervalMS = DataModelSettings.LocalKeepAliveRetryIntervalMS;
|
|||
|
|
DFConstantsAndEnums.LocalKeepAliveTimeOutMS = DataModelSettings.LocalKeepAliveTimeOutMS;
|
|||
|
|
DFConstantsAndEnums.RemoteKeepAliveRetryIntervalSeconds = DataModelSettings.RemoteKeepAliveRetryIntervalSeconds;
|
|||
|
|
DFConstantsAndEnums.RemoteKeepAliveSeconds = DataModelSettings.RemoteKeepAliveSeconds;
|
|||
|
|
|
|||
|
|
DFConstantsAndEnums.ReceiveBufferSizeBytes = DataModelSettings.ReceiveBufferSizeBytes;
|
|||
|
|
DFConstantsAndEnums.SendBufferSizeBytes = DataModelSettings.SendBufferSizeBytes;
|
|||
|
|
|
|||
|
|
DFConstantsAndEnums.HeartbeatAsyncConnectTimeoutMS = DataModelSettings.HeartbeatAsyncConnectTimeoutMS;
|
|||
|
|
|
|||
|
|
_dasFactory.DeviceArrived += _dasFactory_DeviceArrived;
|
|||
|
|
_dasFactory.DeviceFailed += _dasFactory_DeviceFailed;
|
|||
|
|
_dasFactory.DeviceRemoved += _dasFactory_DeviceRemoved;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public event DASFactoryEventHandler OnDeviceArrived;
|
|||
|
|
public event DASFactoryEventHandler OnFactoryChanged;
|
|||
|
|
|
|||
|
|
public void TakeOwnership()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_dasFactory.TakeOwnership();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void _dasFactory_DeviceRemoved(object sender, DASFactoryEventArgs e)
|
|||
|
|
{
|
|||
|
|
OnFactoryChanged?.Invoke(sender, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
private void _dasFactory_DeviceFailed(object sender, DASFactoryEventArgs e)
|
|||
|
|
{
|
|||
|
|
OnFactoryChanged?.Invoke(sender, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void _dasFactory_DeviceArrived(object sender, DASFactoryEventArgs e)
|
|||
|
|
{
|
|||
|
|
OnDeviceArrived?.Invoke(sender, e);
|
|||
|
|
OnFactoryChanged?.Invoke(sender, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void DetachAllDevices(bool detachUSB = false)
|
|||
|
|
{
|
|||
|
|
// FB14290: make USB detaching conditional
|
|||
|
|
_dasFactory.DetachAllDevices(detachUSB);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void DisposeFactory()
|
|||
|
|
{
|
|||
|
|
_dasFactory.Dispose();
|
|||
|
|
_dasFactory = null;
|
|||
|
|
}
|
|||
|
|
//#define LOG_DEBUG_REFRESH
|
|||
|
|
public string[] TDASHostNames
|
|||
|
|
{
|
|||
|
|
get { return _dasFactory.TDASHostNames; }
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (_bInRefresh)
|
|||
|
|
{
|
|||
|
|
#if LOG_DEBUG_REFRESH
|
|||
|
|
var st = new StackTrace(true);
|
|||
|
|
APILogger.Log("TDASHostNames SET WHILE WE ARE IN REFRESH\n", st.ToString());
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
if (null == value || null == _dasFactory.TDASHostNames)
|
|||
|
|
{
|
|||
|
|
_dasFactory.TDASHostNames = value;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var val = value.Distinct().OrderBy(a => a);
|
|||
|
|
// this has side effects, so only change if needed
|
|||
|
|
var isEqual = _dasFactory.TDASHostNames.OrderBy(a => a).SequenceEqual(val);
|
|||
|
|
if (!isEqual)
|
|||
|
|
{
|
|||
|
|
_dasFactory.TDASHostNames = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public string[] SPFDHostNames
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.SPFDHostNames;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (null == value || null == _dasFactory.SPFDHostNames)
|
|||
|
|
{
|
|||
|
|
_dasFactory.SPFDHostNames = value;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var val = value.Distinct().OrderBy(a => a);
|
|||
|
|
// this has side effects, so only change if needed
|
|||
|
|
var isEqual = _dasFactory.SPFDHostNames.OrderBy(a => a).SequenceEqual(val);
|
|||
|
|
if (!isEqual)
|
|||
|
|
{
|
|||
|
|
_dasFactory.SPFDHostNames = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// http://fogbugz/fogbugz/default.asp?2903
|
|||
|
|
/// could also be called SliceDbHostNames
|
|||
|
|
/// </summary>
|
|||
|
|
public string[] SDBHostNames
|
|||
|
|
{
|
|||
|
|
get { return _dasFactory.SliceDBHostNames; }
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (_bInRefresh)
|
|||
|
|
{
|
|||
|
|
#if LOG_DEBUG_REFRESH
|
|||
|
|
var st = new StackTrace(true);
|
|||
|
|
APILogger.Log("SDBHostNames SET WHILE WE ARE IN REFRESH\n", st.ToString());
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
if (null == value || null == _dasFactory.SliceDBHostNames)
|
|||
|
|
{
|
|||
|
|
_dasFactory.SliceDBHostNames = value;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// this has side effects, so only change if needed
|
|||
|
|
var equals = _dasFactory.SliceDBHostNames.OrderBy(a => a).SequenceEqual(value.OrderBy(a => a));
|
|||
|
|
if (false == equals)
|
|||
|
|
{
|
|||
|
|
_dasFactory.SliceDBHostNames = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public SortableBindingList<IDiscoveredDevice> AutoDiscoverMulticast()
|
|||
|
|
{
|
|||
|
|
CancellationToken ct = new CancellationToken();
|
|||
|
|
return _dasFactory.AutoDiscoverMulticast(ct);
|
|||
|
|
}
|
|||
|
|
public delegate void DiscoveredDASEventHandler(object sender, IEnumerable<IDiscoveredDevice> newDevices);
|
|||
|
|
public event DiscoveredDASEventHandler DiscoveredDAS;
|
|||
|
|
public void DiscoveryThread(DFConstantsAndEnums.MultiCastDeviceClasses[] deviceFilter, CancellationToken ct, bool discoverParents = true)
|
|||
|
|
{
|
|||
|
|
while (!ct.IsCancellationRequested)
|
|||
|
|
{
|
|||
|
|
var discoveries = _dasFactory.AutoDiscoverMulticast(ct, discoverParents).ToList();
|
|||
|
|
var filteredDiscoveries = deviceFilter?.Count() > 0 ? discoveries.Where(idd => deviceFilter.Contains(idd.DevClass)).ToList() : discoveries;
|
|||
|
|
|
|||
|
|
DiscoveredDAS.Invoke(this, filteredDiscoveries);
|
|||
|
|
|
|||
|
|
ct.WaitHandle.WaitOne(1000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// starts qats listening
|
|||
|
|
/// </summary>
|
|||
|
|
public void StartQATSListening()
|
|||
|
|
{
|
|||
|
|
_dasFactory.StartQATSListening();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// stops any QATS listening
|
|||
|
|
/// </summary>
|
|||
|
|
public void StopQATSListening()
|
|||
|
|
{
|
|||
|
|
_dasFactory.StopQATSListening();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// sends the request to send UDP QATS messages
|
|||
|
|
/// </summary>
|
|||
|
|
public void SendQATSRequest()
|
|||
|
|
{
|
|||
|
|
_dasFactory.SendQATSRequest();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns any QueryArmTriggerStatus that are waiting and clears the list of waiting QATS
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public IUDPQATSEntry[] GetQATS()
|
|||
|
|
{
|
|||
|
|
return _dasFactory.GetQATS();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// configures the default timeout for multicast autodiscovery receive timeout
|
|||
|
|
/// in ms
|
|||
|
|
/// </summary>
|
|||
|
|
public int MulticastAutoDiscoveryReceiveTimeoutMS
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.MultiCastAutoDiscoveryDefaultTimeoutMS;
|
|||
|
|
set => _dasFactory.MultiCastAutoDiscoveryDefaultTimeoutMS = value;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// configures the trasmit address for multicast autodiscovery
|
|||
|
|
/// in ms
|
|||
|
|
/// </summary>
|
|||
|
|
public string MulticastAutoDiscoveryAddress
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.MulticastAutoDiscoveryAddress;
|
|||
|
|
set => _dasFactory.MulticastAutoDiscoveryAddress = value;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// configures the trasmit port for multicast autodiscovery
|
|||
|
|
/// in ms
|
|||
|
|
/// </summary>
|
|||
|
|
public int MulticastAutoDiscoveryPort
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.MulticastAutoDiscoveryPort;
|
|||
|
|
set => _dasFactory.MulticastAutoDiscoveryPort = value;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// configures the receive port for multicast autodiscovery
|
|||
|
|
/// in ms
|
|||
|
|
/// </summary>
|
|||
|
|
public int MulticastAutoDiscoveryResponsePort
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.MulticastAutoDiscoveryResponsePort;
|
|||
|
|
set => _dasFactory.MulticastAutoDiscoveryResponsePort = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public double S6ConnectNewTimeout
|
|||
|
|
{
|
|||
|
|
get => _dasFactory.S6ConnectNewTimeout;
|
|||
|
|
set => _dasFactory.S6ConnectNewTimeout = value;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// I noticed in some logs - only one refresh at a time
|
|||
|
|
/// refresh is getting called multiple times for some reason.
|
|||
|
|
/// This gets a bit sticky, there's no reason for Refresh to be called more than once while refresh is still running,
|
|||
|
|
/// however one of the options for refresh is to not wait for it to finish, which means you could get into this situation.
|
|||
|
|
/// I haven't been able to duplicate this problem myself, so I'm going to add some logging to help find out how we got there if we
|
|||
|
|
/// get there again, and also some code to address when we do find ourselves there
|
|||
|
|
///
|
|||
|
|
/// </summary>
|
|||
|
|
private volatile bool _bInRefresh;
|
|||
|
|
|
|||
|
|
public void Refresh(bool wait)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
APILogger.Log(APILogger.GetCurrentMethod());
|
|||
|
|
if (_bInRefresh)
|
|||
|
|
{
|
|||
|
|
#if LOG_DEBUG_REFRESH
|
|||
|
|
var st = new StackTrace(true);
|
|||
|
|
APILogger.Log(string.Format("Warning - OVERLAPPING REFRESH CALL!\nStackTrace:\n{0}", st));
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var mre = new ManualResetEvent(false);
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_bInRefresh = true;
|
|||
|
|
_dasFactory.Refresh(delegate
|
|||
|
|
{
|
|||
|
|
mre.Set();
|
|||
|
|
_bInRefresh = false;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("Exception refreshing ", ex.Message);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (wait)
|
|||
|
|
{
|
|||
|
|
mre.WaitOne();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public List<IDASCommunication> GetActiveDevices()
|
|||
|
|
{
|
|||
|
|
return _dasFactory.GetDASList();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 14157 Refresh/Stability Issue in Data Acquisition Tile
|
|||
|
|
/// returns the list of all devices known connectable via ECM/SDB/S6DB
|
|||
|
|
/// these distributors have a HELLO SLICEBASE x.x.x.x:yyyy format, so
|
|||
|
|
/// we keep track of what the db told us
|
|||
|
|
/// this returns all the reported connections
|
|||
|
|
/// </summary>
|
|||
|
|
public string[] GetReportedConnections()
|
|||
|
|
{
|
|||
|
|
return _dasFactory.GetConnectedDevices();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// runs auto discovery if needed, populating the downstream mac addresses for all attached SLICE6/SLICE6Db devices
|
|||
|
|
/// completes immediately if there are no attached SLICE6/SLICE6Db devices
|
|||
|
|
/// </summary>
|
|||
|
|
public void AutoDiscoverIfNecessary()
|
|||
|
|
{
|
|||
|
|
var foundDas = ApplicationProperties.DASFactory.GetActiveDevices();
|
|||
|
|
var bNeedToRun = foundDas.OfType<EthernetSlice6DB>().Any();
|
|||
|
|
if (!bNeedToRun) return;
|
|||
|
|
var ipAddressToIdas = new Dictionary<string, IDASCommunication>();
|
|||
|
|
foreach (var das in foundDas)
|
|||
|
|
{
|
|||
|
|
var h = new DASHardware(das);
|
|||
|
|
var connection = h.ConnectionUSBAware.ToLower();
|
|||
|
|
if (connection.Contains("usb"))
|
|||
|
|
{
|
|||
|
|
connection = h.SerialNumber;
|
|||
|
|
}
|
|||
|
|
ipAddressToIdas[connection] = das;
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var units = ApplicationProperties.DASFactory.AutoDiscoverMulticast();
|
|||
|
|
var macAddresToDevice = new Dictionary<string, IDiscoveredDevice>();
|
|||
|
|
foreach (var unit in units)
|
|||
|
|
{
|
|||
|
|
var mac = unit.Mac.Replace('-', ':').ToUpper();
|
|||
|
|
macAddresToDevice[mac] = unit;
|
|||
|
|
}
|
|||
|
|
foreach (var unit in units)
|
|||
|
|
{
|
|||
|
|
if (!ipAddressToIdas.ContainsKey(unit.Ip))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
ipAddressToIdas[unit.Ip].MACAddress = unit.Mac;
|
|||
|
|
ipAddressToIdas[unit.Ip].DownstreamMACAddresses = (from c in unit.Connections
|
|||
|
|
select c.MACAddress.Replace('-', ':').ToUpper()
|
|||
|
|
into childMac
|
|||
|
|
where macAddresToDevice.ContainsKey(childMac)
|
|||
|
|
select macAddresToDevice[childMac]
|
|||
|
|
into childDevice
|
|||
|
|
select childDevice.Mac).ToArray();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns true if the DAS is streaming
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="das"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static bool IsStreaming(IDASCommunication das)
|
|||
|
|
{
|
|||
|
|
if (!(das is EthernetSlice6Air)) { return false; }
|
|||
|
|
|
|||
|
|
if (null == das.DASArmStatus)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//15932 Error when performing test when S6A is streaming
|
|||
|
|
//don't currently know of a better way to determine if the unit is streaming or not
|
|||
|
|
//when attaching to a stream device some attributes can't be read and because of timing
|
|||
|
|
//some status may not be populated, but if we aren't armed or in realtime and we are a S6A and
|
|||
|
|
//we responded with Invalid mode during setup, we are _probably_ streaming
|
|||
|
|
return !das.DASArmStatus.IsArmed && !das.DASArmStatus.IsInRealtime &&
|
|||
|
|
das.DASArmStatus.ReceivedInvalidModeDuringSetup;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns true if the unit is in realtime
|
|||
|
|
/// uses DASArmStatus, but this isn't always populated on time, so if the unit is streaming it will also
|
|||
|
|
/// return true, which seems to be consistent with what was intended in the old code
|
|||
|
|
/// 15932 Error when performing test when S6A is streaming
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="das"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static bool IsInRealtime(IDASCommunication das)
|
|||
|
|
{
|
|||
|
|
if (null == das.DASArmStatus) { return false; }
|
|||
|
|
return das.DASArmStatus.IsInRealtime || IsStreaming(das);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns true if any of the units in the input parameters are in realtime or are streaming
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="das"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static bool AnyInRealtime(List<IDASCommunication> das)
|
|||
|
|
{
|
|||
|
|
return das.Exists(unit => IsInRealtime(unit));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|