This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,410 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Interface.DASFactory.Download;
using DTS.Common.Interface.StatusAndProgressBar;
using DTS.Common.Utilities.Logging;
namespace DTS.DASLib.Service.StateMachine
{
public class DownloadStatusInformation : IStatusInfo
{
/// <summary>
/// all possible status that can be relayed to consumers
/// </summary>
public enum StatusValues
{
Preparing,
Downloading,
CaptureAttributes,
Failed,
ROIFailed,
Completed,
Cancelling,
Cancelled,
CancelledPartial,
DownloadDirectory,
MissingHardware,
NoDataToDownload,
NotAllChannelsDownloaded,
ExistingFiles,
CleaningUp,
QueryEventData
}
/// <summary>
/// signals cancel was requested
/// </summary>
public ManualResetEvent CancelEvent = new ManualResetEvent(false);
/// <summary>
/// signals when ping and connect is finished
/// </summary>
public ManualResetEvent DoneEvent = new ManualResetEvent(false);
/// <summary>
/// action to take on completion of ping and connect
/// </summary>
public ActionCompleteDelegate CompleteAction { get; set; }
/// <summary>
/// action to take on progress notification
/// </summary>
public SetProgressValueDelegate ProgressAction { get; set; }
/// <summary>
/// action to take on a status notification
/// </summary>
public StatusIntDelegate StatusAction { get; set; }
/// <summary>
/// action to take on extended status notification
/// </summary>
public StatusExIntDelegate StatusExAction { get; set; }
/// <summary>
/// if we were told of DAS to download from,
/// this will be true if all the das were downloaded
/// </summary>
public bool AllDASFinished { get; set; } = false;
public void Reset()
{
AllDASFinished = false;
CompleteAction = null;
StatusAction = null;
StatusExAction = null;
ProgressAction = null;
}
/// <summary>
/// starts Downloading, returns immediately
/// </summary>
public void Download()
{
_DownloadTask = Task.Run(() =>
{
AllDASFinished = false;
var param = States.Instance.DownloadStart.Status.DownloadParams;
var global = States.Instance.DownloadStart.Status.GlobalStatusParameters;
var dasFactory = States.Instance.DownloadStart.DASFactory;
var errorCallback = param.ErrorCallback;
//get a list of all the das to download from
var dasList = param.DASList.ToList();
StatusAction?.Invoke((int)StatusValues.Preparing);
//check if we are supposed to download from a unit, but it's not available
var activeDASList = dasFactory.GetDASList();
if (dasList.Exists(das => !activeDASList.Contains(das)))
{
var missingList = dasList.Where(das => !activeDASList.Contains(das)).ToList();
var dr = errorCallback?.Invoke(StatusValues.MissingHardware.ToString(), string.Join(Environment.NewLine, missingList.Select(das => das.SerialNumber).ToList()));
if (DialogResult.OK != dr)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
}
//check if we have any das left to download from
if (!dasList.Any())
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
//check that all units have config data
if (dasList.Exists(das => das.ConfigData == null))
{
//throw error. by state machine definition we should have gone through Config first
StatusExAction?.Invoke((int)StatusValues.NoDataToDownload, dasList.Where(das => das.ConfigData == null).ToArray());
}
//we have das, they have config data. get the event
var oneEvent = GetOneEvent(dasList);
if (null == oneEvent)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
//create folder environment
var eventId = oneEvent.EventId;
var currentTestTestId = param.CurrentTestTestId;
var currentTestTestIdNode = param.CurrentTestTestIdNode;
var currentTestTestDirectory = param.CurrentTestTestDirectory;
var currentTestOriginalTestDirectory = param.CurrentTestOriginalTestDirectory;
var defaultDownloadFolder = param.DefaultDownloadFolder;
var DownloadFolder = Path.Combine(Environment.CurrentDirectory, defaultDownloadFolder);
var ROI = param.ROI;
var recovery = param.Recovery;
var foldersCopied = param.FoldersCopied;
var directory = Path.Combine(DownloadFolder, eventId, currentTestTestId, "Binary");
if (!Directory.Exists(directory))
{
try
{
Directory.CreateDirectory(directory);
}
catch (Exception ex)
{
StatusAction?.Invoke((int)StatusValues.Failed);
}
}
StatusAction?.Invoke((int)StatusValues.DownloadDirectory);
var DownloadDirectory = Path.Combine(directory, ROI ? "ROI" : "ALL");
var TestItem = $"{defaultDownloadFolder}\\{eventId}\\{currentTestTestId}\\Binary\\{(ROI ? "ROI" : "ALL")}"; //optimize this
if (recovery && Directory.Exists(directory))
{
if (currentTestTestId != currentTestTestIdNode)
{
//We want to keep ROI and ALL "recovery" downloads in the same path, so use the same "Test Id" node in the path
//Since the .../Binary folder exists, we know that this test has downloaded before, so create a recovery folder
DownloadDirectory = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "Binary", ROI ? "ROI" : "ALL");
}
if ((currentTestTestId != currentTestTestIdNode || currentTestTestDirectory != currentTestOriginalTestDirectory) &&
!foldersCopied)
{
//Either this is a "recovery" folder or the Test Id field was changed in Basic info.
//Either way, the following files need to be copied from the original folder
var copied = false;
var originalTestIdNode = Path.GetFileName(currentTestOriginalTestDirectory);
var sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, Common.Constants.DAS_CONFIGS);
var destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, Common.Constants.DAS_CONFIGS);
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, "Logs");
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "Logs");
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, Common.Constants.REPORT_DIR_NAME);
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, Common.Constants.REPORT_DIR_NAME);
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
sourceDir = Path.Combine(DownloadFolder, eventId, originalTestIdNode, "SETUP");
destDir = Path.Combine(DownloadFolder, eventId, currentTestTestIdNode, "SETUP");
DirectoryCopy(sourceDir, destDir, true, param, ref copied, false);
}
}
var mre = new ManualResetEvent(false);
mre.WaitOne();
if (!CancelEvent.WaitOne(1, false))
{
//check that all the units that should have been configured were
var allDownloaded = Array.TrueForAll(param.DASList, d => dasList.Contains(d));
AllDASFinished = allDownloaded;
CompleteAction?.Invoke();
}
StatusAction?.Invoke((int)StatusValues.Completed);
DoneEvent.Set();
});
}
private Task _DownloadTask = null;
public async Task Cancel()
{
CancelEvent.Set();
StatusAction?.Invoke((int)StatusValues.Cancelling);
//simulate time spent waiting for cancel to be acknowledged
Thread.Sleep(100);
DoneEvent.WaitOne();
StatusAction?.Invoke((int)StatusValues.Cancelled);
}
#region helpers
private static string GetHash(DownloadReport.EventInfo myEvent)
{
return $"{myEvent.TestID}_{myEvent.EventNumber:00}".ToUpper();
}
private IEventInfoAggregate GetOneEvent(List<IDASCommunication> dasList)
{
var eventInfo = new DownloadReport.EventInfo();
var eventIds = new List<string>();
var events = new Dictionary<string, IEventInfoAggregate>();
foreach (var das in dasList)
{
if (null == das.EventInfo || !das.EventInfo.Events.Any()) { continue; }
eventInfo = (DownloadReport.EventInfo)das.EventInfo.Events[0];
var key = GetHash(eventInfo);
if (!eventIds.Contains(key))
{
eventIds.Add(key);
//TODO: Add Events, etc to
//events.Add(key, new IEventInfoAggregate(eventInfo));
}
else
{
events[key].Add(eventInfo);
}
}
IEventInfoAggregate oneEvent = null;
if (events.Count > 0)
{
oneEvent = events[GetHash(eventInfo)];
}
return oneEvent;
}
/// <summary>
/// we don't use move as we don't want to necessarily disturb directories already in the target location, and any files that are already there
/// [but we will overwrite if a source file exists in the destination]
/// </summary>
/// <param name="sourceDirName"></param>
/// <param name="destDirName"></param>
/// <param name="copySubDirs"></param>
/// <param name="param"></param>
/// /// <param name="copied"></param>
/// /// <param name="uploadingData"></param>
public bool DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, DownloadParameters param, ref bool copied, bool uploadingData)
{
try
{
var fileLocations = new List<string>();
var destFileLocations = new List<string>();
var dirs = new List<string>();
dirs.Add(sourceDirName);
while (dirs.Count > 0)
{
var currentDir = dirs[0];
dirs.RemoveAt(0);
var dir = new System.IO.DirectoryInfo(currentDir);
if (!dir.Exists) { continue; }
var files = dir.GetFiles();
fileLocations.AddRange(files.Select(file => file.FullName));
var subDirs = dir.GetDirectories();
dirs.AddRange(subDirs.Select(thisdir => thisdir.FullName));
}
//uri requires folders end with \\
if (!sourceDirName.EndsWith("\\")) { sourceDirName += "\\"; }
var diSourceDir = new System.IO.DirectoryInfo(sourceDirName);
if (!destDirName.EndsWith("\\")) { destDirName += "\\"; }
var curFileUndex = 1;
var existingFiles = new List<string>();
for (var i = fileLocations.Count - 1; i >= 0; i--)
{
var curFile = fileLocations[i];
if (uploadingData)
{
var tokens = curFile.Split('\\');
if (tokens.Contains("Binary"))
{
if (!param.DefaultUploadBinaries)
{
continue;
}
}
else if (tokens.Contains("DASConfigs"))
{
if (!param.DefaultUploadSetups)
{
continue;
}
}
else if (tokens.Contains("SETUP"))
{
if (!param.DefaultUploadSetups)
{
continue;
}
}
else if (tokens.Contains("Reports"))
{
if (!param.DefaultUploadReports)
{
continue;
}
}
else if (tokens.Contains("Logs"))
{
if (!param.DefaultUploadLogs)
{
continue;
}
}
else if (tokens.Contains("Exports"))
{
if (!param.DefaultUploadExports)
{
continue;
}
}
}
var fiCurFile = new System.IO.FileInfo(curFile);
var path1 = new Uri("file:\\\\" + diSourceDir.FullName);
var path2 = new Uri("file:\\\\" + fiCurFile.FullName);
var diff = path1.MakeRelativeUri(path2);
var relPath = diff.OriginalString;
path2 = new Uri("file:\\\\" + destDirName);
path2 = new Uri(path2, relPath);
destFileLocations.Add(path2.LocalPath);
if (System.IO.File.Exists(path2.LocalPath))
{
existingFiles.Add(path2.LocalPath);
}
}
if (existingFiles.Count > 0)
{
var temp = new Uri("file:\\\\" + destDirName);
var dr = param.ErrorCallback?.Invoke(StatusValues.ExistingFiles.ToString(), string.Format(Resources.UploadData_Files_Exist, temp.LocalPath, "\n\n"));
if (DialogResult.OK != dr)
{
return false;
}
}
try
{
for (var i = 0; i < fileLocations.Count && i < destFileLocations.Count; i++)
{
var fiSource = new System.IO.FileInfo(fileLocations[i]);
var fiDest = new System.IO.FileInfo(destFileLocations[destFileLocations.Count - 1 - i]);
if (!System.IO.Directory.Exists(fiDest.Directory.FullName)) { System.IO.Directory.CreateDirectory(fiDest.Directory.FullName); }
System.IO.File.Copy(fiSource.FullName, fiDest.FullName, true);
var percent = 100D * curFileUndex++ / fileLocations.Count;
}
}
catch (Exception ex)
{
APILogger.Log("Upload Failure from ", sourceDirName, " to ", destDirName, ex);
return false;
}
if (!uploadingData) return false;
APILogger.Log("Upload Success from ", sourceDirName, " to ", destDirName);
copied = true;
Thread.Sleep(10);
}
catch (Exception ex)
{
APILogger.Log("Upload Failure from ", sourceDirName, " to ", destDirName, ex);
return false;
}
return true;
}
#endregion
}
}

