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; } } /// /// This is the base class for all connected DAS's /// public class ConnectedBaseDevice : IConnectedDAS { protected IDASCommunication Device { get; set; } /// /// Constructor /// /// The device we're handling public ConnectedBaseDevice(IDASCommunication device) { Debug.Assert(device is ICommunication); Device = device; } /// /// Implementation of the interface. /// public virtual string ConnectString => Comm.ConnectString; /// /// Implementation of the interface. /// public virtual ICommunication Comm => Device as ICommunication; /// /// Implementation of the interface. /// public virtual IDASCommunication DASComm => Device; /// /// A text representation /// /// The device serial number public override string ToString() { return Device.SerialNumber; } /// /// Dispose of the device /// public virtual void Dispose() { try { if (Device == null) return; Device.Dispose(); Device = null; } catch (Exception ex) { APILogger.Log(ex); } } } /// /// This represent one SLICE connected thru WinUSB /// public class ConnectedWinUSBSlice : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice(WinUSBSlice device) : base(device) { } } /// /// This represent one SLICE connected thru WinUSB /// public class ConnectedCDCUSBSlice : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedCDCUSBSlice(CDCUSBSlice device) : base(device) { } } /// /// This represent one SLICE connected thru WinUSB /// // ReSharper disable once InconsistentNaming public class ConnectedWinUSBSlice1_5 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice1_5(WinUSBSlice1_5 device) : base(device) { } } /// /// This represent one SLICE 6 connected thru WinUSB /// public class ConnectedWinUSBSlice6 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice6(WinUSBSlice6 device) : base(device) { } } /// /// This represent one SLICE 6 AIR connected thru WinUSB /// public class ConnectedWinUSBSlice6Air : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice6Air(WinUSBSlice6Air device) : base(device) { } } /// /// This represent one SLICE 6 AIR BR connected thru WinUSB /// public class ConnectedWinUSBSlice6AirBridge : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice6AirBridge(WinUSBSlice6AirBridge device) : base(device) { } } /// /// This represent one TSRAIR connected thru WinUSB /// public class ConnectedWinUSBTsrAir : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBTsrAir(WinUSBTsrAir device) : base(device) { } } /// /// This represent one SLICE 6 AIR TC connected thru WinUSB /// public class ConnectedWinUSBSlice6AirThermocoupler : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedWinUSBSlice6AirThermocoupler(WinUSBSlice6AirThermocoupler device) : base(device) { } } /// /// This represent one SLICE connected thru a Slice Distributor /// public class ConnectedEthernetSlice : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice(EthernetSlice device) : base(device) { } } /// /// This represents one SLICE 2 connected thru a Slice Distributor /// public class ConnectedEthernetSlice2 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice2(EthernetSlice2 device) : base(device) { } } /// /// This represents one SLICE 1.5 connected thru a Slice Distributor /// // ReSharper disable once InconsistentNaming public class ConnectedEthernetSlice1_5 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice1_5(EthernetSlice1_5 device) : base(device) { } } /// /// This represents one SLICE 6 connected thru a Slice Distributor /// public class ConnectedEthernetSlice6 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6(EthernetSlice6 device) : base(device) { } } /// /// This represents one SLICE 6 AIR connected thru a Slice Distributor /// public class ConnectedEthernetSlice6Air : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6Air(EthernetSlice6Air device) : base(device) { } } /// /// This represents one SLICE 6 AIR Bridge connected thru a Slice Distributor /// public class ConnectedEthernetSlice6AirBridge : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6AirBridge(EthernetSlice6AirBridge device) : base(device) { } } /// /// This represents one SLICE 6 connected thru a Slice Distributor /// public class ConnectedEthernetSlice6DB : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6DB(EthernetSlice6DB _device) : base(_device) { } } /// /// This represents one PowerPro /// public class ConnectedEthernetPowerPro : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetPowerPro(EthernetPowerPro _device) : base(_device) { } } /// /// This represents one SLICE PRO DB /// public class ConnectedEthernetSliceProDB : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSliceProDB(EthernetSlicePRODB _device) : base(_device) { } } /// This represents one SDB /// public class ConnectedEthernetSDB : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSDB(EthernetSliceDB device) : base(device) { } } /// /// This represents one TSRAIR connected thru a Slice Distributor /// public class ConnectedEthernetTsrAir : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetTsrAir(EthernetTsrAir device) : base(device) { } } /// /// This represents one S6DB3 connected thru a Slice Distributor /// public class ConnectedEthernetSlice6DB3 : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6DB3(EthernetSlice6DB3 device) : base(device) { } } /// /// This represent one SLICE connected thru a Slice Distributor /// public class ConnectedEthernetTDAS : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetTDAS(EthernetTDAS device) : base(device) { } } /// /// This represent one TDAS connected thru a TDAS RS-232 Port /// public class ConnectedSerialTDAS : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedSerialTDAS(SerialTDAS device) : base(device) { } } /// /// This represent one Ribeye connected thru a Slice Distributor /// public class ConnectedEthernetRibeye : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetRibeye(EthernetRibeye device) : base(device) { } } public class ConnectedRESTSPFD : ConnectedBaseDevice { public ConnectedRESTSPFD(RESTSliceProFD device) : base(device) { } } /// /// This represents one SLICE 6 AIR TC connected thru a Slice Distributor /// public class ConnectedEthernetSlice6AirThermocoupler : ConnectedBaseDevice { /// /// Constructor /// /// The device we're handling public ConnectedEthernetSlice6AirThermocoupler(EthernetSlice6AirThermocoupler device) : base(device) { } } /// /// This represents one TSR2 connected thru WinUSB /// public class ConnectedWinUSBTSR2 : IConnectedDevice { private WinUSBSlice Device { get; set; } /// /// Constructor /// /// The device we're handling public ConnectedWinUSBTSR2(WinUSBSlice device) { Device = device; } /// /// Implementation of the interface. /// public string ConnectString => Device.ConnectString; /// /// Implementation of the interface. /// 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 { public DFConstantsAndEnums.DASType DasType; public IConnectedDevice Dev; /// /// is this device in update mode? /// 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> _queueActionPerDevice; protected DeviceHandling(DASFactory factory, UpdateFinishedEventHandler serialUpdateFinished, BlockingCollection> queueActionPerDevice = null) { Factory = factory; SerialUpdateFinished += serialUpdateFinished; _queueActionPerDevice = queueActionPerDevice; } /// /// 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. /// public abstract void UpdateDeviceSetups(); public virtual void Dispose() { var tuple = new Tuple(QueueActions.Exit, this); _queueActionPerDevice.Add(tuple); DetachAllDevices(); } protected List ConnectedDevices = new List(); protected readonly object ConnectedDevicesLock = new object(); #region Connect and disconnect handling /// /// This class is used to transfer device and WaitHandle to callback functions. /// 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 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 _connectingDevices = new List(); /// /// /// /// /// /// /// /// /// /// 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 _CommandPortSockets = new Dictionary(); /// /// adds a new socket into the lookup of ip to socket /// private static void AddCommandPortSocket(string key, Socket s) { lock (COM_LOCK) { _CommandPortSockets[key] = s; } } /// /// returns the socket to the command port to an ip (or null if we don't have one) /// public static Socket GetCommandPortSocket(string key) { lock (COM_LOCK) { if (_CommandPortSockets.ContainsKey(key)) { return _CommandPortSockets[key]; } } return null; } /// /// removes a command port from the lookup /// public static void RemoveCommandPortSocket(string s) { lock (COM_LOCK) { _CommandPortSockets.Remove(s); } } /// /// the port (plus : for slice command port (as opposed to heartbeat port) /// public const string SLICE_COMMAND_PORT = ":8201"; public const string SLICE6_COMMAND_PORT = ":8301"; /// /// Callback for new device connect /// /// state passed in from connect function 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); /// /// Remove the WinUSB devices that aren't connected /// /// true if all OK, false if error 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(); 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"); } } /// /// Callback for WinUSB disconnect /// /// /// always false 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 GetDASList() { lock (ConnectedDevicesLock) { var dasList = new List(); 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 GetDevList() { lock (ConnectedDevicesLock) { var devList = new List(); 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 GetConnectedStrings() { for (var loop = 0; loop < 10; loop++) { lock (ConnectedDevicesLock) { try { var strings = new List(); 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!!!"); } /// /// Make DASFactory forget about all devices. /// 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.Connect, this); _queueActionPerDevice.Add(tuple); } public void EnqueueDisconnect() { var tuple = new Tuple(QueueActions.Disconnect, this); _queueActionPerDevice.Add(tuple); } public void EnqueueDisconnectAndExit() { var tuple = new Tuple(QueueActions.DisconnectAndExit, this); _queueActionPerDevice.Add(tuple); } #endregion /// /// Convert an ICommunicationReport to a ConnectedDevice /// /// the report to get the device from /// The type of DAS we're looking for /// The new ConnectedDevice 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; } /// /// Produce a string to identify a device with /// /// The device /// Always returns a string 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; } /// /// 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 /// /// public virtual void ReportDisconnect(object obj) { //by default do nothing, only the TDAS connector uses it currently } } }