411 lines
17 KiB
C#
411 lines
17 KiB
C#
|
|
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
|
|||
|
|
}
|
|||
|
|
}
|