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 { /// /// all possible status that can be relayed to consumers /// public enum StatusValues { Preparing, Downloading, CaptureAttributes, Failed, ROIFailed, Completed, Cancelling, Cancelled, CancelledPartial, DownloadDirectory, MissingHardware, NoDataToDownload, NotAllChannelsDownloaded, ExistingFiles, CleaningUp, QueryEventData } /// /// signals cancel was requested /// public ManualResetEvent CancelEvent = new ManualResetEvent(false); /// /// signals when ping and connect is finished /// public ManualResetEvent DoneEvent = new ManualResetEvent(false); /// /// action to take on completion of ping and connect /// public ActionCompleteDelegate CompleteAction { get; set; } /// /// action to take on progress notification /// public SetProgressValueDelegate ProgressAction { get; set; } /// /// action to take on a status notification /// public StatusIntDelegate StatusAction { get; set; } /// /// action to take on extended status notification /// public StatusExIntDelegate StatusExAction { get; set; } /// /// if we were told of DAS to download from, /// this will be true if all the das were downloaded /// public bool AllDASFinished { get; set; } = false; public void Reset() { AllDASFinished = false; CompleteAction = null; StatusAction = null; StatusExAction = null; ProgressAction = null; } /// /// starts Downloading, returns immediately /// 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 dasList) { var eventInfo = new DownloadReport.EventInfo(); var eventIds = new List(); var events = new Dictionary(); 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; } /// /// 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] /// /// /// /// /// /// /// /// /// public bool DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, DownloadParameters param, ref bool copied, bool uploadingData) { try { var fileLocations = new List(); var destFileLocations = new List(); var dirs = new List(); 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(); 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 } }