Files
DP44/DataPRO/DASFactory/DASFactory.SPFD.cs

592 lines
20 KiB
C#
Raw Normal View History

2026-04-17 14:55:32 -04:00
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<DASChannel>();
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<CANInfo> GetChannels(ConnectedDevice dev)
{
if ((dev == null) || (dev.Dev == null) || (dev.Dev.Comm == null)) { return new List<CANInfo>(); }
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<CANInfo>();
}
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<Tuple<QueueActions, DeviceHandling>> 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<string> _connectedSPFD = new List<string>();
private readonly object _ConnectedDASLock = new object();
private List<string> GetConnectedEthernetDeviceString()
{
var list = new List<string>();
lock (_ConnectedDASLock)
{
if (null == _connectedSPFD || _connectedSPFD.Count < 1) { return list; }
list.AddRange(_connectedSPFD);
}
return list;
}
private List<string> GetActiveConnectedEthernetDeviceStrings()
{
var list = new List<string>();
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<string> GetConnectedDeviceStrings()
{
return GetConnectedEthernetDeviceString();
}
private List<string> 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);
}
}
}