using CANFDApiProxy; using CANFDApiProxy.Messages; using DTS.Common; using DTS.Common.Enums.DASFactory; using DTS.Common.Events; using DTS.Common.ICommunication; using DTS.Common.Interface.DASFactory; using DTS.Common.Interface.DASFactory.Config; using DTS.Common.SharedResource.Strings; using DTS.Common.Strings; using DTS.Common.Utilities.Logging; using DTS.DASLib.Command; using DTS.DASLib.Command.TDAS; using DTS.DASLib.Service; using DTS.DASLib.Service.Classes.CAN; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing.Text; using System.Linq; using System.Net.Sockets; using System.Threading; using static DTS.DASLib.DASFactory.EthernetTDASHandling; namespace DTS.DASLib.DASFactory { internal abstract class SPFDSetup : IDeviceSetup { protected SPFDSetup() { } private static string GetSerialNumber(ConnectedDevice das) { using (var source = new CancellationTokenSource()) { source.CancelAfter(100); var t = CANFD.API.GetSerial(das.Dev.ConnectString, source.Token); t.Wait(); if (t.IsCompleted) { return t.Result.Serial; } return null; } } private static decimal? GetBattery(string ip) { try { using (var source = new CancellationTokenSource()) { var task = CANFD.API.GetBattery(ip, source.Token); task.Wait(); if (task.IsCompleted) { return task.Result.LoadV; } } } catch (Exception) { //right now we expect exceptions here by nature of sending a connect to ip addresses //just discard them for now } return null; } private static string GetFirmwareVersion(ConnectedDevice dev) { try { using (var cancelSource = new CancellationTokenSource()) { var t = CANFD.API.GetDeviceInfo(dev.Dev.ConnectString, cancelSource.Token); t.Wait(); if (t.IsCompleted) { var res = t.Result; return res.Version_number; } } } catch (Exception ex) { APILogger.Log(ex); } return null; } private static int GetNumberOfChannels(ConnectedDevice dev) { try { using (var cancelSource = new CancellationTokenSource()) { var t = CANFD.API.GetCANInfo(dev.Dev.ConnectString, cancelSource.Token); t.Wait(); if (t.IsCompleted) { var res = t.Result; return res.CANInfoList.Count; } } } catch (Exception ex) { APILogger.Log(ex); } return 0; } public bool QueryInformation(ConnectedDevice dev) { try { APILogger.Log("DasFactory.SPFD::SetupDASInfo enter"); // shortcut dev.Dev.Comm.SerialNumber = GetSerialNumber(dev); dev.Dev.Comm.FirmwareVersion = GetFirmwareVersion(dev) ?? string.Empty; var numChannels = (uint)GetNumberOfChannels(dev); // get the stack contents var idas = (IDASCommunication)dev.Dev.Comm; //taking values roughly from here //http://build:8080/svn/ProductDevelopment/SLICE%20PRO/Controller%20Area%20Network%20(CAN)%20Recorder/SPDBR/Documents/Reviews/PDR/ //LED POWER BLUE means chargin 50-90%, GREEn is >90% PURPLE is 20% <50%, <20% is fault or red idas.MaximumValidBatteryVoltage = 10; idas.MinimumValidBatteryVoltage = 1; idas.BatteryHighVoltage = 5; idas.BatteryLowVoltage = 3.6F; idas.BatteryMediumVoltage = 4F; var serialNumber = new string[1] { dev.Dev.Comm.SerialNumber }; var firmwareVersion = new string[1] { dev.Dev.Comm.FirmwareVersion }; dev.Dev.Comm.DASInfo = new Communication_DASInfo(serialNumber, firmwareVersion); var ir = new InfoResult { NumberOfBridgeChannels = 0, MaxNumberOfModules = 1, OwningDAS = idas, NumberOfBytesPerSampleClock = 1, MaxEventStorageSpaceInBytes = 16 * 1024 * 1024 }; ir.Modules = new IInfoResultModule[1]; ir.Modules[0] = new InfoResult.Module() { ModuleArrayIndex = 0, SerialNumber = serialNumber[0], NumberOfChannels = numChannels }; idas.SetDASInfo(ir); idas.ConfigData = new ConfigurationData() { Modules = new DASModule[1] }; idas.ConfigData.Modules[0] = new DASModule() { OwningDAS = idas }; var canInfo = GetChannels(dev); var channels = new List(); var channelNum = 0; foreach (var channel in canInfo) { channels.Add(new CANInputDASChannel() { UserChannelName = channel.Name, ModuleChannelNumber = channelNum, HardwareChannelName = channel.Name, OwningModule = (DASModule)idas.ConfigData.Modules[0] }); channelNum++; } idas.ConfigData.Modules[0].Channels = channels.ToArray(); GetVoltageInfo(dev); return true; } catch (Exception ex) { APILogger.Log(ex.ToString()); return false; } } private static List GetChannels(ConnectedDevice dev) { if ((dev == null) || (dev.Dev == null) || (dev.Dev.Comm == null)) { return new List(); } try { var t = CANFD.API.GetCANInfo(dev.Dev.ConnectString, CancellationToken.None); t.Wait(); if (t.IsCompleted) { return t.Result.CANInfoList; } } catch (Exception ex) { var msg = string.Format(StringResources.Warning_FailedToRetrieveCanChannels, dev.Dev.ConnectString, dev.Dev.Comm.SerialNumber); SurfaceApplicationError(msg); APILogger.Log(msg, ex); } return new List(); } private static void SurfaceApplicationError(string msg) { try { PageErrorEvent.SurfaceApplicationError(msg); } catch (Exception ex) { APILogger.Log(ex); } } private static void GetVoltageInfo(ConnectedDevice dev) { var batteryVoltage = GetBattery(dev.Dev.ConnectString); var inputValues = new BaseInputValues(); var idas = (IDASCommunication)dev.Dev.Comm; idas.BaseInput = inputValues; if (null == batteryVoltage) { return; } var voltage = Convert.ToDouble(batteryVoltage.Value); if (voltage < idas.MinimumValidBatteryVoltage) { voltage = 0D; } if (voltage > idas.MaximumValidBatteryVoltage) { voltage = 0D; } inputValues.BatteryVoltage = voltage; inputValues.BatteryMilliVolts = 1000D * voltage; inputValues.BatteryVoltageStatusColor = RESTSliceProFD.ConvertBatteryVoltage2Color(voltage, idas); //StatusDisplayBattery inputValues.StatusDisplayBattery = ((voltage < idas.MinimumValidBatteryVoltage) || (voltage > idas.MaximumValidBatteryVoltage)) ? "---" : $"{voltage:0.00} V"; inputValues.BatteryVoltageStatus = inputValues.StatusDisplayBattery; } public abstract ICommunication GetICommunication(); public abstract ICommunication GetICommunication(ConnectedDevice dev); public abstract IConnectedDevice GetIConnectedDevice(ICommunication comm); public abstract bool IsCorrectType(ConnectedDevice dev); public abstract DFConstantsAndEnums.DASType GetDASType(); public abstract Guid GetGuid(); public virtual int GetProductId() { return 0; } public virtual string GetProductIdString() { return string.Empty; } protected DeviceHandling _handler; public virtual void SetHandler(DeviceHandling handler) { _handler = handler; } } internal class SPFDRESTSetup : SPFDSetup { public override ICommunication GetICommunication() { var et = new RESTSliceProFD(); et.OnDisconnected += et_OnDisconnected; return et; } void et_OnDisconnected(object sender, EventArgs e) { _handler.ReportDisconnect(sender); } public override ICommunication GetICommunication(ConnectedDevice dev) { return (dev.Dev as ConnectedRESTSPFD).Comm; } public override IConnectedDevice GetIConnectedDevice(ICommunication comm) { return new ConnectedRESTSPFD(comm as RESTSliceProFD); } public override bool IsCorrectType(ConnectedDevice dev) { return dev.Dev is ConnectedRESTSPFD; } public override DFConstantsAndEnums.DASType GetDASType() { return DFConstantsAndEnums.DASType.REST_SPFD; } public override Guid GetGuid() { return Guid.Empty; } public SPFDRESTSetup() : base() { } } internal class RESTSPFDHandling : DeviceHandling { private static readonly object SPFDHOSTSLOCK = new object(); private readonly ManualResetEvent SPFDSignalEvent = new ManualResetEvent(false); private string[] _SPFDHostNames; public string[] SPFDHostNames { get { lock (SPFDHOSTSLOCK) { return _SPFDHostNames; } } set { if (SPFDListenerThread == null) { // SLICEDBListenerThread is not running if (null == value || value.Length < 1 || string.Empty == value[0]) { // no-op return; } } else { // it's already running, we need to stop it SPFDSignalEvent.Reset(); _bKeepGoing = false; //setting interrupt here signals anny connection attempts to stop Interrupt(); // this will signal when the listen thread is done executing SPFDSignalEvent.WaitOne(3000, false); } lock (SPFDHOSTSLOCK) { _SPFDHostNames = value; } // start it up SPFDSignalEvent.Reset(); _interrupt.Reset(); SPFDListenerThread = new Thread(SPFDEventListenerTask) { IsBackground = true }; SPFDListenerThread.Start(); // wait for it to start SPFDSignalEvent.WaitOne(); } } private void OnSPFDConnected(string s) { lock (_ConnectedDASLock) { if (!_connectedSPFD.Contains(s)) { APILogger.Log($"Added to connected devices {s} [SPFD]"); _connectedSPFD.Add(s); } } UpdateConnectedDevices(); } private void SPFDEventListenerTask() { lock (_ConnectedDASLock) { _connectedSPFD.Clear(); } // signal that we're alive _bKeepGoing = true; SPFDSignalEvent.Set(); try { if (null == _SPFDHostNames || _SPFDHostNames.Length < 1) { return; } while (_bKeepGoing) { var hosts = (string [])SPFDHostNames.Clone(); foreach (var host in hosts) { if (_connectedSPFD.Contains(host)) { continue; } using (var source = new CancellationTokenSource()) { try { source.CancelAfter(100); var task = CANFD.API.GetSerial(host, source.Token); task.Wait(); if ( task.IsCompleted) { OnSPFDConnected(host); } } catch( Exception ex) { APILogger.Log($"Failed to connect to {host}", ex); } } } Thread.Sleep(RECONNECT_SPIN_MS); } } catch (ThreadInterruptedException) { //don't log } catch (ThreadAbortException) { // we must exit } catch (Exception) { // don't care } finally { _bKeepGoing = false; //signal that the thread is done and any connection attempts should stop _interrupt.Set(); RemoveAll(); SPFDListenerThread = null; _SPFDHostNames = null; // signal that thread is done executing SPFDSignalEvent.Set(); } } private const int RECONNECT_SPIN_MS = 200; private volatile bool _bKeepGoing; private readonly ManualResetEvent _interrupt = new ManualResetEvent(false); public void Interrupt() { _interrupt.Set(); } private void RemoveAll() { EnqueueDisconnect(); lock (_ConnectedDASLock) { _connectedSPFD.Clear(); } } public RESTSPFDHandling(DASFactory _factory, UpdateFinishedEventHandler _SerialUpdateFinished, IDeviceSetup deviceSetup, BlockingCollection> queueActionPerDevice ) : base(_factory, _SerialUpdateFinished, queueActionPerDevice) { factory = _factory; _spfdSetup = deviceSetup; } private static DASFactory factory { get; set; } internal override bool DetachAllDevices() { var bDevicesRemoved = false; SPFDHostNames = new string[0]; lock (ConnectedDevicesLock) { foreach (var dev in ConnectedDevices) { dev.Dispose(); bDevicesRemoved = true; } ConnectedDevices.Clear(); _connectedSPFD.Clear(); } return bDevicesRemoved; } private Thread SPFDListenerThread; public override void Dispose() { try { if (null != SPFDListenerThread) { SPFDListenerThread.Abort(); } } catch (Exception) { } base.Dispose(); } internal void OnSPFDConnect(string s) { lock (_ConnectedDASLock) { if (!_connectedSPFD.Contains(s)) { APILogger.Log($"Added to connected devices {s} [SPFD]"); _connectedSPFD.Add(s); } } UpdateConnectedDevices(); } internal void OnSPFDDisconnect(string s) { var updated = false; lock (_ConnectedDASLock) { if (_connectedSPFD.Contains(s)) { updated = true; _connectedSPFD.Remove(s); APILogger.Log($"Removed from connected devices {s} [SPFD]"); } } if (updated) { UpdateDisconnectedDevices(); } } private readonly List _connectedSPFD = new List(); private readonly object _ConnectedDASLock = new object(); private List GetConnectedEthernetDeviceString() { var list = new List(); lock (_ConnectedDASLock) { if (null == _connectedSPFD || _connectedSPFD.Count < 1) { return list; } list.AddRange(_connectedSPFD); } return list; } private List GetActiveConnectedEthernetDeviceStrings() { var list = new List(); lock (_ConnectedDASLock) { if (null == _connectedSPFD || _connectedSPFD.Count < 1) { return list; } } var devs = ConnectedDevices.ToArray(); foreach (var dev in devs) { list.Add(dev.Dev.ConnectString); } return list; } private IDeviceSetup _spfdSetup { get; set; } private List GetConnectedDeviceStrings() { return GetConnectedEthernetDeviceString(); } private List GetAllActiveDeviceStrings() { return GetActiveConnectedEthernetDeviceStrings(); } private new ICommunication GetICommunication() { return _spfdSetup.GetICommunication(); } private new IConnectedDevice GetIConnectedDevice(ICommunication com) { return _spfdSetup.GetIConnectedDevice(com); } private const int CONNECT_TIMEOUT_MS = 3000; private void UpdateConnectedSPFD() { ConnectNewDevices(GetConnectedDeviceStrings, GetAllActiveDeviceStrings, GetICommunication, GetIConnectedDevice, _spfdSetup.GetDASType(), _spfdSetup, CONNECT_TIMEOUT_MS ); } public override void UpdateConnectedDevices() { UpdateConnectedSPFD(); } private bool IsCorrectType(ConnectedDevice dev) { return _spfdSetup.IsCorrectType(dev); } private new ICommunication ConnectedDevice2Communication(ConnectedDevice dev) { return _spfdSetup.GetICommunication(dev); } private void UpdateDisconnectedSPFD() { DisconnectRemovedDevices(GetConnectedDeviceStrings, IsCorrectType, ConnectedDevice2Communication, _spfdSetup.GetDASType(), CONNECT_TIMEOUT_MS); } public override void UpdateDisconnectedDevices() { UpdateDisconnectedSPFD(); } public override void UpdateDeviceSetups() { _spfdSetup.SetHandler(this); } } }