1320 lines
46 KiB
C#
1320 lines
46 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using DTS.Common.DASResource;
|
|
using DTS.Common.Interface.DASFactory;
|
|
using DTS.DASLib.Service;
|
|
using DTS.Common.Utilities.Logging;
|
|
using DTS.Common.Enums.DASFactory;
|
|
using DTS.Common.Enums.Communication;
|
|
using DTS.Common.Interface.Communication;
|
|
using System.Collections.Concurrent;
|
|
using System.Threading.Tasks;
|
|
using DTS.DASLib.Service.Classes.CAN;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace DTS.DASLib.DASFactory
|
|
{
|
|
#region Connected devices list
|
|
|
|
public interface IConnectedDevice : IDisposable
|
|
{
|
|
string ConnectString { get; }
|
|
ICommunication Comm { get; }
|
|
}
|
|
|
|
public interface IConnectedDAS : IConnectedDevice
|
|
{
|
|
IDASCommunication DASComm { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is the base class for all connected DAS's
|
|
/// </summary>
|
|
public class ConnectedBaseDevice : IConnectedDAS
|
|
{
|
|
protected IDASCommunication Device { get; set; }
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedBaseDevice(IDASCommunication device)
|
|
{
|
|
Debug.Assert(device is ICommunication);
|
|
Device = device;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implementation of the interface.
|
|
/// </summary>
|
|
public virtual string ConnectString => Comm.ConnectString;
|
|
|
|
/// <summary>
|
|
/// Implementation of the interface.
|
|
/// </summary>
|
|
public virtual ICommunication Comm => Device as ICommunication;
|
|
|
|
/// <summary>
|
|
/// Implementation of the interface.
|
|
/// </summary>
|
|
public virtual IDASCommunication DASComm => Device;
|
|
|
|
/// <summary>
|
|
/// A text representation
|
|
/// </summary>
|
|
/// <returns>The device serial number</returns>
|
|
public override string ToString()
|
|
{
|
|
return Device.SerialNumber;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose of the device
|
|
/// </summary>
|
|
public virtual void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (Device == null) return;
|
|
Device.Dispose();
|
|
Device = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represent one SLICE connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBSlice : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice(WinUSBSlice device) : base(device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represent one SLICE connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedCDCUSBSlice : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedCDCUSBSlice(CDCUSBSlice device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE connected thru WinUSB
|
|
/// </summary>
|
|
// ReSharper disable once InconsistentNaming
|
|
public class ConnectedWinUSBSlice1_5 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice1_5(WinUSBSlice1_5 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE 6 connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBSlice6 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice6(WinUSBSlice6 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE 6 AIR connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBSlice6Air : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice6Air(WinUSBSlice6Air device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE 6 AIR BR connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBSlice6AirBridge : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice6AirBridge(WinUSBSlice6AirBridge device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one TSRAIR connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBTsrAir : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBTsrAir(WinUSBTsrAir device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE 6 AIR TC connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBSlice6AirThermocoupler : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBSlice6AirThermocoupler(WinUSBSlice6AirThermocoupler device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice(EthernetSlice device) : base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 2 connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice2 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice2(EthernetSlice2 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 1.5 connected thru a Slice Distributor
|
|
/// </summary>
|
|
// ReSharper disable once InconsistentNaming
|
|
public class ConnectedEthernetSlice1_5 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice1_5(EthernetSlice1_5 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 6 connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6(EthernetSlice6 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 6 AIR connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6Air : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6Air(EthernetSlice6Air device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 6 AIR Bridge connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6AirBridge : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6AirBridge(EthernetSlice6AirBridge device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one SLICE 6 connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6DB : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="_device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6DB(EthernetSlice6DB _device)
|
|
: base(_device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one PowerPro
|
|
/// </summary>
|
|
public class ConnectedEthernetPowerPro : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="_device">The device we're handling</param>
|
|
public ConnectedEthernetPowerPro(EthernetPowerPro _device)
|
|
: base(_device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represents one SLICE PRO DB
|
|
/// </summary>
|
|
public class ConnectedEthernetSliceProDB : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="_device">The device we're handling</param>
|
|
public ConnectedEthernetSliceProDB(EthernetSlicePRODB _device)
|
|
: base(_device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// This represents one SDB
|
|
/// </summary>
|
|
public class ConnectedEthernetSDB : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSDB(EthernetSliceDB device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one TSRAIR connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetTsrAir : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetTsrAir(EthernetTsrAir device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represents one S6DB3 connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6DB3 : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6DB3(EthernetSlice6DB3 device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This represent one SLICE connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetTDAS : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetTDAS(EthernetTDAS device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represent one TDAS connected thru a TDAS RS-232 Port
|
|
/// </summary>
|
|
public class ConnectedSerialTDAS : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="_device">The device we're handling</param>
|
|
public ConnectedSerialTDAS(SerialTDAS device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represent one Ribeye connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetRibeye : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetRibeye(EthernetRibeye device) : base(device)
|
|
{
|
|
}
|
|
}
|
|
|
|
public class ConnectedRESTSPFD : ConnectedBaseDevice
|
|
{
|
|
public ConnectedRESTSPFD(RESTSliceProFD device) : base(device) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represents one SLICE 6 AIR TC connected thru a Slice Distributor
|
|
/// </summary>
|
|
public class ConnectedEthernetSlice6AirThermocoupler : ConnectedBaseDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedEthernetSlice6AirThermocoupler(EthernetSlice6AirThermocoupler device)
|
|
: base(device)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represents one TSR2 connected thru WinUSB
|
|
/// </summary>
|
|
public class ConnectedWinUSBTSR2 : IConnectedDevice
|
|
{
|
|
private WinUSBSlice Device { get; set; }
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="device">The device we're handling</param>
|
|
public ConnectedWinUSBTSR2(WinUSBSlice device)
|
|
{
|
|
Device = device;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implementation of the interface.
|
|
/// </summary>
|
|
public string ConnectString => Device.ConnectString;
|
|
|
|
/// <summary>
|
|
/// Implementation of the interface.
|
|
/// </summary>
|
|
public ICommunication Comm => Device;
|
|
|
|
public override string ToString()
|
|
{
|
|
return Device.SerialNumber;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (Device == null) return;
|
|
Device.Dispose();
|
|
Device = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ConnectedDevice : IDisposable, IComparable<ConnectedDevice>
|
|
{
|
|
public DFConstantsAndEnums.DASType DasType;
|
|
|
|
public IConnectedDevice Dev;
|
|
|
|
/// <summary>
|
|
/// is this device in update mode?
|
|
/// </summary>
|
|
public bool InUpdateMode { get; set; }
|
|
|
|
public ConnectedDevice()
|
|
{
|
|
Dev = null;
|
|
DasType = DFConstantsAndEnums.DASType.NONE;
|
|
InUpdateMode = false;
|
|
}
|
|
|
|
public ConnectedDevice(IConnectedDevice icd, DFConstantsAndEnums.DASType dt)
|
|
{
|
|
Dev = icd;
|
|
DasType = dt;
|
|
InUpdateMode = false;
|
|
}
|
|
|
|
public int CompareTo(ConnectedDevice other)
|
|
{
|
|
//Use ICommunication.CompareTo()
|
|
return Dev.Comm.CompareTo(other.Dev.Comm);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return InUpdateMode ? Dev.ConnectString : Dev.ToString();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (Dev == null) return;
|
|
Dev.Dispose();
|
|
Dev = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public interface IDeviceSetup
|
|
{
|
|
bool QueryInformation(ConnectedDevice dev);
|
|
|
|
ICommunication GetICommunication();
|
|
|
|
ICommunication GetICommunication(ConnectedDevice dev);
|
|
|
|
IConnectedDevice GetIConnectedDevice(DTS.Common.Interface.DASFactory.ICommunication comm);
|
|
|
|
bool IsCorrectType(ConnectedDevice dev);
|
|
|
|
DFConstantsAndEnums.DASType GetDASType();
|
|
|
|
Guid GetGuid();
|
|
|
|
int GetProductId();
|
|
|
|
string GetProductIdString();
|
|
|
|
void SetHandler(DeviceHandling handler);
|
|
}
|
|
|
|
public abstract class DeviceHandling
|
|
{
|
|
protected DASFactory Factory { get; set; }
|
|
private readonly BlockingCollection<Tuple<QueueActions, DeviceHandling>> _queueActionPerDevice;
|
|
|
|
protected DeviceHandling(DASFactory factory, UpdateFinishedEventHandler serialUpdateFinished, BlockingCollection<Tuple<QueueActions, DeviceHandling>> queueActionPerDevice = null)
|
|
{
|
|
Factory = factory;
|
|
SerialUpdateFinished += serialUpdateFinished;
|
|
_queueActionPerDevice = queueActionPerDevice;
|
|
}
|
|
|
|
/// <summary>
|
|
/// called after construction of a devicehandler, this method is used
|
|
/// to update the device setups and notify them of their handlers, this way
|
|
/// the setup classes can notify the handlers and vice versa.
|
|
/// </summary>
|
|
public abstract void UpdateDeviceSetups();
|
|
|
|
public virtual void Dispose()
|
|
{
|
|
var tuple = new Tuple<QueueActions, DeviceHandling>(QueueActions.Exit, this);
|
|
_queueActionPerDevice.Add(tuple);
|
|
DetachAllDevices();
|
|
}
|
|
|
|
protected List<ConnectedDevice> ConnectedDevices = new List<ConnectedDevice>();
|
|
protected readonly object ConnectedDevicesLock = new object();
|
|
|
|
#region Connect and disconnect handling
|
|
|
|
/// <summary>
|
|
/// This class is used to transfer device and WaitHandle to callback functions.
|
|
/// </summary>
|
|
internal class DeviceAndWaitTuple
|
|
{
|
|
public object Device { get; set; }
|
|
|
|
public ManualResetEvent CompletedEvent { get; set; }
|
|
|
|
public DeviceAndWaitTuple(object dev, ManualResetEvent completed)
|
|
{
|
|
Device = dev;
|
|
CompletedEvent = completed;
|
|
}
|
|
}
|
|
|
|
internal class DeviceAndWaitExTuple : DeviceAndWaitTuple
|
|
{
|
|
public DFConstantsAndEnums.DASType DASType { get; set; }
|
|
public IDeviceSetup DevSetup { get; set; }
|
|
|
|
public DeviceAndWaitExTuple(object dev,
|
|
ManualResetEvent completed,
|
|
DFConstantsAndEnums.DASType dasType,
|
|
IDeviceSetup devSetup)
|
|
: base(dev, completed)
|
|
{
|
|
DASType = dasType;
|
|
DevSetup = devSetup;
|
|
}
|
|
}
|
|
|
|
#region Connect
|
|
|
|
protected delegate List<string> GetDeviceStrings();
|
|
protected delegate ICommunication GetICommunication();
|
|
protected delegate IConnectedDevice GetIConnectedDevice(DTS.Common.Interface.DASFactory.ICommunication comm);
|
|
|
|
private static readonly object CONNECTING_DEVICES_LOCK = new object();
|
|
private readonly List<string> _connectingDevices = new List<string>();
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="getAllConnected"></param>
|
|
/// <param name="getAllActive"></param>
|
|
/// <param name="getICommunication"></param>
|
|
/// <param name="getIConnectedDevice"></param>
|
|
/// <param name="dasType"></param>
|
|
/// <param name="devSetup"></param>
|
|
/// <param name="callbackTimeout"></param>
|
|
protected virtual void ConnectNewDevices(GetDeviceStrings getAllConnected,
|
|
GetDeviceStrings getAllActive,
|
|
GetICommunication getICommunication,
|
|
GetIConnectedDevice getIConnectedDevice,
|
|
DFConstantsAndEnums.DASType dasType,
|
|
IDeviceSetup devSetup,
|
|
int callbackTimeout)
|
|
{
|
|
var bFailed = false;
|
|
try
|
|
{
|
|
// say hello
|
|
|
|
var connectedDevices = getAllConnected();
|
|
|
|
// subtract the ones we already know about
|
|
var newDevices = connectedDevices.Except(getAllActive()).ToArray();
|
|
|
|
if (!newDevices.Any()) { return; } //nothing to connect
|
|
// add the new ones
|
|
foreach (var devPath in newDevices.AsParallel())
|
|
{
|
|
try
|
|
{
|
|
lock (CONNECTING_DEVICES_LOCK)
|
|
{
|
|
if (_connectingDevices.Contains(devPath))
|
|
{
|
|
continue; //already connecting
|
|
}
|
|
//the device may have been added to active devices since we last checked, recheck before connecting
|
|
//15584 devices attempt connect while already connected
|
|
if (getAllActive().Contains(devPath))
|
|
{
|
|
continue;
|
|
}
|
|
APILogger.Log("Connecting, ", devPath);
|
|
_connectingDevices.Add(devPath);
|
|
}
|
|
try
|
|
{
|
|
var newDevice = getICommunication();
|
|
|
|
var newConnectedDevice = getIConnectedDevice(newDevice);
|
|
|
|
var connectedEvent = new ManualResetEvent(false);
|
|
//FB 25642 & 18152 Get the das ip from connection string and use it to retreive the host ip
|
|
string deviceIp;
|
|
string hostIpAddress = "";
|
|
//FB 25658 for USB there is no hostIP
|
|
if (Common.Utils.NetworkUtils.TryParseConnectionString(devPath, out deviceIp))
|
|
{
|
|
if (!Factory.DasToHost.ContainsKey(deviceIp))
|
|
{
|
|
APILogger.Log($"no known host for {deviceIp} we'll try pinging for it");
|
|
DASFactory.TryPing(deviceIp);
|
|
}
|
|
else
|
|
{
|
|
APILogger.Log($"we have a known host for {deviceIp} and it's {Factory.DasToHost[deviceIp].HostIpAddress}");
|
|
}
|
|
hostIpAddress = Factory.DasToHost[deviceIp].HostIpAddress;
|
|
|
|
}
|
|
|
|
// try to connect it
|
|
newDevice.Connect(devPath,
|
|
NewDeviceConnectCallback,
|
|
new DeviceAndWaitExTuple(newConnectedDevice, connectedEvent, dasType, devSetup),
|
|
callbackTimeout, hostIpAddress);
|
|
if (!connectedEvent.WaitOne(callbackTimeout, false))
|
|
{
|
|
bFailed = true;
|
|
throw new TimeoutException("Failed to connect new device " + devPath);
|
|
}
|
|
if (this is EthernetHandling handling)
|
|
{
|
|
handling.CommandPortConnected = true;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
lock (CONNECTING_DEVICES_LOCK)
|
|
{
|
|
if (_connectingDevices.Contains(devPath))
|
|
{
|
|
_connectingDevices.Remove(devPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log("Exception connecting device", ex);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log("ConnectNewDevices: Exception", ex);
|
|
}
|
|
finally
|
|
{
|
|
if (bFailed)
|
|
{
|
|
Thread.Sleep(100);
|
|
}
|
|
}
|
|
}
|
|
|
|
//lock for concurrency handling on lookup
|
|
private static readonly object COM_LOCK = new object();
|
|
//lookup of dictionary (network path minus port) to socket
|
|
private static Dictionary<string, Socket> _CommandPortSockets = new Dictionary<string, Socket>();
|
|
|
|
/// <summary>
|
|
/// adds a new socket into the lookup of ip to socket
|
|
/// </summary>
|
|
private static void AddCommandPortSocket(string key, Socket s)
|
|
{
|
|
lock (COM_LOCK)
|
|
{
|
|
_CommandPortSockets[key] = s;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// returns the socket to the command port to an ip (or null if we don't have one)
|
|
/// <summary>
|
|
public static Socket GetCommandPortSocket(string key)
|
|
{
|
|
lock (COM_LOCK)
|
|
{
|
|
if (_CommandPortSockets.ContainsKey(key))
|
|
{
|
|
return _CommandPortSockets[key];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/// <summary>
|
|
/// removes a command port from the lookup
|
|
/// </summary>
|
|
public static void RemoveCommandPortSocket(string s)
|
|
{
|
|
lock (COM_LOCK)
|
|
{
|
|
_CommandPortSockets.Remove(s);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// the port (plus : for slice command port (as opposed to heartbeat port)
|
|
/// <summary>
|
|
public const string SLICE_COMMAND_PORT = ":8201";
|
|
public const string SLICE6_COMMAND_PORT = ":8301";
|
|
/// <summary>
|
|
/// Callback for new device connect
|
|
/// </summary>
|
|
/// <param name="report">state passed in from connect function</param>
|
|
protected virtual bool NewDeviceConnectCallback(ICommunicationReport report)
|
|
{
|
|
Debug.Assert(report.UserState is DeviceAndWaitExTuple);
|
|
var deviceInfo = (DeviceAndWaitExTuple)report.UserState;
|
|
|
|
var idStr = "";
|
|
try
|
|
{
|
|
var newDev = ExtractConnectedDevice(report, deviceInfo.DASType);
|
|
idStr = ConnectedDeviceId(newDev);
|
|
|
|
// was the connect successful?
|
|
if (report.Result == CommunicationConstantsAndEnums.CommunicationResult.ConnectOK)
|
|
{
|
|
try
|
|
{
|
|
//if new device is a command port, keep track of it
|
|
//so that we don't try to connect to a HB port while
|
|
//command port is still active
|
|
if (newDev.Dev.ConnectString.EndsWith(SLICE_COMMAND_PORT))
|
|
{
|
|
if (newDev.Dev.Comm.Transport is DTS.Common.EthernetConnection connection)
|
|
{
|
|
var key = newDev.Dev.ConnectString.Replace(SLICE_COMMAND_PORT, "");
|
|
AddCommandPortSocket(key, connection.Sock);
|
|
}
|
|
}
|
|
else if (newDev.Dev.ConnectString.EndsWith(SLICE6_COMMAND_PORT))
|
|
{
|
|
if (newDev.Dev.Comm.Transport is DTS.Common.EthernetConnection connection)
|
|
{
|
|
var key = newDev.Dev.ConnectString.Replace(SLICE6_COMMAND_PORT, "");
|
|
AddCommandPortSocket(key, connection.Sock);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
|
|
// fill the DASInfo, this function does NOT throw exceptions
|
|
var queryInfoOk = deviceInfo.DevSetup.QueryInformation(newDev);
|
|
|
|
// update the ID string
|
|
idStr = ConnectedDeviceId(newDev);
|
|
|
|
if (queryInfoOk)
|
|
{
|
|
// add it to our list
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
APILogger.LogString($"{APILogger.GetCurrentMethod()}: id:{idStr}");
|
|
// add it
|
|
ConnectedDevices.Add(newDev);
|
|
|
|
// sort the list
|
|
ConnectedDevices.Sort();
|
|
}
|
|
// notify our subscribers
|
|
Factory.ReportArrived(newDev);
|
|
}
|
|
else
|
|
{
|
|
// first dispose of it
|
|
newDev.Dispose();
|
|
|
|
// notify our subscribers
|
|
Factory.ReportFailed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no, connect failed
|
|
// first dispose of it
|
|
newDev.Dispose();
|
|
|
|
// notify our users of the failure
|
|
Factory.ReportFailed();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.LogString(string.Format("{0}: exception: {1} id:{2}", APILogger.GetCurrentMethod(), report.Result, idStr));
|
|
APILogger.LogException(ex);
|
|
}
|
|
finally
|
|
{
|
|
deviceInfo.CompletedEvent.Set();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Disconnect
|
|
|
|
protected delegate bool DASTypeFilter(ConnectedDevice dev);
|
|
protected delegate ICommunication ConnectedDevice2Communication(ConnectedDevice dev);
|
|
|
|
/// <summary>
|
|
/// Remove the WinUSB devices that aren't connected
|
|
/// </summary>
|
|
/// <returns>true if all OK, false if error</returns>
|
|
protected virtual void DisconnectRemovedDevices(GetDeviceStrings getAllConnected,
|
|
DASTypeFilter isCorrectType,
|
|
ConnectedDevice2Communication getCommunication,
|
|
DFConstantsAndEnums.DASType dasType,
|
|
int callbackTimeout)
|
|
{
|
|
try
|
|
{
|
|
|
|
|
|
// get a list of units currently connected
|
|
var connectedDeviceStrings = getAllConnected();
|
|
|
|
// remove the ones that aren't there anymore
|
|
// make a local copy of the connected devices list
|
|
ConnectedDevice[] connectedDevicesClone;
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
connectedDevicesClone = ConnectedDevices.ToArray();
|
|
}
|
|
|
|
var removedDeviceSerials = new List<string>();
|
|
var devicesWhereRemoved = false;
|
|
foreach (var dev in connectedDevicesClone)
|
|
{
|
|
try
|
|
{
|
|
//if we are removing a command port, we don't have to worry about
|
|
//connecting to the HB port while the command port is active, as
|
|
//we should be disconnecting the command port ...
|
|
if (dev.Dev.Comm.Transport is DTS.Common.EthernetConnection connection)
|
|
{
|
|
if (dev.Dev.ConnectString.EndsWith(SLICE_COMMAND_PORT))
|
|
{
|
|
RemoveCommandPortSocket(dev.Dev.ConnectString.Replace(SLICE_COMMAND_PORT, ""));
|
|
}
|
|
else if (dev.Dev.ConnectString.EndsWith(SLICE6_COMMAND_PORT))
|
|
{
|
|
RemoveCommandPortSocket(dev.Dev.ConnectString.Replace(SLICE6_COMMAND_PORT, ""));
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
|
|
// if it's not of us we don't deal with it
|
|
if (!isCorrectType(dev))
|
|
{
|
|
continue;
|
|
}
|
|
// if it's still connected that's good
|
|
if (connectedDeviceStrings.Contains(dev.Dev.ConnectString))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// say hello
|
|
APILogger.LogString(string.Format("{0}:{1}", APILogger.GetCurrentMethod(), dev.Dev.ConnectString));
|
|
|
|
devicesWhereRemoved = true;
|
|
removedDeviceSerials.Add(dev.Dev.Comm.SerialNumber);
|
|
try
|
|
{
|
|
var disconnectedEvent = new ManualResetEvent(false);
|
|
// this is one of those places where we need to get to the ICommunication part
|
|
try
|
|
{
|
|
getCommunication(dev).Disconnect(false,
|
|
DisconnectDeviceCallback,
|
|
new DeviceAndWaitExTuple(dev, disconnectedEvent, dasType, null),
|
|
callbackTimeout);
|
|
}
|
|
catch (SocketException)
|
|
{
|
|
//15575 No notification if unplug comm cable during downloading on G5
|
|
//previously Disconnect would fail in some circumstances - namely if the unit is already disconnected
|
|
//this would cause an enormous and unnecessary delay in the waitone below
|
|
//to correct this, on socketexception we are done disconnecting, set the event and continue on
|
|
disconnectedEvent.Set();
|
|
}
|
|
|
|
disconnectedEvent.WaitOne();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.LogString("DisconnectRemovedDevices: Exception");
|
|
APILogger.LogException(ex);
|
|
}
|
|
finally
|
|
{
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
if (ConnectedDevices.Contains(dev))
|
|
{
|
|
ConnectedDevices.Remove(dev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// notify our subscribers if we removed something
|
|
if (devicesWhereRemoved)
|
|
{
|
|
Factory.ReportRemoved(removedDeviceSerials.ToArray());
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.LogString("DisconnectRemovedDevices: Exception");
|
|
APILogger.LogException(ex);
|
|
}
|
|
finally
|
|
{
|
|
//APILogger.LogString("DisconnectRemovedDevices: Exit");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback for WinUSB disconnect
|
|
/// </summary>
|
|
/// <param name="report"></param>
|
|
/// <returns>always false</returns>
|
|
private bool DisconnectDeviceCallback(ICommunicationReport report)
|
|
{
|
|
Debug.Assert(report.UserState is DeviceAndWaitExTuple);
|
|
var deviceInfo = (DeviceAndWaitExTuple)report.UserState;
|
|
|
|
var idStr = "";
|
|
try
|
|
{
|
|
var newDev = ExtractConnectedDevice(report, deviceInfo.DASType);
|
|
idStr = ConnectedDeviceId(newDev);
|
|
APILogger.LogString("DisconnectDeviceCallback: Enter " + idStr);
|
|
|
|
// first dispose of it
|
|
newDev.Dispose();
|
|
|
|
// remove it from our list
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
ConnectedDevices.Remove(newDev);
|
|
}
|
|
|
|
APILogger.LogString("DisconnectDeviceCallback: " + report.Result + " " + idStr);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.LogString("DisconnectDeviceCallback: exception " + report.Result + " " + idStr);
|
|
APILogger.LogException(ex);
|
|
}
|
|
finally
|
|
{
|
|
APILogger.LogString("DisconnectDeviceCallback: Exit " + idStr);
|
|
deviceInfo.CompletedEvent.Set();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Get DAS/DEV List
|
|
public List<IDASCommunication> GetDASList()
|
|
{
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
var dasList = new List<IDASCommunication>();
|
|
try
|
|
{
|
|
if (ConnectedDevices != null)
|
|
{
|
|
if (ConnectedDevices.Count == 0)
|
|
{
|
|
//UpdateConnectedDevices();
|
|
|
|
}
|
|
foreach (var connectedDevice in ConnectedDevices)
|
|
{
|
|
// if it's not a DAS we're not interested
|
|
if (!(connectedDevice.Dev is IConnectedDAS))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// make it so
|
|
var connectedDAS = (IConnectedDAS)connectedDevice.Dev;
|
|
|
|
// it must also be an ICommunication
|
|
if (!(connectedDAS.DASComm is ICommunication))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var devCom = (ICommunication)connectedDAS.DASComm;
|
|
if (devCom.Connected)
|
|
{
|
|
dasList.Add(connectedDAS.DASComm);
|
|
}
|
|
else if (devCom.Transport.IsSoftDisconnected)
|
|
{
|
|
//the unit is not actively connected, but is still considered
|
|
//one of the das we could connect to ...
|
|
dasList.Add(connectedDAS.DASComm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// we don't know the state of the devices
|
|
dasList.Clear();
|
|
}
|
|
return dasList;
|
|
}
|
|
}
|
|
|
|
public List<ICommunication> GetDevList()
|
|
{
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
var devList = new List<ICommunication>();
|
|
try
|
|
{
|
|
if (ConnectedDevices != null)
|
|
{
|
|
foreach (var connectedDevice in ConnectedDevices)
|
|
{
|
|
// and so it shall be
|
|
if (connectedDevice?.Dev.Comm == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var devCom = connectedDevice.Dev.Comm;
|
|
if (devCom.Connected)
|
|
{
|
|
devList.Add(connectedDevice.Dev.Comm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// we don't know the state of the devices
|
|
devList.Clear();
|
|
}
|
|
return devList;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
protected List<string> GetConnectedStrings()
|
|
{
|
|
for (var loop = 0; loop < 10; loop++)
|
|
{
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
try
|
|
{
|
|
var strings = new List<string>();
|
|
foreach (var dev in ConnectedDevices)
|
|
{
|
|
strings.Add(dev.Dev.ConnectString);
|
|
}
|
|
return strings;
|
|
}
|
|
catch (NullReferenceException)
|
|
{
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
throw new Exception("DASFactory.GetConnectedStrings: tried 10 times to get strings!!!");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make DASFactory forget about all devices.
|
|
/// </summary>
|
|
internal virtual bool DetachAllDevices()
|
|
{
|
|
var devicesWhereRemoved = false;
|
|
lock (ConnectedDevicesLock)
|
|
{
|
|
foreach (var dev in ConnectedDevices)
|
|
{
|
|
dev.Dispose();
|
|
devicesWhereRemoved = true;
|
|
}
|
|
ConnectedDevices.Clear();
|
|
}
|
|
|
|
// let our caller know if something was removed
|
|
return devicesWhereRemoved;
|
|
}
|
|
|
|
#region Serialization of connect and disconnect
|
|
|
|
public enum QueueActions
|
|
{
|
|
None, // we never enqueue this one
|
|
Connect,
|
|
Disconnect,
|
|
Idle,
|
|
Exit,
|
|
//FB 17556 Added action to disconnect when we exit
|
|
DisconnectAndExit
|
|
}
|
|
|
|
public delegate void UpdateFinishedEventHandler(DeviceHandling dev);
|
|
|
|
protected event UpdateFinishedEventHandler SerialUpdateFinished;
|
|
|
|
public abstract void UpdateDisconnectedDevices();
|
|
|
|
public abstract void UpdateConnectedDevices();
|
|
|
|
public void EnqueueConnect()
|
|
{
|
|
var tuple = new Tuple<QueueActions, DeviceHandling>(QueueActions.Connect, this);
|
|
_queueActionPerDevice.Add(tuple);
|
|
}
|
|
|
|
public void EnqueueDisconnect()
|
|
{
|
|
var tuple = new Tuple<QueueActions, DeviceHandling>(QueueActions.Disconnect, this);
|
|
_queueActionPerDevice.Add(tuple);
|
|
}
|
|
public void EnqueueDisconnectAndExit()
|
|
{
|
|
var tuple = new Tuple<QueueActions, DeviceHandling>(QueueActions.DisconnectAndExit, this);
|
|
_queueActionPerDevice.Add(tuple);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Convert an ICommunicationReport to a ConnectedDevice
|
|
/// </summary>
|
|
/// <param name="report">the report to get the device from</param>
|
|
/// <param name="dastype">The type of DAS we're looking for</param>
|
|
/// <returns>The new ConnectedDevice</returns>
|
|
protected ConnectedDevice ExtractConnectedDevice(ICommunicationReport report, DFConstantsAndEnums.DASType dastype)
|
|
{
|
|
Debug.Assert(report.UserState is DeviceAndWaitTuple);
|
|
var deviceParameter = ((DeviceAndWaitTuple)report.UserState).Device;
|
|
|
|
ConnectedDevice newDev;
|
|
if (deviceParameter is ConnectedDevice)
|
|
{
|
|
newDev = deviceParameter as ConnectedDevice;
|
|
}
|
|
else
|
|
{
|
|
newDev = new ConnectedDevice
|
|
{
|
|
Dev = deviceParameter as IConnectedDevice,
|
|
DasType = dastype,
|
|
InUpdateMode = false
|
|
};
|
|
// assume not in update mode
|
|
}
|
|
return newDev;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produce a string to identify a device with
|
|
/// </summary>
|
|
/// <param name="dev">The device</param>
|
|
/// <returns>Always returns a string</returns>
|
|
internal string ConnectedDeviceId(ConnectedDevice dev)
|
|
{
|
|
if (dev == null)
|
|
{
|
|
return "device==null";
|
|
}
|
|
if (dev.Dev == null)
|
|
{
|
|
return "device.dev==null";
|
|
}
|
|
if (!(dev.Dev is IConnectedDAS))
|
|
{
|
|
return "device.dev not IConnectedDAS";
|
|
}
|
|
if (((IConnectedDAS)dev.Dev).DASComm == null)
|
|
{
|
|
if (string.IsNullOrEmpty(dev.Dev.ConnectString))
|
|
{
|
|
return "device.dev.ConnectString==null";
|
|
}
|
|
return dev.Dev.ConnectString;
|
|
}
|
|
if (!string.IsNullOrEmpty(((IConnectedDAS)dev.Dev).DASComm.SerialNumber))
|
|
return ((IConnectedDAS)dev.Dev).DASComm.SerialNumber;
|
|
return string.IsNullOrEmpty(dev.Dev.ConnectString) ? "device.dev.ConnectString==null" : dev.Dev.ConnectString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// handlers current monitor connections for disconnects
|
|
/// but a das factory handler may actually need to hand off an object
|
|
/// and not be able to check it for connectivity actively and may need to
|
|
/// rely on being notified that the object is disconnected or available again
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
public virtual void ReportDisconnect(object obj)
|
|
{
|
|
//by default do nothing, only the TDAS connector uses it currently
|
|
}
|
|
}
|
|
} |