From afa8693682bb0b8c68c7fa7427f9139ccc720303 Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Fri, 23 Feb 2024 14:29:15 +0100 Subject: [PATCH 1/8] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dbd545c..900cc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /WpfApp/obj /App.WPF /WpfApp/bin/Debug/net8.0-windows +/._.gitignore From f320173b5b52c944a43f277af392ff2a50f7fa4e Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Sat, 24 Feb 2024 11:58:38 +0100 Subject: [PATCH 2/8] #104 Edit pour utiliser le StreamWriter au lieu du file copy --- App.Cmd/ViewModels/SaveViewModel.cs | 2 +- App.Core/Services/CopyService.cs | 330 +++++++++++++++-------- App.Core/Services/LoggerService.cs | 53 +--- App.Core/Services/SaveService.cs | 22 +- App.Core/Services/StateManagerService.cs | 29 +- 5 files changed, 268 insertions(+), 168 deletions(-) diff --git a/App.Cmd/ViewModels/SaveViewModel.cs b/App.Cmd/ViewModels/SaveViewModel.cs index 2c27d10..eeee831 100644 --- a/App.Cmd/ViewModels/SaveViewModel.cs +++ b/App.Cmd/ViewModels/SaveViewModel.cs @@ -215,7 +215,7 @@ public bool UserChoice(int UserEntry) saveService!.CreateSave( InPath, OutPath, type, SaveName); - stateManagerService!.UpdateStateFile(); + //stateManagerService!.UpdateStateFile(); ListSaveModel = saveService.ListSaveModel; listState = stateManagerService!.listStateModel!; diff --git a/App.Core/Services/CopyService.cs b/App.Core/Services/CopyService.cs index c6e05ca..a317158 100644 --- a/App.Core/Services/CopyService.cs +++ b/App.Core/Services/CopyService.cs @@ -29,13 +29,13 @@ public CopyService(StateManagerService stateManagerService) this.stateManagerService = stateManagerService; } - public void ExecuteCopy(SaveModel saveModel) + public void ExecuteCopy(SaveModel saveModel, StreamWriter logWriter, StreamWriter stateWriter) { isPaused = false; isStopped = false; totalFile = 0; totalSize = 0; - ProcessCopy(saveModel); + BackupDirectory(saveModel.InPath, saveModel.OutPath, logWriter, stateWriter); } public void PauseCopy(SaveModel saveModel) @@ -50,12 +50,12 @@ public void StopCopy(SaveModel saveModel) // Implement any necessary logic for stopping the copying process } - public void ResumeCopy(SaveModel saveModel) - { - isPaused = false; - // Implement any necessary logic for resuming the copying process - ProcessCopy(saveModel); - } + //public void ResumeCopy(SaveModel saveModel) + //{ + // isPaused = false; + // // Implement any necessary logic for resuming the copying process + // BackupDirectory(saveModel); + //} private void EncryptFile(string sourcePath) { @@ -70,137 +70,253 @@ private void EncryptFile(string sourcePath) } - private void ProcessCopy(SaveModel saveModel) + + void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter logWritter, StreamWriter stateWritter) { - if (isPaused || isStopped) - return; + int TotalFailed = 0; - foreach (var item in Directory.GetFiles(this.CopyModel.SourcePath, "*", SearchOption.AllDirectories)) - { - totalFile += 1; - totalSize += new FileInfo(item).Length; - } + // Stage 1: Create the destination folder - foreach (StateManagerModel stateModel in stateManagerService.listStateModel!) + if (!Directory.Exists(targetDirPath)) { - if (stateModel.SaveName == saveModel.SaveName) + try { + Directory.CreateDirectory(targetDirPath); + logWritter.WriteLine($"Create folder: {targetDirPath}"); + } + catch (Exception ex) + { + // Cannot create folder + stateWritter.WriteLine($"Failed to create folder: {targetDirPath}\r\nAccess Denied\r\n"); + return; + } + } - stateModel.TotalFilesToCopy += totalFile; - stateModel.NbFilesLeftToDo = totalFile; - stateModel.TotalFilesSize = totalSize; - stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); - progress = stateModel.Progression; - stateModel.State = "ACTIVE"; - stateManagerService.UpdateStateFile(); + // Stage 2: Get all files from the source folder - if (File.Exists(this.CopyModel.SourcePath)) - { - // File copying operation + string[] files = null; - stateModel.SourceFilePath = this.CopyModel.SourcePath; - stateModel.TargetFilePath = this.CopyModel.TargetPath; - stateManagerService.UpdateStateFile(); - + try + { + files = Directory.GetFiles(sourceDirPath); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot read folder or files + stateWritter.WriteLine($"{sourceDirPath}\r\nAccess Denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWritter.WriteLine($"{sourceDirPath}\r\n{e.Message}\r\n"); + } + // Stage 3: Copy all files from source to destination folder + if (files != null && files.Length > 0) + { + foreach (string file in files) + { + try + { + string name = Path.GetFileName(file); + string dest = Path.Combine(targetDirPath, name); - File.Copy(this.CopyModel.SourcePath, this.CopyModel.TargetPath, true); - stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); - progress = stateModel.Progression; - stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; + File.Copy(file, dest, true); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot write file + stateWritter.WriteLine($"{file}\r\nAccess Denied\r\n"); + TotalFailed++; + } + catch (Exception e) + { + // Other unknown error + TotalFailed++; + stateWritter.WriteLine($"{file}\r\n{e.Message}\r\n"); + } + } + } - stateManagerService.UpdateStateFile(); + // Stage 4: Get all sub-folders + string[] folders = null; - loggerService!.loggerModel!.Name = saveModel.SaveName; - loggerService.loggerModel.FileSource = this.CopyModel.SourcePath; - loggerService.loggerModel.FileTarget = this.CopyModel.TargetPath; - loggerService.loggerModel.FileSize = new FileInfo(this.CopyModel.SourcePath).Length.ToString(); - loggerService.AddEntryLog(); + try + { + folders = Directory.GetDirectories(sourceDirPath); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot read folders + stateWritter.WriteLine($"{sourceDirPath}\r\nAccess denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWritter.WriteLine($"{sourceDirPath}\r\nAccess {e.Message}\r\n"); + } - } - else if (Directory.Exists(this.CopyModel.SourcePath)) + // Stage 5: Backup files and "sub-sub-folders" in the sub-folder + if (folders != null && folders.Length > 0) + { + foreach (string folder in folders) + { + try { - // Directory copying operation - CopyDirectory(this.CopyModel.SourcePath, this.CopyModel.TargetPath, stateModel); + string name = Path.GetFileName(folder); + string dest = Path.Combine(targetDirPath, name); + // recursive call with updated source and target paths + BackupDirectory(folder, dest, logWritter, stateWritter); } - else + catch (UnauthorizedAccessException) { - // Log the error and handle it gracefully - loggerService!.loggerModel!.Name = saveModel.SaveName; - loggerService.AddEntryLog(); + // Access denied, cannot read folders + stateWritter.WriteLine($"{folder}\r\nAccess denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWritter.WriteLine($"{targetDirPath}\r\nAccess {e.Message}\r\n"); } - - - - stateModel.State = "END"; - stateManagerService.UpdateStateFile(); } } + if (this.isEncrypted) EncryptFile(this.CopyModel.TargetPath); + } - if (isEncrypted) EncryptFile(this.CopyModel.TargetPath); + //private void ProcessCopy(SaveModel saveModel) + //{ + // if (isPaused || isStopped) + // return; - } + // foreach (var item in Directory.GetFiles(this.CopyModel.SourcePath, "*", SearchOption.AllDirectories)) + // { + // totalFile += 1; + // totalSize += new FileInfo(item).Length; + // } + // foreach (StateManagerModel stateModel in stateManagerService.listStateModel!) + // { + // if (stateModel.SaveName == saveModel.SaveName) + // { - private void CopyDirectory(string sourceDirPath, string targetDirPath, StateManagerModel stateModel) - { + // stateModel.TotalFilesToCopy += totalFile; + // stateModel.NbFilesLeftToDo = totalFile; + // stateModel.TotalFilesSize = totalSize; + // stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); + // progress = stateModel.Progression; + // stateModel.State = "ACTIVE"; + // stateManagerService.UpdateStateFile(); + // if (File.Exists(this.CopyModel.SourcePath)) + // { + // File copying operation - // Vérifie si le répertoire source existe - if (!Directory.Exists(sourceDirPath)) - { - Console.WriteLine("Le répertoire source n'existe pas."); - return; - } + // stateModel.SourceFilePath = this.CopyModel.SourcePath; + // stateModel.TargetFilePath = this.CopyModel.TargetPath; + // stateManagerService.UpdateStateFile(); - // Crée le répertoire cible s'il n'existe pas encore - if (!Directory.Exists(targetDirPath)) - { - Directory.CreateDirectory(targetDirPath); - } - // Obtient la liste des fichiers et des sous-répertoires dans le répertoire source - string[] files = Directory.GetFiles(sourceDirPath); - string[] subdirectories = Directory.GetDirectories(sourceDirPath); - - // Copie les fichiers - foreach (string filePath in files) - { - string fileName = Path.GetFileName(filePath); - string targetFilePath = Path.Combine(targetDirPath, fileName); - stateModel.SourceFilePath = filePath; - stateModel.TargetFilePath = targetFilePath; - - stateManagerService.UpdateStateFile(); - - DateTime start = DateTime.Now; - File.Copy(filePath, targetFilePath, true); // Le paramètre true permet d'écraser le fichier s'il existe déjà dans le répertoire cible - stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; - start = DateTime.Now; - - loggerService!.loggerModel!.FileEncryptionTime = (DateTime.Now - start).ToString(); - - stateManagerService.UpdateStateFile(); - loggerService!.loggerModel!.Name = stateModel.SaveName; - loggerService!.loggerModel!.FileSource = filePath; - loggerService!.loggerModel.FileTarget = targetFilePath; - loggerService!.loggerModel.FileSize = new FileInfo(filePath).Length.ToString(); - loggerService!.loggerModel.FileTransferTime = (DateTime.Now - start).ToString(); - loggerService.AddEntryLog(); - } + // File.Copy(this.CopyModel.SourcePath, this.CopyModel.TargetPath, true); + // stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); + // progress = stateModel.Progression; + // stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; - // Copie les sous-répertoires récursivement - foreach (string subdirectory in subdirectories) - { - string subdirectoryName = Path.GetFileName(subdirectory); - string targetSubdirectoryPath = Path.Combine(targetDirPath, subdirectoryName); - CopyDirectory(subdirectory, targetSubdirectoryPath, stateModel); // Appel récursif pour copier les sous-répertoires - } - } + // stateManagerService.UpdateStateFile(); + + + // loggerService!.loggerModel!.Name = saveModel.SaveName; + // loggerService.loggerModel.FileSource = this.CopyModel.SourcePath; + // loggerService.loggerModel.FileTarget = this.CopyModel.TargetPath; + // loggerService.loggerModel.FileSize = new FileInfo(this.CopyModel.SourcePath).Length.ToString(); + // loggerService.AddEntryLog(); + + // } + // else if (Directory.Exists(this.CopyModel.SourcePath)) + // { + // Directory copying operation + // CopyDirectory(this.CopyModel.SourcePath, this.CopyModel.TargetPath, stateModel); + + // } + // else + // { + // Log the error and handle it gracefully + // loggerService!.loggerModel!.Name = saveModel.SaveName; + // loggerService.AddEntryLog(); + // } + + + + // stateModel.State = "END"; + // stateManagerService.UpdateStateFile(); + // } + // } + + + // if (isEncrypted) EncryptFile(this.CopyModel.TargetPath); + + + //} + + //private void CopyDirectory(string sourceDirPath, string targetDirPath, StateManagerModel stateModel) + //{ + + + // Vérifie si le répertoire source existe + // if (!Directory.Exists(sourceDirPath)) + // { + // Console.WriteLine("Le répertoire source n'existe pas."); + // return; + // } + + // Crée le répertoire cible s'il n'existe pas encore + // if (!Directory.Exists(targetDirPath)) + // { + // Directory.CreateDirectory(targetDirPath); + // } + + // Obtient la liste des fichiers et des sous-répertoires dans le répertoire source + // string[] files = Directory.GetFiles(sourceDirPath); + // string[] subdirectories = Directory.GetDirectories(sourceDirPath); + + + // Copie les fichiers + // foreach (string filePath in files) + // { + // string fileName = Path.GetFileName(filePath); + // string targetFilePath = Path.Combine(targetDirPath, fileName); + // stateModel.SourceFilePath = filePath; + // stateModel.TargetFilePath = targetFilePath; + + // stateManagerService.UpdateStateFile(); + + // DateTime start = DateTime.Now; + // File.Copy(filePath, targetFilePath, true); // Le paramètre true permet d'écraser le fichier s'il existe déjà dans le répertoire cible + // stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; + // start = DateTime.Now; + + // loggerService!.loggerModel!.FileEncryptionTime = (DateTime.Now - start).ToString(); + + // stateManagerService.UpdateStateFile(); + // loggerService!.loggerModel!.Name = stateModel.SaveName; + // loggerService!.loggerModel!.FileSource = filePath; + // loggerService!.loggerModel.FileTarget = targetFilePath; + // loggerService!.loggerModel.FileSize = new FileInfo(filePath).Length.ToString(); + // loggerService!.loggerModel.FileTransferTime = (DateTime.Now - start).ToString(); + // loggerService.AddEntryLog(); + // } + + // Copie les sous - répertoires récursivement + // foreach (string subdirectory in subdirectories) + // { + // string subdirectoryName = Path.GetFileName(subdirectory); + // string targetSubdirectoryPath = Path.Combine(targetDirPath, subdirectoryName); + // CopyDirectory(subdirectory, targetSubdirectoryPath, stateModel); // Appel récursif pour copier les sous-répertoires + // } + //} } } diff --git a/App.Core/Services/LoggerService.cs b/App.Core/Services/LoggerService.cs index de31b5c..09471c6 100644 --- a/App.Core/Services/LoggerService.cs +++ b/App.Core/Services/LoggerService.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Reflection.Metadata.Ecma335; +using System.Text.Json; using System.Xml.Serialization; using App.Core.Models; using static System.Runtime.InteropServices.JavaScript.JSType; @@ -21,40 +22,6 @@ public LoggerService() loggerModel = new(); } - - //public void WriteLog() - //{ - // //TODO : Gerer les exeptions - - // try - // { - // // Serialize the log model to JSON - - // string logEntry = JsonSerializer.Serialize(loggerModel, options) + ","; - // string jsonlogEntry = JsonSerializer.Serialize(loggerModel, options) + ","; - // // Append the log entry to the log file or create if not exist - // using StreamWriter LogWriter = File.AppendText(jsonlogFilePath); - // LogWriter.WriteLineAsync(jsonlogEntry); - - // // Serialize the log model to XML - // using StringWriter xmlStringWriter = new(); - // XmlSerializer xmlSerializer = new(typeof(loggerModel)); - // xmlSerializer.Serialize(xmlStringWriter, loggerModel); - // string xmlLogEntry = xmlStringWriter.ToString(); - // // Append the XML log entry to the XML log file or create if not exist - - // using StreamWriter xmlLogWriter = File.AppendText(xmlLogFilePath); - // xmlLogWriter.WriteLineAsync(xmlLogEntry); - // } - // catch (Exception ex) - // { - // Console.WriteLine($"Error writing to log file: {ex.Message}"); - - // } - //} - /// - /// Method to open the log file - /// public void OpenLogFile() { // Open the log file in notepad @@ -73,14 +40,22 @@ public void CreateLogFile() File.WriteAllText(jsonlogFilePath, "[]"); } - public void AddEntryLog() + public string GetLogFormat() + { + if (configService.LogsFormat == "JSON") return jsonlogFilePath; + else return xmlLogFilePath; + } + + public void AddEntryLog(StreamWriter streamWriter) { if (configService.LogsFormat == "JSON") { try { - using StreamWriter logWriter = File.AppendText(jsonlogFilePath); - logWriter.WriteLineAsync(JsonSerializer.Serialize(loggerModel, options) + ","); + using (streamWriter = File.AppendText(jsonlogFilePath)) ; + { + streamWriter.WriteLineAsync(JsonSerializer.Serialize(loggerModel, options) + ","); + } } catch (InvalidOperationException) { @@ -93,7 +68,7 @@ public void AddEntryLog() { XmlSerializer xmlSerializer = new XmlSerializer(typeof(LoggerModel)); - using (StreamWriter streamWriter = File.AppendText(xmlLogFilePath)) + using (streamWriter = File.AppendText(xmlLogFilePath)) { xmlSerializer.Serialize(streamWriter, loggerModel ); } diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 960cb24..13fbd3a 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -10,7 +10,13 @@ public class SaveService public required ObservableCollection ListStateManager { get; set; } = []; public required ObservableCollection ListSaveModel { get; set; } = []; + + private readonly StreamWriter? logWriter ; + private readonly StreamWriter? stateWriter; + + private readonly ConfigService configService = new(); + private readonly LoggerService loggerService = new(); public CopyService copyService = new(); private readonly JsonSerializerOptions options = new() { @@ -20,9 +26,11 @@ public class SaveService public SaveService() { + logWriter = new StreamWriter(loggerService.GetLogFormat()); + stateWriter = new StreamWriter(StateManagerService.stateFilePath); LoadSave(); copyService.stateManagerService.listStateModel = ListStateManager; - copyService.stateManagerService.UpdateStateFile(); + copyService.stateManagerService.UpdateStateFile(stateWriter); } public bool IsSoftwareRunning() @@ -42,18 +50,17 @@ public bool IsSoftwareRunning() public void ExecuteSave(SaveModel saveModel) { - // Method to execute the copy service - // Execute the copy service + copyService.CopyModel.SourcePath = saveModel.InPath; copyService.CopyModel.TargetPath = saveModel.OutPath; copyService.stateManagerService.listStateModel = ListStateManager; - copyService.ExecuteCopy(saveModel); + copyService.ExecuteCopy(saveModel, logWriter!, stateWriter!); } - public void LoadSave() + public void LoadSave() { if (File.Exists("saves.json")) @@ -80,11 +87,6 @@ public void LoadSave() } - /// - /// Show info of a save - /// - /// - /// public Tuple ShowInfo(SaveModel saveModel) { return Tuple.Create(saveModel.SaveName, saveModel.InPath, saveModel.OutPath, saveModel.Type, saveModel.Date); diff --git a/App.Core/Services/StateManagerService.cs b/App.Core/Services/StateManagerService.cs index 8a89083..4314f8c 100644 --- a/App.Core/Services/StateManagerService.cs +++ b/App.Core/Services/StateManagerService.cs @@ -8,7 +8,7 @@ public class StateManagerService { public ObservableCollection? listStateModel; - private readonly string stateFilePath = "state.json"; + public static readonly string stateFilePath = "state.json"; //Options for the JsonSerializer @@ -35,32 +35,39 @@ public void OpenStateFile() System.Diagnostics.Process.Start("notepad.exe", stateFilePath); } - public void UpdateStateFile() + public void UpdateStateFile(StreamWriter streamWriter) { - // Clear the state file - ClearStateFile(); - int i = 0; - foreach (StateManagerModel stateModel in listStateModel!) + try { - using (StreamWriter stateWriter = File.AppendText(stateFilePath)) + // Open the file for writing (append mode) + using (streamWriter = File.AppendText(stateFilePath)) { - stateWriter.WriteLineAsync(JsonSerializer.Serialize(stateModel, options) + ","); + foreach (StateManagerModel stateModel in listStateModel!) + { + // Write the serialized stateModel to the file + streamWriter.WriteLineAsync(JsonSerializer.Serialize(stateModel, options) + ","); + } } - //Increment the index to get the next save name - i += 1; + } + catch (IOException ex) + { + // Handle the exception (e.g., log or retry) + Console.WriteLine($"Error updating state file: {ex.Message}"); } } + public void CreateStateFile() { // Create the state file File.WriteAllText(stateFilePath, "[]"); + } public void ClearStateFile() { // Clear the state file - File.WriteAllText(stateFilePath, ""); + File.WriteAllText(stateFilePath, "") ; } From a5371451d7c51ac8f1874d632bef1809d212c2ad Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Sat, 24 Feb 2024 17:01:45 +0100 Subject: [PATCH 3/8] Suite des modifs --- App.Cmd/ViewModels/SaveViewModel.cs | 97 ++++--- App.Core/Models/ConfigModel.cs | 15 -- App.Core/Models/CopyModel.cs | 5 +- App.Core/Models/LoggerModel.cs | 8 +- App.Core/Models/SaveModel.cs | 8 +- App.Core/Models/StateManagerModel.cs | 7 +- App.Core/Services/CopyService.cs | 375 +++++++++------------------ App.Core/Services/SaveService.cs | 15 +- 8 files changed, 219 insertions(+), 311 deletions(-) delete mode 100644 App.Core/Models/ConfigModel.cs diff --git a/App.Cmd/ViewModels/SaveViewModel.cs b/App.Cmd/ViewModels/SaveViewModel.cs index eeee831..7f2cbb7 100644 --- a/App.Cmd/ViewModels/SaveViewModel.cs +++ b/App.Cmd/ViewModels/SaveViewModel.cs @@ -32,7 +32,8 @@ public SaveViewModel() }; loggerService = new(); - copyService = new(stateManagerService); + copyService = new(); + copyService.stateManagerService = stateManagerService; displayService = new(); saveService = new() @@ -46,21 +47,12 @@ public SaveViewModel() saveService.ListStateManager = listState; } - - - public enum TypeOfSave { Sequential, Complete } - - /// - /// Method to get the user choice - /// - /// - /// public bool UserChoice(int UserEntry) { saveService!.ListSaveModel = ListSaveModel; @@ -266,15 +258,36 @@ public bool UserChoice(int UserEntry) string[] parts = UserChoice.Split(';'); int z1 = int.Parse(parts[0]); int z2 = int.Parse(parts[1]); - DisplayService.SetForegroundColor("Green", $"Save {z1} is running"); - saveService!.ExecuteSave(ListSaveModel[z1]); - DisplayService.SetForegroundColor("Green", $"Save {z1} Executed"); - Thread.Sleep(1000); - DisplayService.SetForegroundColor("Green", $"Save {z2} is running"); - saveService!.ExecuteSave(ListSaveModel[z2]); - DisplayService.SetForegroundColor("Green", $"Save {z2} Executed"); - Thread.Sleep(1000); + new Thread(() => + { + saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; + DisplayService.SetForegroundColor("Green", $"Save {z1} is running"); + saveService!.ExecuteSave(ListSaveModel[z1]); + DisplayService.SetForegroundColor("Green", $"Save {z1} Executed"); + saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; + + }).Start(); + + + + + + new Thread(() => + { + saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; + DisplayService.SetForegroundColor("Green", $"Save {z2} is running"); + saveService!.ExecuteSave(ListSaveModel[z2]); + DisplayService.SetForegroundColor("Green", $"Save {z2} Executed"); + saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; + }).Start(); + + + } @@ -296,10 +309,18 @@ public bool UserChoice(int UserEntry) int max = int.Parse(parts[1]); for (int x = min; x <= max; x++) { - DisplayService.SetForegroundColor("Green", $"Save {x} is running"); - saveService!.ExecuteSave(ListSaveModel[x]); - DisplayService.SetForegroundColor("Green", $"Save {x} Executed"); - Thread.Sleep(1000); + new Thread(() => + { + saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; + DisplayService.SetForegroundColor("Green", $"Save {x} is running"); + saveService!.ExecuteSave(ListSaveModel[x]); + DisplayService.SetForegroundColor("Green", $"Save {x} Executed"); + saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; + saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; + }).Start(); + + } } @@ -355,15 +376,21 @@ public bool UserChoice(int UserEntry) } } + private void SaveViewModel_CopyPaused(object? sender, EventArgs e) + { + Console.WriteLine("Save Paused"); + } - - + private void SaveViewModel_CopyStopped(object? sender, EventArgs e) + { + Console.WriteLine("Save Stopped"); + } public void ShowLogs() { //Method to show the logs - loggerService.OpenLogFile(); + loggerService?.OpenLogFile(); } /// @@ -384,17 +411,17 @@ public void ShowSavesSchedule() { FileInfo fileInfo = new("saves.json"); - if (fileInfo.Length > 0) - { - string json = File.ReadAllText("saves.json"); - ListSaveModel = JsonConvert.DeserializeObject>(json) ?? []; + if (fileInfo.Length > 0) + { + string json = File.ReadAllText("saves.json"); + ListSaveModel = JsonConvert.DeserializeObject>(json) ?? []; + } + else + { + // Fichier vide, initialiser la liste sans désérialiser + ListSaveModel = []; + } } - else - { - // Fichier vide, initialiser la liste sans désérialiser - ListSaveModel = []; - } - } else { // Créer le fichier s'il n'existe pas diff --git a/App.Core/Models/ConfigModel.cs b/App.Core/Models/ConfigModel.cs deleted file mode 100644 index a1c8cd2..0000000 --- a/App.Core/Models/ConfigModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using App.Core.Services; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Configuration; - -namespace App.Core.Models -{ - public class ConfigModel - { - - } -} diff --git a/App.Core/Models/CopyModel.cs b/App.Core/Models/CopyModel.cs index f4d3307..7925634 100644 --- a/App.Core/Models/CopyModel.cs +++ b/App.Core/Models/CopyModel.cs @@ -1,11 +1,14 @@  +using System.ComponentModel; + namespace App.Core.Models { - public class CopyModel + public class CopyModel : INotifyPropertyChanged { public string SourcePath { get; set; } = string.Empty; // Path of the source file public string TargetPath { get; set; } = string.Empty; // Path of the target file + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/App.Core/Models/LoggerModel.cs b/App.Core/Models/LoggerModel.cs index ca60ee8..2599988 100644 --- a/App.Core/Models/LoggerModel.cs +++ b/App.Core/Models/LoggerModel.cs @@ -1,6 +1,8 @@ -namespace App.Core.Models +using System.ComponentModel; + +namespace App.Core.Models { - public class LoggerModel + public class LoggerModel : INotifyPropertyChanged { public string Name { get; set; } = string.Empty; // Name of the save public string FileSource { get; set; } = string.Empty; // Path of the source file @@ -9,5 +11,7 @@ public class LoggerModel public string FileTransferTime { get; set; } = string.Empty; // Time of the transfer public string FileEncryptionTime { get; set; } = string.Empty; // Time of the encryption public DateTime Time { get; set; } = DateTime.Now; // Date of the save + + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/App.Core/Models/SaveModel.cs b/App.Core/Models/SaveModel.cs index ec17d61..1409189 100644 --- a/App.Core/Models/SaveModel.cs +++ b/App.Core/Models/SaveModel.cs @@ -1,6 +1,8 @@ -namespace App.Core.Models +using System.ComponentModel; + +namespace App.Core.Models { - public class SaveModel + public class SaveModel : INotifyPropertyChanged { public string InPath { get; set; } = ""; // Path of the source file public string OutPath { get; set; } = ""; // Path of the target file @@ -8,5 +10,7 @@ public class SaveModel public string SaveName { get; set; } = "" ; // Name of the save public string EncryptChoice { get; set; } = ""; public DateTime Date { get; } = DateTime.Now; // Date of the save + + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/App.Core/Models/StateManagerModel.cs b/App.Core/Models/StateManagerModel.cs index d32e3c7..3e1c3c5 100644 --- a/App.Core/Models/StateManagerModel.cs +++ b/App.Core/Models/StateManagerModel.cs @@ -1,7 +1,9 @@ -namespace App.Core.Models +using System.ComponentModel; + +namespace App.Core.Models { - public class StateManagerModel + public class StateManagerModel : INotifyPropertyChanged { public string SaveName { get; set; } = string.Empty; // Name of the save public string SourceFilePath { get; set; } = string.Empty; // Path of the source file @@ -12,5 +14,6 @@ public class StateManagerModel public long NbFilesLeftToDo { get; set; } = 0; // Number of files left to copy public float Progression { get; set; } = 0; // Progression of the save + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/App.Core/Services/CopyService.cs b/App.Core/Services/CopyService.cs index a317158..90d8124 100644 --- a/App.Core/Services/CopyService.cs +++ b/App.Core/Services/CopyService.cs @@ -5,57 +5,51 @@ namespace App.Core.Services { - public class CopyService + public class CopyService : IDisposable { public LoggerService loggerService = new(); public StateManagerService stateManagerService = new(); - private bool isStopped; - private bool isPaused; public bool isEncrypted; - private long totalFile = 0; - private long totalSize = 0; - private float progress =0; + private readonly object locker = new(); + private ManualResetEvent? manualReset = new(false); public CopyModel CopyModel { get; set; } + public event EventHandler? CopyCompleted; + public event EventHandler? CopyPaused; + public event EventHandler? CopyStopped; + + + public CopyService() { this.CopyModel = new(); } - public CopyService(StateManagerService stateManagerService) + + public void ExecuteCopy(SaveModel saveModel, StreamWriter logWriter, StreamWriter stateWriter) { - this.CopyModel = new(); - this.stateManagerService = stateManagerService; + Thread thread = new Thread(() => BackupDirectory(saveModel.InPath, saveModel.OutPath, logWriter, stateWriter)); + thread.Start(); } - public void ExecuteCopy(SaveModel saveModel, StreamWriter logWriter, StreamWriter stateWriter) + public void PauseCopy() { - isPaused = false; - isStopped = false; - totalFile = 0; - totalSize = 0; - BackupDirectory(saveModel.InPath, saveModel.OutPath, logWriter, stateWriter); + CopyPaused?.Invoke(this, EventArgs.Empty); + manualReset?.Reset(); } - public void PauseCopy(SaveModel saveModel) + public void ResumeCopy() { - isPaused = true; - // Implement any necessary logic for pausing the copying process + CopyCompleted?.Invoke(this, EventArgs.Empty); + manualReset?.Set(); } - public void StopCopy(SaveModel saveModel) + public void StopCopy() { - isStopped = true; - // Implement any necessary logic for stopping the copying process + CopyStopped?.Invoke(this, EventArgs.Empty); } - //public void ResumeCopy(SaveModel saveModel) - //{ - // isPaused = false; - // // Implement any necessary logic for resuming the copying process - // BackupDirectory(saveModel); - //} private void EncryptFile(string sourcePath) { @@ -69,254 +63,131 @@ private void EncryptFile(string sourcePath) Process.Start(startInfo); } - - - void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter logWritter, StreamWriter stateWritter) + void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter logWriter, StreamWriter stateWriter) { - int TotalFailed = 0; + while (!manualReset!.WaitOne(0)) + { // Wait until manualReset is set - // Stage 1: Create the destination folder + int TotalFailed = 0; + // Stage 1: Create the destination folder + if (!Directory.Exists(targetDirPath)) + { + try + { + Directory.CreateDirectory(targetDirPath); + logWriter.WriteLine($"Create folder: {targetDirPath}"); + } + catch (Exception ex) + { + // Cannot create folder + stateWriter.WriteLine($"Failed to create folder: {targetDirPath}\r\nAccess Denied\r\n"); + return; + } + } - if (!Directory.Exists(targetDirPath)) - { + // Stage 2: Get all files from the source folder + string[] files = null; try { - Directory.CreateDirectory(targetDirPath); - logWritter.WriteLine($"Create folder: {targetDirPath}"); + files = Directory.GetFiles(sourceDirPath); } - catch (Exception ex) + catch (UnauthorizedAccessException) { - // Cannot create folder - stateWritter.WriteLine($"Failed to create folder: {targetDirPath}\r\nAccess Denied\r\n"); - return; + // Access denied, cannot read folder or files + stateWriter.WriteLine($"{sourceDirPath}\r\nAccess Denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWriter.WriteLine($"{sourceDirPath}\r\n{e.Message}\r\n"); } - } - - // Stage 2: Get all files from the source folder - - string[] files = null; - - try - { - files = Directory.GetFiles(sourceDirPath); - } - catch (UnauthorizedAccessException) - { - // Access denied, cannot read folder or files - stateWritter.WriteLine($"{sourceDirPath}\r\nAccess Denied\r\n"); - } - catch (Exception e) - { - // Other unknown read errors - stateWritter.WriteLine($"{sourceDirPath}\r\n{e.Message}\r\n"); - } - // Stage 3: Copy all files from source to destination folder - if (files != null && files.Length > 0) - { - foreach (string file in files) + // Stage 3: Copy all files from source to destination folder + if (files != null && files.Length > 0) { - try + foreach (string file in files) { - string name = Path.GetFileName(file); - string dest = Path.Combine(targetDirPath, name); - File.Copy(file, dest, true); - } - catch (UnauthorizedAccessException) - { - // Access denied, cannot write file - stateWritter.WriteLine($"{file}\r\nAccess Denied\r\n"); - TotalFailed++; - } - catch (Exception e) - { - // Other unknown error - TotalFailed++; - stateWritter.WriteLine($"{file}\r\n{e.Message}\r\n"); + try + { + string name = Path.GetFileName(file); + string dest = Path.Combine(targetDirPath, name); + + File.Copy(file, dest, true); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot write file + stateWriter.WriteLine($"{file}\r\nAccess Denied\r\n"); + TotalFailed++; + } + catch (Exception e) + { + // Other unknown error + TotalFailed++; + stateWriter.WriteLine($"{file}\r\n{e.Message}\r\n"); + } } } - } - - // Stage 4: Get all sub-folders - string[] folders = null; - - try - { - folders = Directory.GetDirectories(sourceDirPath); - } - catch (UnauthorizedAccessException) - { - // Access denied, cannot read folders - stateWritter.WriteLine($"{sourceDirPath}\r\nAccess denied\r\n"); - } - catch (Exception e) - { - // Other unknown read errors - stateWritter.WriteLine($"{sourceDirPath}\r\nAccess {e.Message}\r\n"); - } + // Stage 4: Get all sub-folders + string[] folders = null; + try + { + folders = Directory.GetDirectories(sourceDirPath); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot read folders + stateWriter.WriteLine($"{sourceDirPath}\r\nAccess denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWriter.WriteLine($"{sourceDirPath}\r\nAccess {e.Message}\r\n"); + } - // Stage 5: Backup files and "sub-sub-folders" in the sub-folder - if (folders != null && folders.Length > 0) - { - foreach (string folder in folders) + // Stage 5: Backup files and "sub-sub-folders" in the sub-folder + if (folders != null && folders.Length > 0) { - try + foreach (string folder in folders) { - string name = Path.GetFileName(folder); - string dest = Path.Combine(targetDirPath, name); - // recursive call with updated source and target paths - BackupDirectory(folder, dest, logWritter, stateWritter); - } - catch (UnauthorizedAccessException) - { - // Access denied, cannot read folders - stateWritter.WriteLine($"{folder}\r\nAccess denied\r\n"); - } - catch (Exception e) - { - // Other unknown read errors - stateWritter.WriteLine($"{targetDirPath}\r\nAccess {e.Message}\r\n"); + try + { + string name = Path.GetFileName(folder); + string dest = Path.Combine(targetDirPath, name); + + // recursive call with updated source and target paths + BackupDirectory(folder, dest, logWriter, stateWriter); + } + catch (UnauthorizedAccessException) + { + // Access denied, cannot read folders + stateWriter.WriteLine($"{folder}\r\nAccess denied\r\n"); + } + catch (Exception e) + { + // Other unknown read errors + stateWriter.WriteLine($"{targetDirPath}\r\nAccess {e.Message}\r\n"); + } } } + + // Perform encryption if required + if (this.isEncrypted) + { + EncryptFile(this.CopyModel.TargetPath); + } } - if (this.isEncrypted) EncryptFile(this.CopyModel.TargetPath); } - - - //private void ProcessCopy(SaveModel saveModel) - //{ - // if (isPaused || isStopped) - // return; - - // foreach (var item in Directory.GetFiles(this.CopyModel.SourcePath, "*", SearchOption.AllDirectories)) - // { - // totalFile += 1; - // totalSize += new FileInfo(item).Length; - // } - - // foreach (StateManagerModel stateModel in stateManagerService.listStateModel!) - // { - // if (stateModel.SaveName == saveModel.SaveName) - // { - - // stateModel.TotalFilesToCopy += totalFile; - // stateModel.NbFilesLeftToDo = totalFile; - // stateModel.TotalFilesSize = totalSize; - // stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); - // progress = stateModel.Progression; - // stateModel.State = "ACTIVE"; - // stateManagerService.UpdateStateFile(); - - // if (File.Exists(this.CopyModel.SourcePath)) - // { - // File copying operation - - // stateModel.SourceFilePath = this.CopyModel.SourcePath; - // stateModel.TargetFilePath = this.CopyModel.TargetPath; - // stateManagerService.UpdateStateFile(); - - - - // File.Copy(this.CopyModel.SourcePath, this.CopyModel.TargetPath, true); - // stateModel.Progression = 100 - ((stateModel.NbFilesLeftToDo / totalFile) * 100); - // progress = stateModel.Progression; - // stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; - - // stateManagerService.UpdateStateFile(); - - - // loggerService!.loggerModel!.Name = saveModel.SaveName; - // loggerService.loggerModel.FileSource = this.CopyModel.SourcePath; - // loggerService.loggerModel.FileTarget = this.CopyModel.TargetPath; - // loggerService.loggerModel.FileSize = new FileInfo(this.CopyModel.SourcePath).Length.ToString(); - // loggerService.AddEntryLog(); - - // } - // else if (Directory.Exists(this.CopyModel.SourcePath)) - // { - // Directory copying operation - // CopyDirectory(this.CopyModel.SourcePath, this.CopyModel.TargetPath, stateModel); - - // } - // else - // { - // Log the error and handle it gracefully - // loggerService!.loggerModel!.Name = saveModel.SaveName; - // loggerService.AddEntryLog(); - // } - - - - // stateModel.State = "END"; - // stateManagerService.UpdateStateFile(); - // } - // } - - - // if (isEncrypted) EncryptFile(this.CopyModel.TargetPath); - - - //} - - //private void CopyDirectory(string sourceDirPath, string targetDirPath, StateManagerModel stateModel) - //{ - - - // Vérifie si le répertoire source existe - // if (!Directory.Exists(sourceDirPath)) - // { - // Console.WriteLine("Le répertoire source n'existe pas."); - // return; - // } - - // Crée le répertoire cible s'il n'existe pas encore - // if (!Directory.Exists(targetDirPath)) - // { - // Directory.CreateDirectory(targetDirPath); - // } - - // Obtient la liste des fichiers et des sous-répertoires dans le répertoire source - // string[] files = Directory.GetFiles(sourceDirPath); - // string[] subdirectories = Directory.GetDirectories(sourceDirPath); - - - // Copie les fichiers - // foreach (string filePath in files) - // { - // string fileName = Path.GetFileName(filePath); - // string targetFilePath = Path.Combine(targetDirPath, fileName); - // stateModel.SourceFilePath = filePath; - // stateModel.TargetFilePath = targetFilePath; - - // stateManagerService.UpdateStateFile(); - - // DateTime start = DateTime.Now; - // File.Copy(filePath, targetFilePath, true); // Le paramètre true permet d'écraser le fichier s'il existe déjà dans le répertoire cible - // stateModel.NbFilesLeftToDo = stateModel.NbFilesLeftToDo > 0 ? stateModel.NbFilesLeftToDo - 1 : 0; - // start = DateTime.Now; - - // loggerService!.loggerModel!.FileEncryptionTime = (DateTime.Now - start).ToString(); - - // stateManagerService.UpdateStateFile(); - // loggerService!.loggerModel!.Name = stateModel.SaveName; - // loggerService!.loggerModel!.FileSource = filePath; - // loggerService!.loggerModel.FileTarget = targetFilePath; - // loggerService!.loggerModel.FileSize = new FileInfo(filePath).Length.ToString(); - // loggerService!.loggerModel.FileTransferTime = (DateTime.Now - start).ToString(); - // loggerService.AddEntryLog(); - // } - - // Copie les sous - répertoires récursivement - // foreach (string subdirectory in subdirectories) - // { - // string subdirectoryName = Path.GetFileName(subdirectory); - // string targetSubdirectoryPath = Path.Combine(targetDirPath, subdirectoryName); - // CopyDirectory(subdirectory, targetSubdirectoryPath, stateModel); // Appel récursif pour copier les sous-répertoires - // } - //} + public void Dispose() + { + manualReset!.Dispose(); + } } + } + + diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 13fbd3a..243fe9c 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -50,12 +50,23 @@ public bool IsSoftwareRunning() public void ExecuteSave(SaveModel saveModel) { - copyService.CopyModel.SourcePath = saveModel.InPath; copyService.CopyModel.TargetPath = saveModel.OutPath; copyService.stateManagerService.listStateModel = ListStateManager; - copyService.ExecuteCopy(saveModel, logWriter!, stateWriter!); + // Start copying process in a separate thread + Thread thread = new Thread(() => copyService.ExecuteCopy(saveModel, logWriter!, stateWriter!)); + thread.Start(); + } + + public void PauseSave() + { + copyService.PauseCopy(); + } + + public void StopSave() + { + copyService.StopCopy(); } From 76a4da9adeead8e2bd62980a0b17f0807bf88feb Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Sun, 25 Feb 2024 18:16:58 +0100 Subject: [PATCH 4/8] =?UTF-8?q?#104=20Ajouts=20des=20sauvegardes=20parall?= =?UTF-8?q?=C3=A8les=20-=20[]=20manque=20les=20logs=20-=20[]=20manque=20le?= =?UTF-8?q?s=20states?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.Cmd/ViewModels/SaveViewModel.cs | 62 +++++------------------------ App.Core/Services/CopyService.cs | 49 +++++++++++++++++++---- App.Core/Services/SaveService.cs | 50 ++++++++++++++++++----- 3 files changed, 92 insertions(+), 69 deletions(-) diff --git a/App.Cmd/ViewModels/SaveViewModel.cs b/App.Cmd/ViewModels/SaveViewModel.cs index 7f2cbb7..41154d6 100644 --- a/App.Cmd/ViewModels/SaveViewModel.cs +++ b/App.Cmd/ViewModels/SaveViewModel.cs @@ -1,6 +1,7 @@ using App.Core.Models; using App.Core.Services; using Newtonsoft.Json; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Text.RegularExpressions; @@ -254,86 +255,45 @@ public bool UserChoice(int UserEntry) { if (!saveService!.IsSoftwareRunning()) { - + List list = new List(); string[] parts = UserChoice.Split(';'); int z1 = int.Parse(parts[0]); int z2 = int.Parse(parts[1]); - - new Thread(() => - { - saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; - DisplayService.SetForegroundColor("Green", $"Save {z1} is running"); - saveService!.ExecuteSave(ListSaveModel[z1]); - DisplayService.SetForegroundColor("Green", $"Save {z1} Executed"); - saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; - - }).Start(); - - - - - - new Thread(() => - { - saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; - DisplayService.SetForegroundColor("Green", $"Save {z2} is running"); - saveService!.ExecuteSave(ListSaveModel[z2]); - DisplayService.SetForegroundColor("Green", $"Save {z2} Executed"); - saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; - }).Start(); - - - - - + Console.WriteLine(); + list.Add(ListSaveModel[z1]); + list.Add(ListSaveModel[z2]); + saveService!.ExecuteSave(list); } else { DisplayService.SetBackForeColor("Black", "DarkRed", "Software is running save cannot be executed..."); System.Threading.Thread.Sleep(1500); } - } else { //TODO : Ajouter un try catch pour la gestion des entrées de l'utilisateur if (!saveService!.IsSoftwareRunning()) { + List list = new List(); string[] parts = UserChoice.Split('-'); int min = int.Parse(parts[0]); int max = int.Parse(parts[1]); for (int x = min; x <= max; x++) { - new Thread(() => - { - saveService!.copyService!.CopyPaused += SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped += SaveViewModel_CopyStopped; - DisplayService.SetForegroundColor("Green", $"Save {x} is running"); - saveService!.ExecuteSave(ListSaveModel[x]); - DisplayService.SetForegroundColor("Green", $"Save {x} Executed"); - saveService!.copyService!.CopyPaused -= SaveViewModel_CopyPaused; - saveService!.copyService!.CopyStopped -= SaveViewModel_CopyStopped; - }).Start(); - - + list.Add(ListSaveModel[x]); } - + saveService!.ExecuteSave(list); } else { DisplayService.SetBackForeColor("Black", "DarkRed", "Software is running save cannot be executed..."); System.Threading.Thread.Sleep(1500); } - - } - //return true; - } + } + catch (ArgumentOutOfRangeException) { //TODO : Quand save est vide diff --git a/App.Core/Services/CopyService.cs b/App.Core/Services/CopyService.cs index 90d8124..3c0635c 100644 --- a/App.Core/Services/CopyService.cs +++ b/App.Core/Services/CopyService.cs @@ -15,9 +15,8 @@ public class CopyService : IDisposable private ManualResetEvent? manualReset = new(false); public CopyModel CopyModel { get; set; } - public event EventHandler? CopyCompleted; - public event EventHandler? CopyPaused; - public event EventHandler? CopyStopped; + public event EventHandler? BackupStarted; + public event EventHandler? BackupCompleted; @@ -29,25 +28,29 @@ public CopyService() public void ExecuteCopy(SaveModel saveModel, StreamWriter logWriter, StreamWriter stateWriter) { - Thread thread = new Thread(() => BackupDirectory(saveModel.InPath, saveModel.OutPath, logWriter, stateWriter)); + + Thread thread = new Thread(() => + { + OnBackupStarted(saveModel); + BackupDirectory(saveModel.InPath, saveModel.OutPath, logWriter, stateWriter); + OnBackupCompleted(saveModel); + }); thread.Start(); + } public void PauseCopy() { - CopyPaused?.Invoke(this, EventArgs.Empty); manualReset?.Reset(); } public void ResumeCopy() { - CopyCompleted?.Invoke(this, EventArgs.Empty); manualReset?.Set(); } public void StopCopy() { - CopyStopped?.Invoke(this, EventArgs.Empty); } @@ -179,6 +182,8 @@ void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter lo { EncryptFile(this.CopyModel.TargetPath); } + + } } @@ -186,8 +191,38 @@ public void Dispose() { manualReset!.Dispose(); } + + protected void OnBackupStarted(SaveModel save) + { + BackupStarted?.Invoke(this, new BackupEventArgs + { + SourcePath = save.InPath, + TargetPath = save.OutPath, + StartTime = DateTime.Now + }); + } + + protected void OnBackupCompleted(SaveModel save) + { + BackupCompleted?.Invoke(this, new BackupEventArgs + { + SourcePath = save.InPath, + TargetPath = save.OutPath, + EndTime = DateTime.Now + }); + } } + + } +public class BackupEventArgs : EventArgs +{ + public string SourcePath { get; set; } + public string TargetPath { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } +} + diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 243fe9c..4b8773f 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -14,6 +14,8 @@ public class SaveService private readonly StreamWriter? logWriter ; private readonly StreamWriter? stateWriter; + + private readonly ConfigService configService = new(); private readonly LoggerService loggerService = new(); @@ -48,15 +50,45 @@ public bool IsSoftwareRunning() } - public void ExecuteSave(SaveModel saveModel) + public void ExecuteSave(List saveModel) { - copyService.CopyModel.SourcePath = saveModel.InPath; - copyService.CopyModel.TargetPath = saveModel.OutPath; - copyService.stateManagerService.listStateModel = ListStateManager; + foreach (SaveModel save in saveModel) + { + copyService = new(); + copyService.CopyModel.SourcePath = save.InPath; + copyService.CopyModel.TargetPath = save.OutPath; + copyService.stateManagerService.listStateModel = ListStateManager; + + // Start copying process in a separate thread + + + copyService.BackupStarted += (sender, args) => + { + Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); + }; + + copyService.BackupCompleted += (sender, args) => + { + Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); + }; + Thread thread = new Thread(() => + { + copyService.ExecuteCopy(save, logWriter!, stateWriter!); + }); + thread.Start(); - // Start copying process in a separate thread - Thread thread = new Thread(() => copyService.ExecuteCopy(saveModel, logWriter!, stateWriter!)); - thread.Start(); + copyService.BackupStarted -= (sender, args) => + { + Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); + }; + + copyService.BackupCompleted -= (sender, args) => + { + Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); + }; + + + } } public void PauseSave() @@ -127,9 +159,5 @@ public void DeleteSave(SaveModel saveModel) { ListSaveModel.Remove(saveModel); } - - - - } } From 7c3c23e7dd2567a33fc821e0299abc54f066076f Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Mon, 26 Feb 2024 14:53:03 +0100 Subject: [PATCH 5/8] #104 Play create delete work in parallel Missing : Pause restart show is running --- .DS_Store | Bin 0 -> 6148 bytes ._.DS_Store | Bin 0 -> 4096 bytes App.Cmd/ViewModels/SaveViewModel.cs | 7 +- App.Core/Services/CopyService.cs | 249 +++++++++++++++++- App.Core/Services/SaveService.cs | 304 +++++++++++++++++++--- App.Core/Services/StateManagerService.cs | 33 ++- App.Core/Services/ThreadManagerService.cs | 41 +++ WpfApp/MainWindow.xaml | 78 ++++-- WpfApp/MainWindow.xaml.cs | 144 +++++++++- WpfApp/ViewModels/ListViewModel.cs | 194 +++++++------- WpfApp/ViewModels/MainViewModel.cs | 71 +++++ WpfApp/Views/ControlView.xaml | 16 -- WpfApp/Views/ControlView.xaml.cs | 28 -- WpfApp/Views/ListView.xaml | 25 -- WpfApp/Views/ListView.xaml.cs | 28 -- WpfApp/Views/MenuView.xaml | 52 ---- WpfApp/Views/MenuView.xaml.cs | 28 -- WpfApp/WpfApp.csproj | 4 + WpfApp/WpfApp.csproj.user | 20 -- 19 files changed, 936 insertions(+), 386 deletions(-) create mode 100644 .DS_Store create mode 100644 ._.DS_Store create mode 100644 App.Core/Services/ThreadManagerService.cs create mode 100644 WpfApp/ViewModels/MainViewModel.cs delete mode 100644 WpfApp/Views/ControlView.xaml delete mode 100644 WpfApp/Views/ControlView.xaml.cs delete mode 100644 WpfApp/Views/ListView.xaml delete mode 100644 WpfApp/Views/ListView.xaml.cs delete mode 100644 WpfApp/Views/MenuView.xaml delete mode 100644 WpfApp/Views/MenuView.xaml.cs diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..986a9fe2c83ae28f37330103b7ed0b57f6e7205b GIT binary patch literal 6148 zcmeHKI|@QE5ZqM}!N$@uSMUZ=^aK{RqGBZ|i2bWPmq&BcIWM3b37he*{=h}?aK6>#E}G4U=FOTNiu&z1zj(T64dh4#sKB!V-B^yS{x9Hf z`u}GVS5$xs{FMUQn=j`xJSl5y>v2|V3w#Z?oEzK>bEjbNat!oxjD?lsu_r}du{ri@ VViV|e#GMZ0&w%MdqXOSn;0}IZ6=whd literal 0 HcmV?d00001 diff --git a/._.DS_Store b/._.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4943dd333fe502e9a3c94cf3d8a6d4ec96e8cb17 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIhYCu0iY;W;207T z)YDln1JS`yfmP9{;AjYphQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2v9i$7=h-2 zFc`>%WMmdA6r~pDmlh?bDx~EXWh-Rnm89mCWaj53=Hyf=w1 QnnLwI+$%B+a{vDW078f!h5!Hn literal 0 HcmV?d00001 diff --git a/App.Cmd/ViewModels/SaveViewModel.cs b/App.Cmd/ViewModels/SaveViewModel.cs index 41154d6..1e4d4ee 100644 --- a/App.Cmd/ViewModels/SaveViewModel.cs +++ b/App.Cmd/ViewModels/SaveViewModel.cs @@ -16,6 +16,7 @@ public partial class SaveViewModel private readonly LoggerService? loggerService; private readonly CopyService? copyService; private readonly DisplayService displayService; + private ThreadManagerService threadManagerService = new(); [GeneratedRegex(@"^\d+;\d+$")] private static partial Regex MyRegex(); @@ -262,7 +263,7 @@ public bool UserChoice(int UserEntry) Console.WriteLine(); list.Add(ListSaveModel[z1]); list.Add(ListSaveModel[z2]); - saveService!.ExecuteSave(list); + saveService!.ExecuteSave(list, threadManagerService); } else { @@ -284,7 +285,7 @@ public bool UserChoice(int UserEntry) { list.Add(ListSaveModel[x]); } - saveService!.ExecuteSave(list); + saveService!.ExecuteSave(list, threadManagerService); } else { @@ -393,7 +394,7 @@ public void ShowSavesSchedule() foreach (SaveModel item in ListSaveModel) { - Console.WriteLine($"{i} "+saveService!.ShowInfo(item)); + Console.WriteLine($"{i} "+saveService!.ShowInfo(item) + threadManagerService.AreThreadsRunning(i)); i++; } diff --git a/App.Core/Services/CopyService.cs b/App.Core/Services/CopyService.cs index 3c0635c..d9a4fa3 100644 --- a/App.Core/Services/CopyService.cs +++ b/App.Core/Services/CopyService.cs @@ -1,18 +1,18 @@ using App.Core.Models; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Threading; namespace App.Core.Services { - public class CopyService : IDisposable + public class CopyService { public LoggerService loggerService = new(); public StateManagerService stateManagerService = new(); public bool isEncrypted; private readonly object locker = new(); - private ManualResetEvent? manualReset = new(false); public CopyModel CopyModel { get; set; } public event EventHandler? BackupStarted; @@ -41,12 +41,11 @@ public void ExecuteCopy(SaveModel saveModel, StreamWriter logWriter, StreamWrite public void PauseCopy() { - manualReset?.Reset(); } public void ResumeCopy() { - manualReset?.Set(); + } public void StopCopy() @@ -68,7 +67,7 @@ private void EncryptFile(string sourcePath) void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter logWriter, StreamWriter stateWriter) { - while (!manualReset!.WaitOne(0)) + while (true) { // Wait until manualReset is set int TotalFailed = 0; @@ -187,10 +186,242 @@ void BackupDirectory(string sourceDirPath, string targetDirPath, StreamWriter lo } } - public void Dispose() - { - manualReset!.Dispose(); - } + + + //public void CompleteSave(string inputpathsave, string inputDestToSave, bool copyDir, bool verif) //Function for full folder backup + //{ + // StateManagerModel stateModel = new StateManagerModel(); + // stateModel.State = "ACTIVE"; + // Stopwatch stopwatch = new Stopwatch(); + // Stopwatch cryptwatch = new Stopwatch(); + // stopwatch.Start(); //Starting the timed for the log file + + // DirectoryInfo dir = new DirectoryInfo(inputpathsave); // Get the subdirectories for the specified directory. + + // if (!dir.Exists) //Check if the file is present + // { + // throw new DirectoryNotFoundException("ERROR 404 : Directory Not Found ! " + inputpathsave); + // } + + // DirectoryInfo[] dirs = dir.GetDirectories(); + // Directory.CreateDirectory(inputDestToSave); // If the destination directory doesn't exist, create it. + + // FileInfo[] files = dir.GetFiles(); // Get the files in the directory and copy them to the new location. + + // if (!verif) // Check for the status file if it needs to reset the variables + // { + // TotalSize = 0; + // nbfilesmax = 0; + // size = 0; + // nbfiles = 0; + // progs = 0; + + // foreach (FileInfo file in files) // Loop to allow calculation of files and folder size + // { + // TotalSize += file.Length; + // nbfilesmax++; + // } + // foreach (DirectoryInfo subdir in dirs) // Loop to allow calculation of subfiles and subfolder size + // { + // FileInfo[] Maxfiles = subdir.GetFiles(); + // foreach (FileInfo file in Maxfiles) + // { + // TotalSize += file.Length; + // nbfilesmax++; + // } + // } + + // } + + // Loop that allows to copy the files to make the backup + // foreach (FileInfo file in files) + // { + // if (this.button_pause == true) + // { + // MessageBox.Show("test"); + // } + // if (this.button_stop == true) + // { + // Thread.ResetAbort(); + // } + + // string tempPath = Path.Combine(inputDestToSave, file.Name); + + // if (size > 0) + // { + // progs = ((float)size / TotalSize) * 100; + // } + + // Systems which allows to insert the values ​​of each file in the report file. + // DataState.SourceFile = Path.Combine(inputpathsave, file.Name); + // DataState.TargetFile = tempPath; + // DataState.TotalSize = nbfilesmax; + // DataState.TotalFile = TotalSize; + // DataState.TotalSizeRest = TotalSize - size; + // DataState.FileRest = nbfilesmax - nbfiles; + // DataState.Progress = progs; + // UpdateStatefile(); //Call of the function to start the state file system + + // if (PriorityExt(Path.GetExtension(file.Name))) + // { + // if (CryptExt(Path.GetExtension(file.Name))) + // { + // cryptwatch.Start(); + // Encrypt(DataState.SourceFile, tempPath); + // cryptwatch.Stop(); + // } + // else + // { + // file.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + // } + + // } + // else + // { + // if (CryptExt(Path.GetExtension(file.Name))) + // { + // cryptwatch.Start(); + // Encrypt(DataState.SourceFile, tempPath); + // cryptwatch.Stop(); + // } + // else + // { + // file.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + // } + // } + + // nbfiles++; + // size += file.Length; + + // } + + // If copying subdirectories, copy them and their contents to new location. + // if (copyDir) + // { + // foreach (DirectoryInfo subdir in dirs) + // { + // string tempPath = Path.Combine(inputDestToSave, subdir.Name); + // CompleteSave(subdir.FullName, tempPath, copyDir, true); + // } + // } + // System which allows the values ​​to be reset to 0 at the end of the backup + // DataState.TotalSize = TotalSize; + // DataState.SourceFile = null; + // DataState.TargetFile = null; + // DataState.TotalFile = 0; + // DataState.TotalSize = 0; + // DataState.TotalSizeRest = 0; + // DataState.FileRest = 0; + // DataState.Progress = 0; + // DataState.SaveState = false; + + // UpdateStatefile(); //Call of the function to start the state file system + + // stopwatch.Stop(); //Stop the stopwatch + // cryptwatch.Stop(); + // this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable + // this.CryptTransfert = cryptwatch.Elapsed; + //} + + //public void DifferentialSave(string pathA, string pathB, string pathC) // Function that allows you to make a differential backup + //{ + // DataState = new DataState(NameStateFile); //Instattation of the method + // Stopwatch stopwatch = new Stopwatch(); // Instattation of the method + // Stopwatch cryptwatch = new Stopwatch(); + // stopwatch.Start(); //Starting the stopwatch + + // DataState.SaveState = true; + // TotalSize = 0; + // nbfilesmax = 0; + + // System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA); + // System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB); + + // Take a snapshot of the file system. + // IEnumerable list1 = dir1.GetFiles("*.*", System.IO.SearchOption.AllDirectories); + // IEnumerable list2 = dir2.GetFiles("*.*", System.IO.SearchOption.AllDirectories); + + // A custom file comparer defined below + // FileCompare myFileCompare = new FileCompare(); + + // var queryList1Only = (from file in list1 select file).Except(list2, myFileCompare); + // size = 0; + // nbfiles = 0; + // progs = 0; + + // foreach (var v in queryList1Only) + // { + // TotalSize += v.Length; + // nbfilesmax++; + + // } + + // Loop that allows the backup of different files + // foreach (var v in queryList1Only) + // { + // string tempPath = Path.Combine(pathC, v.Name); + // Systems which allows to insert the values ​​of each file in the report file. + // DataState.SourceFile = Path.Combine(pathA, v.Name); + // DataState.TargetFile = tempPath; + // DataState.TotalSize = nbfilesmax; + // DataState.TotalFile = TotalSize; + // DataState.TotalSizeRest = TotalSize - size; + // DataState.FileRest = nbfilesmax - nbfiles; + // DataState.Progress = progs; + + // UpdateStatefile();//Call of the function to start the state file system + + // if (PriorityExt(Path.GetExtension(v.Name))) + // { + // if (CryptExt(Path.GetExtension(v.Name))) + // { + // cryptwatch.Start(); + // Encrypt(DataState.SourceFile, tempPath); + // cryptwatch.Stop(); + // } + // else + // { + // v.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + // } + // } + // else + // { + // if (CryptExt(Path.GetExtension(v.Name))) + // { + // cryptwatch.Start(); + // Encrypt(DataState.SourceFile, tempPath); + // cryptwatch.Stop(); + // } + // else + // { + // v.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + // } + // } + + // size += v.Length; + // nbfiles++; + // } + + // System which allows the values ​​to be reset to 0 at the end of the backup + // DataState.SourceFile = null; + // DataState.TargetFile = null; + // DataState.TotalFile = 0; + // DataState.TotalSize = 0; + // DataState.TotalSizeRest = 0; + // DataState.FileRest = 0; + // DataState.Progress = 0; + // DataState.SaveState = false; + // UpdateStatefile();//Call of the function to start the state file system + + // stopwatch.Stop(); //Stop the stopwatch + // this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable + // this.CryptTransfert = cryptwatch.Elapsed; + //} + + + + + protected void OnBackupStarted(SaveModel save) { diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 4b8773f..896fc49 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -2,37 +2,37 @@ using System.Text.Json; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Drawing; +using System.Security.Policy; namespace App.Core.Services { - public class SaveService + public class SaveService : IDisposable { - public required ObservableCollection ListStateManager { get; set; } = []; - public required ObservableCollection ListSaveModel { get; set; } = []; + public ObservableCollection ListStateManager { get; set; } = []; + public ObservableCollection ListSaveModel { get; set; } = []; private readonly StreamWriter? logWriter ; private readonly StreamWriter? stateWriter; - - - private readonly ConfigService configService = new(); + private readonly StateManagerService stateManagerService = new(); private readonly LoggerService loggerService = new(); public CopyService copyService = new(); + private ManualResetEvent resetEvent = new ManualResetEvent(false); private readonly JsonSerializerOptions options = new() { WriteIndented = true }; - public SaveService() { - logWriter = new StreamWriter(loggerService.GetLogFormat()); - stateWriter = new StreamWriter(StateManagerService.stateFilePath); + //logWriter = new StreamWriter(loggerService.GetLogFormat()); + //stateWriter = new StreamWriter(StateManagerService.stateFilePath); LoadSave(); copyService.stateManagerService.listStateModel = ListStateManager; - copyService.stateManagerService.UpdateStateFile(stateWriter); + //copyService.stateManagerService.UpdateStateFile(stateWriter); } public bool IsSoftwareRunning() @@ -50,7 +50,7 @@ public bool IsSoftwareRunning() } - public void ExecuteSave(List saveModel) + public void ExecuteSave(List saveModel, ThreadManagerService threadManagerService) { foreach (SaveModel save in saveModel) { @@ -62,30 +62,18 @@ public void ExecuteSave(List saveModel) // Start copying process in a separate thread - copyService.BackupStarted += (sender, args) => - { - Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); - }; + copyService.BackupStarted += (sender, args) =>Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); - copyService.BackupCompleted += (sender, args) => - { - Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); - }; - Thread thread = new Thread(() => - { - copyService.ExecuteCopy(save, logWriter!, stateWriter!); - }); + copyService.BackupCompleted += (sender, args) => Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); + + + Thread thread = new Thread(() => copyService.ExecuteCopy(save, logWriter!, stateWriter!)); + threadManagerService.AddThread(thread); thread.Start(); - copyService.BackupStarted -= (sender, args) => - { - Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); - }; + copyService.BackupStarted -= (sender, args) => Console.WriteLine($"Backup started from {args.SourcePath} to {args.TargetPath} at {args.StartTime}"); - copyService.BackupCompleted -= (sender, args) => - { - Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); - }; + copyService.BackupCompleted -= (sender, args) =>Console.WriteLine($"Backup completed from {args.SourcePath} to {args.TargetPath} at {args.EndTime}"); } @@ -101,9 +89,228 @@ public void StopSave() copyService.StopCopy(); } + public async Task LaunchSave(SaveModel saveModel) + { + //TODO : Detection logicel Métier + + if (saveModel.Type == "Complete") + { + // Run CompleteSave asynchronously + await Task.Run(() => + { + while (CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel)) + { + // Optionally, you can introduce a small delay to avoid tight loops + Thread.Sleep(100); // Adjust as needed + } + }); + + return false; + } + + if (saveModel.Type == "Incremental") + { + IncrementalSave(saveModel.InPath, saveModel.OutPath, true); + return false; + } + + return true; + } + + + + private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel saveModel) + { + //DataState = new DataState(NameStateFile); + //this.DataState.SaveState = true; + StateManagerModel stateModel = new() + { + SaveName = saveModel.SaveName, + State = "END" + }; + + foreach (StateManagerModel item in ListStateManager) + { + if (item.SaveName == stateModel.SaveName) + { + stateModel = item; + } + } + + long TotalSize = 0; + long nbfilesmax = 0; + long size = 0; + long nbfiles = 0; + float progs = 0; + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); //Starting the timed for the log file + + DirectoryInfo dir = new DirectoryInfo(InPath); // Get the subdirectories for the specified directory. + + if (!dir.Exists) //Check if the file is present + { + throw new DirectoryNotFoundException("ERROR 404 : Directory Not Found ! " + InPath); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + Directory.CreateDirectory(OutPath); // If the destination directory doesn't exist, create it. + + FileInfo[] files = dir.GetFiles(); // Get the files in the directory and copy them to the new location. + + if (!verif) // Check for the status file if it needs to reset the variables + { + TotalSize = 0; + nbfilesmax = 0; + size = 0; + nbfiles = 0; + progs = 0; + + foreach (FileInfo file in files) // Loop to allow calculation of files and folder size + { + TotalSize += file.Length; + nbfilesmax++; + } + foreach (DirectoryInfo subdir in dirs) // Loop to allow calculation of subfiles and subfolder size + { + FileInfo[] Maxfiles = subdir.GetFiles(); + foreach (FileInfo file in Maxfiles) + { + TotalSize += file.Length; + nbfilesmax++; + } + } + + } + + //Loop that allows to copy the files to make the backup + foreach (FileInfo file in files) + { + string tempPath = Path.Combine(OutPath, file.Name); + + if (size > 0) + { + progs = ((float)size / TotalSize) * 100; + } + + stateModel.SourceFilePath = Path.Combine(OutPath, file.Name); + stateModel.TargetFilePath = tempPath; + stateModel.TotalFilesToCopy = nbfilesmax; + stateModel.TotalFilesSize = TotalSize; + stateModel.NbFilesLeftToDo = nbfilesmax - nbfiles; + stateModel.Progression = progs; + stateModel.State = "IN PROGRESS"; + + stateManagerService.UpdateStateFile(ListStateManager); + + //Systems which allows to insert the values ​​of each file in the report file. + + file.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + nbfiles++; + size += file.Length; + + } + + // If copying subdirectories, copy them and their contents to new location. + + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(OutPath, subdir.Name); + CompleteSave(subdir.FullName, tempPath, true, saveModel); + } + //System which allows the values ​​to be reset to 0 at the end of the backup + //DataState.TotalSize = TotalSize; + //DataState.SourceFile = null; + //DataState.TargetFile = null; + //DataState.TotalFile = 0; + //DataState.TotalSize = 0; + //DataState.TotalSizeRest = 0; + //DataState.FileRest = 0; + //DataState.Progress = 0; + //DataState.SaveState = false; + + //UpdateStatefile(); //Call of the function to start the state file system + + stopwatch.Stop(); //Stop the stopwatch + return false; + //this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable + } + + //public void DifferentialSave(string pathA, string pathB, string pathC) // Function that allows you to make a differential backup + //{ + // DataState = new DataState(NameStateFile); //Instattation of the method + // Stopwatch stopwatch = new Stopwatch(); // Instattation of the method + // stopwatch.Start(); //Starting the stopwatch + + // DataState.SaveState = true; + // TotalSize = 0; + // nbfilesmax = 0; + + // System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA); + // System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB); + + // // Take a snapshot of the file system. + // IEnumerable list1 = dir1.GetFiles("*.*", System.IO.SearchOption.AllDirectories); + // IEnumerable list2 = dir2.GetFiles("*.*", System.IO.SearchOption.AllDirectories); + + // //A custom file comparer defined below + // FileCompare myFileCompare = new FileCompare(); + + // var queryList1Only = (from file in list1 select file).Except(list2, myFileCompare); + // size = 0; + // nbfiles = 0; + // progs = 0; + + // foreach (var v in queryList1Only) + // { + // TotalSize += v.Length; + // nbfilesmax++; + + // } + + // //Loop that allows the backup of different files + // foreach (var v in queryList1Only) + // { + // string tempPath = Path.Combine(pathC, v.Name); + // //Systems which allows to insert the values ​​of each file in the report file. + // DataState.SourceFile = Path.Combine(pathA, v.Name); + // DataState.TargetFile = tempPath; + // DataState.TotalSize = nbfilesmax; + // DataState.TotalFile = TotalSize; + // DataState.TotalSizeRest = TotalSize - size; + // DataState.FileRest = nbfilesmax - nbfiles; + // DataState.Progress = progs; + + // UpdateStatefile();//Call of the function to start the state file system + // v.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + // size += v.Length; + // nbfiles++; + // } + + // //System which allows the values ​​to be reset to 0 at the end of the backup + // DataState.SourceFile = null; + // DataState.TargetFile = null; + // DataState.TotalFile = 0; + // DataState.TotalSize = 0; + // DataState.TotalSizeRest = 0; + // DataState.FileRest = 0; + // DataState.Progress = 0; + // DataState.SaveState = false; + // UpdateStatefile();//Call of the function to start the state file system + + // stopwatch.Stop(); //Stop the stopwatch + // this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable + //} + + private void IncrementalSave(string InPath, string OutPath, bool verif) + { + + } + - public void LoadSave() + + public ObservableCollection LoadSave() { if (File.Exists("saves.json")) @@ -128,6 +335,8 @@ public void LoadSave() File.WriteAllText("saves.json", "[]"); } + return ListSaveModel; + } public Tuple ShowInfo(SaveModel saveModel) @@ -135,15 +344,12 @@ public Tuple ShowInfo(SaveModel saveMo return Tuple.Create(saveModel.SaveName, saveModel.InPath, saveModel.OutPath, saveModel.Type, saveModel.Date); } - - - - public bool CreateSave(string inPath, string outPath, string type, string saveName) + public bool CreateSave(SaveModel saveModel) { try { - ListSaveModel.Add(new SaveModel { InPath = inPath, OutPath = outPath, Type = type, SaveName = saveName}); - ListStateManager.Add(new StateManagerModel { SaveName = saveName, SourceFilePath = inPath, TargetFilePath = outPath }); + ListSaveModel.Add(saveModel); + ListStateManager.Add(new StateManagerModel { SaveName = saveModel.SaveName, SourceFilePath = saveModel.InPath, TargetFilePath = saveModel.OutPath }); File.WriteAllText("saves.json", JsonSerializer.Serialize(ListSaveModel, options)); return true; } @@ -152,6 +358,22 @@ public bool CreateSave(string inPath, string outPath, string type, string saveNa return false; } } + + + //public bool CreateSave(string inPath, string outPath, string type, string saveName) + //{ + // try + // { + // ListSaveModel.Add(new SaveModel { InPath = inPath, OutPath = outPath, Type = type, SaveName = saveName}); + // ListStateManager.Add(new StateManagerModel { SaveName = saveName, SourceFilePath = inPath, TargetFilePath = outPath }); + // File.WriteAllText("saves.json", JsonSerializer.Serialize(ListSaveModel, options)); + // return true; + // } + // catch + // { + // return false; + // } + //} @@ -159,5 +381,11 @@ public void DeleteSave(SaveModel saveModel) { ListSaveModel.Remove(saveModel); } + + public void Dispose() + { + logWriter?.Dispose(); + stateWriter?.Dispose(); + } } } diff --git a/App.Core/Services/StateManagerService.cs b/App.Core/Services/StateManagerService.cs index 4314f8c..4689d80 100644 --- a/App.Core/Services/StateManagerService.cs +++ b/App.Core/Services/StateManagerService.cs @@ -1,4 +1,5 @@ using App.Core.Models; +using Newtonsoft.Json; using System.Collections.ObjectModel; using System.Text.Json; @@ -6,16 +7,11 @@ namespace App.Core.Services { public class StateManagerService { - + public ObservableCollection? listStateModel; public static readonly string stateFilePath = "state.json"; - - //Options for the JsonSerializer - private readonly JsonSerializerOptions options = new() - { - WriteIndented = true - }; + private static Mutex mut = new(); public StateManagerService() @@ -23,7 +19,7 @@ public StateManagerService() //Create the state file if it does not exist if (!File.Exists(stateFilePath)) { - CreateStateFile(); + //CreateStateFile(); } //UpdateStateFile(); @@ -35,35 +31,35 @@ public void OpenStateFile() System.Diagnostics.Process.Start("notepad.exe", stateFilePath); } - public void UpdateStateFile(StreamWriter streamWriter) + + public void UpdateStateFile(ObservableCollection stateManagerModels) { + //TODO : Voir les states files + mut.WaitOne(); try { + ClearStateFile(); // Open the file for writing (append mode) + StreamWriter? streamWriter = null; using (streamWriter = File.AppendText(stateFilePath)) { - foreach (StateManagerModel stateModel in listStateModel!) + foreach (StateManagerModel stateModel in stateManagerModels!) { // Write the serialized stateModel to the file - streamWriter.WriteLineAsync(JsonSerializer.Serialize(stateModel, options) + ","); + streamWriter.WriteLineAsync(JsonConvert.SerializeObject(stateModel, Formatting.Indented) + Environment.NewLine); } } + streamWriter?.Dispose(); } catch (IOException ex) { // Handle the exception (e.g., log or retry) Console.WriteLine($"Error updating state file: {ex.Message}"); } + mut.ReleaseMutex(); } - public void CreateStateFile() - { - // Create the state file - File.WriteAllText(stateFilePath, "[]"); - - } - public void ClearStateFile() { // Clear the state file @@ -71,5 +67,6 @@ public void ClearStateFile() } + } } diff --git a/App.Core/Services/ThreadManagerService.cs b/App.Core/Services/ThreadManagerService.cs new file mode 100644 index 0000000..1af7141 --- /dev/null +++ b/App.Core/Services/ThreadManagerService.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Threading; + +namespace App.Core.Services +{ + public class ThreadManagerService + { + private List runningThreads = new List(); + + public void AddThread(Thread thread) + { + + runningThreads.Add(thread); + + } + + public void RemoveThread(Thread thread) + { + runningThreads.Remove(thread); + } + + public bool AreThreadsRunning(int threadNumber) + { + if (runningThreads.Count == 0 || threadNumber >= runningThreads.Count) + { + return false; + } + + try + { + Console.WriteLine($"Thread {runningThreads[threadNumber].ThreadState}"); + return runningThreads[threadNumber].ThreadState == ThreadState.Running; + } + catch (System.Exception) + { + return false; + } + } + + } +} diff --git a/WpfApp/MainWindow.xaml b/WpfApp/MainWindow.xaml index 8011f17..901996f 100644 --- a/WpfApp/MainWindow.xaml +++ b/WpfApp/MainWindow.xaml @@ -3,34 +3,80 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:WpfApp.Views" + xmlns:local="clr-namespace:WpfApp.ViewModels" mc:Ignorable="d" Title="EasySave" Height="475" Width="800" MinHeight="475" MinWidth="800"> + + + + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + public partial class MainWindow : Window { + private readonly MainViewModel? viewModel; + public ObservableCollection Saves { get; set; } = new ObservableCollection(); + public MainWindow() { InitializeComponent(); + viewModel = new MainViewModel(); + List_Save.Items.Clear(); + LoadSave(); + List_Save.ItemsSource = this.Saves; + } + + private void LoadSave() + { + this.Saves = viewModel!.saves; + } + + private void addSave_Click(object sender, RoutedEventArgs e) + { + SaveModel save = new SaveModel + { + SaveName = SaveName.Text, + InPath = SourceName.Text, + OutPath = DestinationName.Text, + Type = IsComplete.IsChecked != null && (bool)IsComplete.IsChecked ? "Complete" : "Incremental", + EncryptChoice = IsEncrypted.IsChecked != null && (bool)IsEncrypted.IsChecked ? "True" : "False" + }; + try + { + if (string.IsNullOrEmpty(SaveName.Text) || SourceName.Text == "" || DestinationName.Text == "" || Destination.Text == "Destination" || Source.Text == "Source" ) + { + MessageBox.Show("Please fill in all the fields"); + return; + } + + viewModel!.AddSave(save); + MessageBox.Show("Save added successfully"); + } + catch + { + // TODO : Handle exception + } + } + + private void SourcePopup_Click(object sender, RoutedEventArgs e) + { + OpenFolderDialog dialog = new OpenFolderDialog(); //Declaration of the method to open the window to choose the folder path. + dialog.DefaultDirectory = ""; + if ((bool)dialog.ShowDialog()) + { + SourceName.Text = dialog.FolderName; + } + + + } + + private void DestinationPopup_Click(object sender, RoutedEventArgs e) + { + OpenFolderDialog dialog = new OpenFolderDialog(); //Declaration of the method to open the window to choose the folder path. + dialog.DefaultDirectory = ""; + if ((bool)dialog.ShowDialog()) + { + DestinationName.Text = dialog.FolderName; + } + } + + private void StartSave_Click(object sender, RoutedEventArgs e) + { + if (List_Save.SelectedItem != null) + { + // Start the save operation + viewModel!.StartSave((SaveModel)List_Save.SelectedItem); + + // Change the visual appearance of the selected item to indicate that it's running + var selectedItem = List_Save.SelectedItem as SaveModel; + if (selectedItem != null) + { + // Get the row corresponding to the selected item + var row = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); + if (row != null) + { + // Change the background color of the row to green + row.Background = Brushes.Green; + + // Wait for the save operation to complete + Task.Run(() => + { + Application.Current.Dispatcher.Invoke(() => + { + while (viewModel!.IsSaveRunning((SaveModel)List_Save.SelectedItem)) + { + Thread.Sleep(1000); // Sleep for 1 second before checking again + } + + // Update the row color to red when the save operation is completed + row.Dispatcher.Invoke(() => + { + row.Background = Brushes.Red; + }); + }); + }); + } + } + } + else + { + // Handle case where no item is selected + } + } + + + + + + private void DeleteSave_Click(object sender, RoutedEventArgs e) + { + if (List_Save.SelectedItem != null) + { + // Start the save operation + viewModel!.DeleteSave((SaveModel)List_Save.SelectedItem); + } + } + + private void PlaySave_Click(object sender, RoutedEventArgs e) + { + + } + + private void PauseSave_Click(object sender, RoutedEventArgs e) + { + + } + + private void StopSave_Click(object sender, RoutedEventArgs e) + { + } } } \ No newline at end of file diff --git a/WpfApp/ViewModels/ListViewModel.cs b/WpfApp/ViewModels/ListViewModel.cs index 00d49fa..8791323 100644 --- a/WpfApp/ViewModels/ListViewModel.cs +++ b/WpfApp/ViewModels/ListViewModel.cs @@ -6,113 +6,111 @@ namespace WpfApp.ViewModels { - class ListViewModel : INotifyPropertyChanged + class ListViewModel { - private SaveModel _selected; - public SaveModel Selected + public ObservableCollection Selected { - get => _selected; + get => Selected; set { - if (_selected != value) + if (Selected != value) { - _selected = value; - OnPropertyChanged(); + Selected = value; } } } - public ObservableCollection listStateManager { get; set; } = new ObservableCollection(); - public ObservableCollection listSaveModel{ get; set; } = new ObservableCollection(); - public ObservableCollection Items { get; set; } = new ObservableCollection(); - - - private SaveService? saveService; - - - public event PropertyChangedEventHandler PropertyChanged; - - public RelayCommand RefreshCommand { get; } - public RelayCommand CreateCommand { get; } - public RelayCommand ExecuteCommand { get; } - public RelayCommand DeleteCommand { get; } - - public ListViewModel() - { - saveService = new() - { - ListStateManager = this.listStateManager, - ListSaveModel = this.listSaveModel - }; - - saveService.LoadSave(); - this.listSaveModel = saveService.ListSaveModel; - this.listStateManager = saveService.ListStateManager; - foreach (var item in listSaveModel) - { - Items.Add(item); - } - RefreshCommand = new RelayCommand(Refresh); - CreateCommand = new RelayCommand(Create); - ExecuteCommand = new RelayCommand(Execute); - DeleteCommand = new RelayCommand(Delete); - - } - - public void Execute() - { - if (Selected != null) - { - Task.Run(() => saveService!.ExecuteSave(Selected)); - } - else - { - // Handle case where no item is selected - } - } - - public void Create() - { - // Create a new instance of SaveModel (assuming SaveModel has a default constructor) - var newSaveModel = new SaveModel(); - - // Add the new SaveModel instance to the Items collection - Items.Add(newSaveModel); - - // Optionally, select the newly added item - newSaveModel = Selected; - - saveService!.CreateSave(newSaveModel.InPath, newSaveModel.OutPath, newSaveModel.Type, newSaveModel.SaveName); - Thread.Sleep(500); - Refresh(); - } - - public void Refresh() - { - Items.Clear(); - foreach (var item in listSaveModel) - { - Items.Add(item); - } - } - - public void Delete() - { - if (Selected != null) - { - saveService!.DeleteSave(Selected); - Refresh(); - } - else - { - // Handle case where no item is selected - } - } - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + //public ObservableCollection listStateManager { get; set; } = new ObservableCollection(); + //public ObservableCollection listSaveModel{ get; set; } = new ObservableCollection(); + //public ObservableCollection Items { get; set; } = new ObservableCollection(); + + + //private SaveService? saveService; + + + //public event PropertyChangedEventHandler PropertyChanged; + + //public RelayCommand RefreshCommand { get; } + //public RelayCommand CreateCommand { get; } + //public RelayCommand ExecuteCommand { get; } + //public RelayCommand DeleteCommand { get; } + + //public ListViewModel() + //{ + // saveService = new() + // { + // ListStateManager = this.listStateManager, + // ListSaveModel = this.listSaveModel + // }; + + // saveService.LoadSave(); + // this.listSaveModel = saveService.ListSaveModel; + // this.listStateManager = saveService.ListStateManager; + // foreach (var item in listSaveModel) + // { + // Items.Add(item); + // } + // RefreshCommand = new RelayCommand(Refresh); + // CreateCommand = new RelayCommand(Create); + // ExecuteCommand = new RelayCommand(Execute); + // DeleteCommand = new RelayCommand(Delete); + + //} + + //public void Execute() + //{ + // if (Selected != null) + // { + // Task.Run(() => saveService!.ExecuteSave(Selected)); + // } + // else + // { + // // Handle case where no item is selected + // } + //} + + //public void Create() + //{ + // // Create a new instance of SaveModel (assuming SaveModel has a default constructor) + // var newSaveModel = new ObservableCollection(); + + // // Add the new SaveModel instance to the Items collection + // Items.Add(newSaveModel); + + // // Optionally, select the newly added item + // newSaveModel = Selected; + + // saveService!.CreateSave(newSaveModel.InPath, newSaveModel.OutPath, newSaveModel.Type, newSaveModel.SaveName); + // Thread.Sleep(500); + // Refresh(); + //} + + //public void Refresh() + //{ + // Items.Clear(); + // foreach (var item in listSaveModel) + // { + // Items.Add(item); + // } + //} + + //public void Delete() + //{ + // if (Selected != null) + // { + // saveService!.DeleteSave(Selected); + // Refresh(); + // } + // else + // { + // // Handle case where no item is selected + // } + //} + + //protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + //{ + // PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + //} } } diff --git a/WpfApp/ViewModels/MainViewModel.cs b/WpfApp/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..afaf5be --- /dev/null +++ b/WpfApp/ViewModels/MainViewModel.cs @@ -0,0 +1,71 @@ +using App.Core.Models; +using App.Core.Services; +using System.Collections.ObjectModel; +using System.Linq; + +namespace WpfApp.ViewModels +{ + public class MainViewModel + { + public readonly ObservableCollection saves = new(); + private readonly SaveService saveService = new(); + private readonly HashSet runningSaves = new(); // HashSet to store the IDs of running saves + + + + public MainViewModel() + { + saves = saveService.LoadSave(); + } + + public void AddSave(SaveModel save) + { + saveService.CreateSave(save); + } + + + + public void DeleteSave(SaveModel saveModel) + { + saveService.DeleteSave(saveModel); + } + + public void PlaySave() + { + } + + public void PauseSave() + { + } + + public void StopSave() + { + } + + public bool IsSaveRunning(SaveModel saveModel) + { + lock (runningSaves) + { + return runningSaves.Contains(saveModel); // Check if the saveModel instance is in the runningSaves collection + } + } + + public void StartSave(SaveModel saveModel) + { + lock (runningSaves) + { + runningSaves.Add(saveModel); // Add the saveModel instance to the runningSaves collection + } + + // Start the save operation + saveService.LaunchSave(saveModel); + + // Remove the saveModel instance from the runningSaves collection when the operation is completed + lock (runningSaves) + { + runningSaves.Remove(saveModel); + } + } + + } +} diff --git a/WpfApp/Views/ControlView.xaml b/WpfApp/Views/ControlView.xaml deleted file mode 100644 index 5726ac5..0000000 --- a/WpfApp/Views/ControlView.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/WpfApp/Views/ControlView.xaml.cs b/WpfApp/Views/ControlView.xaml.cs deleted file mode 100644 index 6e8551a..0000000 --- a/WpfApp/Views/ControlView.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace WpfApp.Views -{ - /// - /// Logique d'interaction pour ControlView.xaml - /// - public partial class ControlView : UserControl - { - public ControlView() - { - InitializeComponent(); - } - } -} diff --git a/WpfApp/Views/ListView.xaml b/WpfApp/Views/ListView.xaml deleted file mode 100644 index 26fc407..0000000 --- a/WpfApp/Views/ListView.xaml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WpfApp/Views/MenuView.xaml.cs b/WpfApp/Views/MenuView.xaml.cs deleted file mode 100644 index cce221c..0000000 --- a/WpfApp/Views/MenuView.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace WpfApp.Views -{ - /// - /// Logique d'interaction pour Menu.xaml - /// - public partial class Menu : UserControl - { - public Menu() - { - InitializeComponent(); - } - } -} diff --git a/WpfApp/WpfApp.csproj b/WpfApp/WpfApp.csproj index c6fdd78..7f2af4d 100644 --- a/WpfApp/WpfApp.csproj +++ b/WpfApp/WpfApp.csproj @@ -67,4 +67,8 @@ + + + + diff --git a/WpfApp/WpfApp.csproj.user b/WpfApp/WpfApp.csproj.user index e4c3053..644b0a6 100644 --- a/WpfApp/WpfApp.csproj.user +++ b/WpfApp/WpfApp.csproj.user @@ -6,29 +6,9 @@ Designer - - - Code - - - Code - - - Code - - Designer - - Designer - - - Designer - - - Designer - \ No newline at end of file From a92b7abd4c65c42035ee757a5762d82c5275b8a2 Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Mon, 26 Feb 2024 16:48:02 +0100 Subject: [PATCH 6/8] #104 software ,pause, start work Stop / kill don't work --- App.Core/Services/SaveService.cs | 183 +++++++++-------------------- WpfApp/MainWindow.xaml.cs | 64 +++++----- WpfApp/ViewModels/MainViewModel.cs | 22 ++-- 3 files changed, 97 insertions(+), 172 deletions(-) diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 896fc49..ae40333 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Drawing; using System.Security.Policy; +using System.Threading; namespace App.Core.Services { @@ -12,6 +13,7 @@ public class SaveService : IDisposable public ObservableCollection ListStateManager { get; set; } = []; public ObservableCollection ListSaveModel { get; set; } = []; + public AutoResetEvent pauseEvent { get; private set; } = new AutoResetEvent(false); private readonly StreamWriter? logWriter ; private readonly StreamWriter? stateWriter; @@ -20,12 +22,15 @@ public class SaveService : IDisposable private readonly StateManagerService stateManagerService = new(); private readonly LoggerService loggerService = new(); public CopyService copyService = new(); - private ManualResetEvent resetEvent = new ManualResetEvent(false); private readonly JsonSerializerOptions options = new() { WriteIndented = true }; + private Thread saveThread; + private bool isPaused; + private bool shouldAbort; + public SaveService() { //logWriter = new StreamWriter(loggerService.GetLogFormat()); @@ -38,7 +43,7 @@ public SaveService() public bool IsSoftwareRunning() { // Check if the software is running (e.g., "notepad.exe") - Process[] processes = Process.GetProcessesByName(configService.Software); + Process[] processes = Process.GetProcessesByName("Paint.exe"); if (processes.Length > 0) { return true; @@ -79,42 +84,50 @@ public void ExecuteSave(List saveModel, ThreadManagerService threadMa } } - public void PauseSave() - { - copyService.PauseCopy(); - } - - public void StopSave() - { - copyService.StopCopy(); - } - - public async Task LaunchSave(SaveModel saveModel) + public void LaunchSave(SaveModel saveModel) { //TODO : Detection logicel Métier if (saveModel.Type == "Complete") { - // Run CompleteSave asynchronously - await Task.Run(() => - { - while (CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel)) - { - // Optionally, you can introduce a small delay to avoid tight loops - Thread.Sleep(100); // Adjust as needed - } - }); + + saveThread = new Thread(() => + + CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel) + ); + + // Start the thread + saveThread.Start(); - return false; } if (saveModel.Type == "Incremental") { - IncrementalSave(saveModel.InPath, saveModel.OutPath, true); - return false; + //IncrementalSave(saveModel.InPath, saveModel.OutPath, true); } + } + + public void PauseSave() + { + Thread pauseThread = new Thread(() => + { + isPaused = true; + this.pauseEvent.WaitOne(); // Met en pause le thread jusqu'à ce que le signal soit reçu + }); + + pauseThread.Start(); // Start the thread; // Met en pause le thread jusqu'à ce que le signal soit reçu + } - return true; + public void StopSave() + { + + } + + // Ajoutez une méthode pour reprendre la sauvegarde + public void ResumeSave() + { + isPaused = false; + pauseEvent.Set(); // Signale au thread de reprendre } @@ -123,6 +136,23 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s { //DataState = new DataState(NameStateFile); //this.DataState.SaveState = true; + + while (IsSoftwareRunning()) + { + //wait for the software to close + Thread.Sleep(1000); + //display message box to inform the user that the software is still running + + } + + if (isPaused) + { + // Attendre l'événement de reprise avant de continuer + pauseEvent.WaitOne(); // Attend que l'événement soit déclenché + } + + + StateManagerModel stateModel = new() { SaveName = saveModel.SaveName, @@ -218,94 +248,14 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s string tempPath = Path.Combine(OutPath, subdir.Name); CompleteSave(subdir.FullName, tempPath, true, saveModel); } - //System which allows the values ​​to be reset to 0 at the end of the backup - //DataState.TotalSize = TotalSize; - //DataState.SourceFile = null; - //DataState.TargetFile = null; - //DataState.TotalFile = 0; - //DataState.TotalSize = 0; - //DataState.TotalSizeRest = 0; - //DataState.FileRest = 0; - //DataState.Progress = 0; - //DataState.SaveState = false; - - //UpdateStatefile(); //Call of the function to start the state file system + stopwatch.Stop(); //Stop the stopwatch return false; //this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable } - //public void DifferentialSave(string pathA, string pathB, string pathC) // Function that allows you to make a differential backup - //{ - // DataState = new DataState(NameStateFile); //Instattation of the method - // Stopwatch stopwatch = new Stopwatch(); // Instattation of the method - // stopwatch.Start(); //Starting the stopwatch - - // DataState.SaveState = true; - // TotalSize = 0; - // nbfilesmax = 0; - - // System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA); - // System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB); - - // // Take a snapshot of the file system. - // IEnumerable list1 = dir1.GetFiles("*.*", System.IO.SearchOption.AllDirectories); - // IEnumerable list2 = dir2.GetFiles("*.*", System.IO.SearchOption.AllDirectories); - - // //A custom file comparer defined below - // FileCompare myFileCompare = new FileCompare(); - - // var queryList1Only = (from file in list1 select file).Except(list2, myFileCompare); - // size = 0; - // nbfiles = 0; - // progs = 0; - - // foreach (var v in queryList1Only) - // { - // TotalSize += v.Length; - // nbfilesmax++; - - // } - - // //Loop that allows the backup of different files - // foreach (var v in queryList1Only) - // { - // string tempPath = Path.Combine(pathC, v.Name); - // //Systems which allows to insert the values ​​of each file in the report file. - // DataState.SourceFile = Path.Combine(pathA, v.Name); - // DataState.TargetFile = tempPath; - // DataState.TotalSize = nbfilesmax; - // DataState.TotalFile = TotalSize; - // DataState.TotalSizeRest = TotalSize - size; - // DataState.FileRest = nbfilesmax - nbfiles; - // DataState.Progress = progs; - - // UpdateStatefile();//Call of the function to start the state file system - // v.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. - // size += v.Length; - // nbfiles++; - // } - - // //System which allows the values ​​to be reset to 0 at the end of the backup - // DataState.SourceFile = null; - // DataState.TargetFile = null; - // DataState.TotalFile = 0; - // DataState.TotalSize = 0; - // DataState.TotalSizeRest = 0; - // DataState.FileRest = 0; - // DataState.Progress = 0; - // DataState.SaveState = false; - // UpdateStatefile();//Call of the function to start the state file system - - // stopwatch.Stop(); //Stop the stopwatch - // this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable - //} - - private void IncrementalSave(string InPath, string OutPath, bool verif) - { - - } + @@ -360,23 +310,6 @@ public bool CreateSave(SaveModel saveModel) } - //public bool CreateSave(string inPath, string outPath, string type, string saveName) - //{ - // try - // { - // ListSaveModel.Add(new SaveModel { InPath = inPath, OutPath = outPath, Type = type, SaveName = saveName}); - // ListStateManager.Add(new StateManagerModel { SaveName = saveName, SourceFilePath = inPath, TargetFilePath = outPath }); - // File.WriteAllText("saves.json", JsonSerializer.Serialize(ListSaveModel, options)); - // return true; - // } - // catch - // { - // return false; - // } - //} - - - public void DeleteSave(SaveModel saveModel) { ListSaveModel.Remove(saveModel); diff --git a/WpfApp/MainWindow.xaml.cs b/WpfApp/MainWindow.xaml.cs index cc349a7..2eef2de 100644 --- a/WpfApp/MainWindow.xaml.cs +++ b/WpfApp/MainWindow.xaml.cs @@ -83,39 +83,15 @@ private void StartSave_Click(object sender, RoutedEventArgs e) { if (List_Save.SelectedItem != null) { - // Start the save operation - viewModel!.StartSave((SaveModel)List_Save.SelectedItem); - - // Change the visual appearance of the selected item to indicate that it's running - var selectedItem = List_Save.SelectedItem as SaveModel; - if (selectedItem != null) + // Start the save operation asynchronously + var selectedItem = (SaveModel)List_Save.SelectedItem; + var row_init = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); + if (row_init != null) { - // Get the row corresponding to the selected item - var row = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); - if (row != null) - { - // Change the background color of the row to green - row.Background = Brushes.Green; - - // Wait for the save operation to complete - Task.Run(() => - { - Application.Current.Dispatcher.Invoke(() => - { - while (viewModel!.IsSaveRunning((SaveModel)List_Save.SelectedItem)) - { - Thread.Sleep(1000); // Sleep for 1 second before checking again - } - - // Update the row color to red when the save operation is completed - row.Dispatcher.Invoke(() => - { - row.Background = Brushes.Red; - }); - }); - }); - } + row_init.Background = Brushes.Green; } + viewModel!.LaunchSave(selectedItem); + } else { @@ -127,6 +103,7 @@ private void StartSave_Click(object sender, RoutedEventArgs e) + private void DeleteSave_Click(object sender, RoutedEventArgs e) { if (List_Save.SelectedItem != null) @@ -138,17 +115,36 @@ private void DeleteSave_Click(object sender, RoutedEventArgs e) private void PlaySave_Click(object sender, RoutedEventArgs e) { - + var selectedItem = (SaveModel)List_Save.SelectedItem; + var row_init = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); + if (row_init != null) + { + row_init.Background = Brushes.Green; + } + viewModel!.PlaySave(); } private void PauseSave_Click(object sender, RoutedEventArgs e) { - + var selectedItem = (SaveModel)List_Save.SelectedItem; + var row_init = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); + if (row_init != null) + { + row_init.Background = Brushes.Orange; + } + viewModel!.PauseSave(); } + private void StopSave_Click(object sender, RoutedEventArgs e) { - + var selectedItem = (SaveModel)List_Save.SelectedItem; + var row_init = (DataGridRow)List_Save.ItemContainerGenerator.ContainerFromItem(selectedItem); + if (row_init != null) + { + row_init.Background = Brushes.Red; + } + viewModel!.StopSave(); } } } \ No newline at end of file diff --git a/WpfApp/ViewModels/MainViewModel.cs b/WpfApp/ViewModels/MainViewModel.cs index afaf5be..3533cba 100644 --- a/WpfApp/ViewModels/MainViewModel.cs +++ b/WpfApp/ViewModels/MainViewModel.cs @@ -2,6 +2,7 @@ using App.Core.Services; using System.Collections.ObjectModel; using System.Linq; +using System.Windows.Media.Animation; namespace WpfApp.ViewModels { @@ -32,14 +33,17 @@ public void DeleteSave(SaveModel saveModel) public void PlaySave() { + saveService.ResumeSave(); } public void PauseSave() { + saveService.PauseSave(); } public void StopSave() { + saveService.ResumeSave(); } public bool IsSaveRunning(SaveModel saveModel) @@ -50,22 +54,14 @@ public bool IsSaveRunning(SaveModel saveModel) } } - public void StartSave(SaveModel saveModel) + public void LaunchSave(SaveModel saveModel) { - lock (runningSaves) - { - runningSaves.Add(saveModel); // Add the saveModel instance to the runningSaves collection - } - - // Start the save operation + // Vérifiez si la sauvegarde est en cours d'exécution saveService.LaunchSave(saveModel); - - // Remove the saveModel instance from the runningSaves collection when the operation is completed - lock (runningSaves) - { - runningSaves.Remove(saveModel); - } + } + + } } From 667e2751e89be0264ca9fc91cb7dcdc05b5a6421 Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Tue, 27 Feb 2024 09:45:12 +0100 Subject: [PATCH 7/8] #104 Play Pause Stop Works --- App.Core/Services/SaveService.cs | 75 ++++++++++++++---------------- WpfApp/MainWindow.xaml | 1 + WpfApp/ViewModels/MainViewModel.cs | 2 +- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index ae40333..7966be9 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -2,9 +2,6 @@ using System.Text.Json; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Drawing; -using System.Security.Policy; -using System.Threading; namespace App.Core.Services { @@ -13,14 +10,16 @@ public class SaveService : IDisposable public ObservableCollection ListStateManager { get; set; } = []; public ObservableCollection ListSaveModel { get; set; } = []; - public AutoResetEvent pauseEvent { get; private set; } = new AutoResetEvent(false); + public ManualResetEvent pauseEvent { get; private set; } = new ManualResetEvent(false); + public ManualResetEvent resumeEvent { get; private set; } = new ManualResetEvent(true); + public ManualResetEvent stopEvent { get; private set; } = new ManualResetEvent(false); + + //TODO : Mettre manualResetsEvent et autre event resume private readonly StreamWriter? logWriter ; private readonly StreamWriter? stateWriter; - private readonly ConfigService configService = new(); private readonly StateManagerService stateManagerService = new(); - private readonly LoggerService loggerService = new(); public CopyService copyService = new(); private readonly JsonSerializerOptions options = new() { @@ -28,8 +27,7 @@ public class SaveService : IDisposable }; private Thread saveThread; - private bool isPaused; - private bool shouldAbort; + public SaveService() { @@ -43,15 +41,10 @@ public SaveService() public bool IsSoftwareRunning() { // Check if the software is running (e.g., "notepad.exe") - Process[] processes = Process.GetProcessesByName("Paint.exe"); - if (processes.Length > 0) - { - return true; - } - else - { - return false; - } + + Process[] processes = Process.GetProcessesByName("mspaint"); + return processes.Length > 0; + } @@ -90,11 +83,15 @@ public void LaunchSave(SaveModel saveModel) if (saveModel.Type == "Complete") { - + // Set the resume event to allow it to be resumed later saveThread = new Thread(() => - - CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel) - ); + { + //resumeEvent.Set(); + //pauseEvent.Set(); + + //stopEvent.Set(); + CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel); + }); // Start the thread saveThread.Start(); @@ -109,25 +106,19 @@ public void LaunchSave(SaveModel saveModel) public void PauseSave() { - Thread pauseThread = new Thread(() => - { - isPaused = true; - this.pauseEvent.WaitOne(); // Met en pause le thread jusqu'à ce que le signal soit reçu - }); - - pauseThread.Start(); // Start the thread; // Met en pause le thread jusqu'à ce que le signal soit reçu + pauseEvent.Set(); // Reset the pause event to block threads + resumeEvent.Reset(); // Set the resume event to allow it to be resumed later } public void StopSave() { - + stopEvent.Set(); // Signal the stop event to stop the save operation } - // Ajoutez une méthode pour reprendre la sauvegarde public void ResumeSave() { - isPaused = false; - pauseEvent.Set(); // Signale au thread de reprendre + pauseEvent.Reset(); // Signal the pause event to resume the save operation + resumeEvent.Set(); // Reset the resume event to block it until it's signaled again } @@ -137,20 +128,26 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s //DataState = new DataState(NameStateFile); //this.DataState.SaveState = true; - while (IsSoftwareRunning()) + // Wait for pauseEvent to be signaled before proceeding + pauseEvent.WaitOne(0); + + while (IsSoftwareRunning() && !stopEvent.WaitOne(0)) { - //wait for the software to close + // Software is running, wait for it to close Thread.Sleep(1000); - //display message box to inform the user that the software is still running - + // Display message box to inform the user that the software is still running } - if (isPaused) + if (stopEvent.WaitOne(0)) { - // Attendre l'événement de reprise avant de continuer - pauseEvent.WaitOne(); // Attend que l'événement soit déclenché + // Stop event has been signaled, return false to indicate incomplete save + return false; } + // Implement the logic with the event resume, pause, and stop + + // Wait for resumeEvent to be signaled before continuing + resumeEvent.WaitOne(); StateManagerModel stateModel = new() diff --git a/WpfApp/MainWindow.xaml b/WpfApp/MainWindow.xaml index 901996f..9389d5c 100644 --- a/WpfApp/MainWindow.xaml +++ b/WpfApp/MainWindow.xaml @@ -27,6 +27,7 @@ + diff --git a/WpfApp/ViewModels/MainViewModel.cs b/WpfApp/ViewModels/MainViewModel.cs index 3533cba..14be36a 100644 --- a/WpfApp/ViewModels/MainViewModel.cs +++ b/WpfApp/ViewModels/MainViewModel.cs @@ -43,7 +43,7 @@ public void PauseSave() public void StopSave() { - saveService.ResumeSave(); + saveService.StopSave(); } public bool IsSaveRunning(SaveModel saveModel) From c54ba03c02170011901c6bc96fc32de00b52ec00 Mon Sep 17 00:00:00 2001 From: Nathan LORIT Date: Tue, 27 Feb 2024 11:28:07 +0100 Subject: [PATCH 8/8] #107 Poucentage 80% fonctionnel - bug dans l'affichage - bug avec le play, pause, stop dans le cas de plusieurs sauvegardes --- App.Core/Models/SaveModel.cs | 33 +++++++++++--- App.Core/Services/SaveService.cs | 71 +++++++++++++++++++----------- WpfApp/MainWindow.xaml | 24 ++++++++++ WpfApp/ViewModels/MainViewModel.cs | 1 + 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/App.Core/Models/SaveModel.cs b/App.Core/Models/SaveModel.cs index 1409189..366918a 100644 --- a/App.Core/Models/SaveModel.cs +++ b/App.Core/Models/SaveModel.cs @@ -1,16 +1,37 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; namespace App.Core.Models { public class SaveModel : INotifyPropertyChanged { - public string InPath { get; set; } = ""; // Path of the source file - public string OutPath { get; set; } = ""; // Path of the target file - public string Type { get; set; } = ""; // Type of the save // false = Complete, true = Sequentiel - public string SaveName { get; set; } = "" ; // Name of the save + private int _percentage; + + public string InPath { get; set; } = ""; // Path of the source file + public string OutPath { get; set; } = ""; // Path of the target file + public string Type { get; set; } = ""; // Type of the save (false = Complete, true = Sequential) + public string SaveName { get; set; } = ""; // Name of the save public string EncryptChoice { get; set; } = ""; - public DateTime Date { get; } = DateTime.Now; // Date of the save + public DateTime Date { get; } = DateTime.Now; // Date of the save + + public float percentage + { + get { return _percentage; } + set + { + if (_percentage != value) + { + _percentage = (int)value; + OnPropertyChanged(nameof(percentage)); + } + } + } public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } diff --git a/App.Core/Services/SaveService.cs b/App.Core/Services/SaveService.cs index 7966be9..29cf401 100644 --- a/App.Core/Services/SaveService.cs +++ b/App.Core/Services/SaveService.cs @@ -14,6 +14,7 @@ public class SaveService : IDisposable public ManualResetEvent resumeEvent { get; private set; } = new ManualResetEvent(true); public ManualResetEvent stopEvent { get; private set; } = new ManualResetEvent(false); + private int progress = 0; //TODO : Mettre manualResetsEvent et autre event resume private readonly StreamWriter? logWriter ; @@ -27,6 +28,8 @@ public class SaveService : IDisposable }; private Thread saveThread; + private long fileDo =0; + private long fileTotal = 0; public SaveService() @@ -89,8 +92,11 @@ public void LaunchSave(SaveModel saveModel) //resumeEvent.Set(); //pauseEvent.Set(); - //stopEvent.Set(); + stopEvent.Reset(); + fileDo = 0; CompleteSave(saveModel.InPath, saveModel.OutPath, true, saveModel); + fileDo = fileTotal; + saveModel.percentage = (fileDo * 100 / fileTotal); }); // Start the thread @@ -112,6 +118,7 @@ public void PauseSave() public void StopSave() { + fileDo = 0; stopEvent.Set(); // Signal the stop event to stop the save operation } @@ -125,8 +132,8 @@ public void ResumeSave() private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel saveModel) { - //DataState = new DataState(NameStateFile); - //this.DataState.SaveState = true; + // DataState = new DataState(NameStateFile); + // this.DataState.SaveState = true; // Wait for pauseEvent to be signaled before proceeding pauseEvent.WaitOne(0); @@ -149,6 +156,11 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s // Wait for resumeEvent to be signaled before continuing resumeEvent.WaitOne(); + fileTotal += Directory.GetFiles(InPath).Length; + + // Recursively count files in subdirectories + + fileTotal += CountFiles(InPath); StateManagerModel stateModel = new() { @@ -168,14 +180,13 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s long nbfilesmax = 0; long size = 0; long nbfiles = 0; - float progs = 0; Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); //Starting the timed for the log file + stopwatch.Start(); // Starting the timed for the log file DirectoryInfo dir = new DirectoryInfo(InPath); // Get the subdirectories for the specified directory. - if (!dir.Exists) //Check if the file is present + if (!dir.Exists) // Check if the file is present { throw new DirectoryNotFoundException("ERROR 404 : Directory Not Found ! " + InPath); } @@ -184,14 +195,12 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s Directory.CreateDirectory(OutPath); // If the destination directory doesn't exist, create it. FileInfo[] files = dir.GetFiles(); // Get the files in the directory and copy them to the new location. - - if (!verif) // Check for the status file if it needs to reset the variables + if (verif) // Check for the status file if it needs to reset the variables { TotalSize = 0; nbfilesmax = 0; size = 0; nbfiles = 0; - progs = 0; foreach (FileInfo file in files) // Loop to allow calculation of files and folder size { @@ -210,31 +219,29 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s } - //Loop that allows to copy the files to make the backup + // Loop that allows to copy the files to make the backup foreach (FileInfo file in files) { string tempPath = Path.Combine(OutPath, file.Name); - if (size > 0) - { - progs = ((float)size / TotalSize) * 100; - } - stateModel.SourceFilePath = Path.Combine(OutPath, file.Name); stateModel.TargetFilePath = tempPath; stateModel.TotalFilesToCopy = nbfilesmax; stateModel.TotalFilesSize = TotalSize; stateModel.NbFilesLeftToDo = nbfilesmax - nbfiles; - stateModel.Progression = progs; stateModel.State = "IN PROGRESS"; - + stateManagerService.UpdateStateFile(ListStateManager); - //Systems which allows to insert the values ​​of each file in the report file. + // Systems which allows to insert the values of each file in the report file. - file.CopyTo(tempPath, true); //Function that allows you to copy the file to its new folder. + file.CopyTo(tempPath, true); // Function that allows you to copy the file to its new folder. nbfiles++; + fileDo+= nbfiles; size += file.Length; + saveModel.percentage = (int)((fileDo * 100) / fileTotal); + + // Calculate the global percentage of execution and store it in saveModel.percentage } @@ -246,16 +253,10 @@ private bool CompleteSave(string InPath, string OutPath, bool verif, SaveModel s CompleteSave(subdir.FullName, tempPath, true, saveModel); } - - stopwatch.Stop(); //Stop the stopwatch + stopwatch.Stop(); // Stop the stopwatch return false; - //this.TimeTransfert = stopwatch.Elapsed; // Transfer of the chrono time to the variable } - - - - public ObservableCollection LoadSave() { @@ -317,5 +318,23 @@ public void Dispose() logWriter?.Dispose(); stateWriter?.Dispose(); } + + + public int CountFiles(string directoryPath) + { + int fileCount = 0; + + // Count files in the current directory + fileCount += Directory.GetFiles(directoryPath).Length; + + // Recursively count files in subdirectories + foreach (string subDirectory in Directory.GetDirectories(directoryPath)) + { + fileCount += CountFiles(subDirectory); + } + + return fileCount; + } + } } diff --git a/WpfApp/MainWindow.xaml b/WpfApp/MainWindow.xaml index 9389d5c..fda4c68 100644 --- a/WpfApp/MainWindow.xaml +++ b/WpfApp/MainWindow.xaml @@ -52,10 +52,34 @@ + + + + + + + + + + + + + + + + + + + + + +