View File

@@ -0,0 +1,135 @@
using DTS.Common.Enums.DASFactory;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.DASLib.Command.TDAS;
namespace DTS.DASLib.Service
{
/// <summary>
/// Each time <see cref="DTS.DASLib.Service.DiagnosticsService" />.Diagnose is called these values
/// are populated. Each IDASCommunication will have one of these objects. It
/// provides information about input voltage to the base unit, like power input,
/// presence of a battery and battery output voltage.
/// </summary>
public class BaseInputValues : IBaseInputValues
{
/// <summary>
/// The current input voltage to the base.
/// </summary>
public double InputMilliVolts { get; set; }
//18740 DataPRO system settings Power setting values for TDAS rack do not change actual power values on device
// added Min/Max valid and tied Valid bool to those values
public virtual bool InputMilliVoltsValid => InputMilliVolts > 1000D * MinimumValidInputVoltage && InputMilliVolts < 1000D * MaximumValidInputVoltage;
/// <summary>
/// The current input voltage to the base.
/// </summary>
public double InputVoltage { get; set; }
/// <summary>
/// The minimum valid input voltage to the base.
/// </summary>
public double MinimumValidInputVoltage { get; set; } = 6D;
/// <summary>
/// The maximum valid input voltage to the base.
/// </summary>
public double MaximumValidInputVoltage { get; set; } = 16D;
public virtual bool BatteryMilliVoltsValid => BatteryMilliVolts > 1000D * MinimumValidBatteryVoltage && BatteryMilliVolts < 1000D * MaximumValidBatteryVoltage;
/// <summary>
/// The current battery voltage.
/// </summary>
public double BatteryMilliVolts { get; set; }
/// <summary>
/// The current battery voltage.
/// </summary>
public double BatteryVoltage { get; set; }
/// <summary>
/// battery state of charge - null if not queried
/// can be 0 if not available
/// this is a percentage
/// </summary>
public double? BatterySoC { get; set; } = null;
/// <summary>
/// The minimum valid battery voltage to the base.
/// </summary>
public double MinimumValidBatteryVoltage { get; set; } = 6D;
/// <summary>
/// The maximum valid battery voltage to the base.
/// </summary>
public double MaximumValidBatteryVoltage { get; set; } = 16D;
/// <summary>
/// TRUE if the battery is currently charging.
/// </summary>
public bool BatteryIsCharging { get; set; }
/// <summary>
/// Temperature sensed by logic in the hardware, in degrees Celsius.
/// </summary>
public double TemperatureC { get; set; }
/// <summary>
/// returns status
/// </summary>
public virtual string BatteryVoltageStatus { get; set; }
/// <summary>
/// returns status
/// </summary>
public virtual string InputVoltageStatus { get; set; }
/// <summary>
/// returns status
/// </summary>
public virtual string StatusDisplayBattery { get; set; }
/// <summary>
/// returns status
/// </summary>
public virtual string StatusDisplayInput { get; set; }
/// <summary>
/// returns color
/// </summary>
public virtual DFConstantsAndEnums.VoltageStatusColor BatteryVoltageStatusColor { get; set; }
/// <summary>
/// returns color
/// </summary>
public virtual DFConstantsAndEnums.VoltageStatusColor InputVoltageStatusColor { get; set; }
public double ChargeCapacity { get; set; } = double.NaN;
public bool ChargeCapacityValid => !double.IsNaN(ChargeCapacity) && ChargeCapacity > 0 && ChargeCapacity < 100;
public BaseInputValues() { }
public BaseInputValues(IBaseInputValues copy)
{
InputMilliVolts = copy.InputMilliVolts;
InputVoltage = copy.InputVoltage;
MinimumValidInputVoltage = copy.MinimumValidInputVoltage;
MaximumValidInputVoltage = copy.MaximumValidInputVoltage;
BatteryMilliVolts = copy.BatteryMilliVolts;
BatteryVoltage = copy.BatteryVoltage;
BatterySoC = copy.BatterySoC;
MinimumValidBatteryVoltage = copy.MinimumValidBatteryVoltage;
MaximumValidBatteryVoltage = copy.MaximumValidBatteryVoltage;
BatteryIsCharging = copy.BatteryIsCharging;
TemperatureC = copy.TemperatureC;
BatteryVoltageStatus = copy.BatteryVoltageStatus;
InputVoltageStatus = copy.InputVoltageStatus;
StatusDisplayBattery = copy.StatusDisplayBattery;
StatusDisplayInput = copy.StatusDisplayInput;
BatteryVoltageStatusColor = copy.BatteryVoltageStatusColor;
InputVoltageStatusColor = copy.InputVoltageStatusColor;
ChargeCapacity = copy.ChargeCapacity;
}
}
}