diff --git a/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor b/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor index f090a60..88324ea 100644 --- a/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor +++ b/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor @@ -18,6 +18,7 @@ updating } +
diff --git a/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor.cs b/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor.cs index f42ff6d..b64bc17 100644 --- a/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor.cs +++ b/EgwControlCenter.App/Components/Compo/ApplicationCheck.razor.cs @@ -1,6 +1,7 @@ using EgwControlCenter.Core; using EgwControlCenter.Core.DTO; using Microsoft.AspNetCore.Components; +using System.Diagnostics; namespace EgwControlCenter.App.Components.Compo { @@ -36,6 +37,7 @@ namespace EgwControlCenter.App.Components.Compo set { showOnlyUpdate = value; + currPage = 1; ForceReload(); } } @@ -63,25 +65,69 @@ namespace EgwControlCenter.App.Components.Compo { case CoreEnum.AppType.None: break; + case CoreEnum.AppType.Cli: break; + case CoreEnum.AppType.Machine: break; + case CoreEnum.AppType.WebApp: break; + case CoreEnum.AppType.WinApp: // esegue update delle app Iob-* - await ACService.DoUpdateIobWin(ReqApp); + Stopwatch sw = new Stopwatch(); + sw.Start(); + UpdateMsg = "Backup Config"; + currVal = 0; + nextVal = 60; + await InvokeAsync(StateHasChanged); + await ACService.IobWinUpdateStep(ReqApp, 1); + currStats.RecordExeData("Step01_Backup", sw.Elapsed); + sw.Restart(); + + UpdateMsg = "Download update"; + currVal = 60; + nextVal = 90; + await InvokeAsync(StateHasChanged); + await ACService.IobWinUpdateStep(ReqApp, 2); + currStats.RecordExeData("Step02_Download", sw.Elapsed); + sw.Restart(); + + UpdateMsg = "Unzip software"; + currVal = 90; + nextVal = 95; + await InvokeAsync(StateHasChanged); + await ACService.IobWinUpdateStep(ReqApp, 3); + currStats.RecordExeData("Step03_UnZip", sw.Elapsed); + sw.Restart(); + + UpdateMsg = "Config Restore"; + currVal = 95; + nextVal = 100; + await InvokeAsync(StateHasChanged); + await ACService.IobWinUpdateStep(ReqApp, 4); + currStats.RecordExeData("Step04_Restore", sw.Elapsed); + currStats.SaveToDisk(); + UpdateMsg = "Completed!"; + currVal = 100; + await InvokeAsync(StateHasChanged); break; + default: break; } isUpdating = false; await InvokeAsync(StateHasChanged); - await Task.Delay(50); + // check finale + await ACService.DoFullCheckAsync(true); } - + /// + /// Oggetto gestione statistiche + /// + private TaskRunStats currStats { get; set; } = new TaskRunStats(); protected async Task ForceCheck() { @@ -92,6 +138,7 @@ namespace EgwControlCenter.App.Components.Compo { ACService.EA_StatusUpdated += ACService_EA_StatusUpdated; ACService.EA_RemoteCalling += ACService_EA_RemoteCalling; + currStats = new TaskRunStats(ACService.DataDir, "Stats.json"); } protected override void OnParametersSet() @@ -119,6 +166,16 @@ namespace EgwControlCenter.App.Components.Compo #endregion Protected Methods + #region Private Fields + + private int currVal = 100; + private int expTimeMSec = 6000; + private int maxVal = 100; + private int nextVal = 100; + private string UpdateMsg = "Updating..."; + + #endregion Private Fields + #region Private Properties private int currPage { get; set; } = 1; diff --git a/EgwControlCenter.App/EgwControlCenter.App.csproj b/EgwControlCenter.App/EgwControlCenter.App.csproj index 7013267..1422668 100644 --- a/EgwControlCenter.App/EgwControlCenter.App.csproj +++ b/EgwControlCenter.App/EgwControlCenter.App.csproj @@ -5,7 +5,7 @@ enable true enable - 1.1.2412.2418 + 1.1.2412.2712 Debug;Release;DEBUG_Local diff --git a/EgwControlCenter.App/Properties/PublishProfiles/ClickOnceProfile.pubxml b/EgwControlCenter.App/Properties/PublishProfiles/ClickOnceProfile.pubxml index 9481e19..b599158 100644 --- a/EgwControlCenter.App/Properties/PublishProfiles/ClickOnceProfile.pubxml +++ b/EgwControlCenter.App/Properties/PublishProfiles/ClickOnceProfile.pubxml @@ -4,8 +4,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - 2418 - 1.1.2412.2418 + 2712 + 1.1.2412.2712 True Release True diff --git a/EgwControlCenter.Core/AppControlService.cs b/EgwControlCenter.Core/AppControlService.cs index acad33c..076c790 100644 --- a/EgwControlCenter.Core/AppControlService.cs +++ b/EgwControlCenter.Core/AppControlService.cs @@ -146,6 +146,11 @@ namespace EgwControlCenter.Core } } + /// + /// Dir applicazione + /// + public string DataDir { get; set; } = ""; + public List ListAppStatus { get => CurrCheck.ListAppStatus; @@ -301,79 +306,35 @@ namespace EgwControlCenter.Core } /// - /// metodo update specifico x app IobWin + /// Metodo update specifico x app IobWin a step /// /// + /// Step richiesto: 1=backup conf, 2=download, 3=unzip, $=fix conf e close /// - public async Task DoUpdateIobWin(VersStatusDTO reqApp) + public async Task IobWinUpdateStep(VersStatusDTO reqApp, int step) { - // controllo di avere un file di riferimento... - string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; - if (!string.IsNullOrEmpty(appDir)) + // eseguo step secondo richiesta... + switch (step) { - // step 0: inserisce i file semaforo di segnalazione update in corso - using (FileStream fs = File.Create(updFilePath)) - { - // File is created and access is released when the using block ends - } + case 1: + await IobWinDoBackup(reqApp); + break; - // step 1: backup configurazioni partendo da path applicazione... - string srcDir = Path.Combine(appDir, "DATA", "CONF"); - string destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "DATA", "CONF"); - // se MAN uso folder "accorciata" - if (reqApp.CodApp == "IOB-MAN") - { - srcDir = Path.Combine(appDir, "CONF"); - destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "CONF"); - } - CopyDirectory(srcDir, destDir); + case 2: + await IobWinDownload(reqApp); + break; - // step 2: kill processi - ForceKillByName(reqApp.CodApp); + case 3: + await IobWinUnzip(reqApp); + break; - // step 3: backup + zip intera folder applicativo - string tempZip = Path.Combine(tempDir, "FULL", reqApp.CodApp); - if (Directory.Exists(tempZip)) - { - Directory.Delete(tempZip, true); - } - Directory.CreateDirectory(tempZip); - CopyDirectory(appDir, tempZip); - - string zipPath = Path.Combine(backupDir, "Archive", reqApp.CodApp, $"{DateTime.Now:yyyyMMdd}_{DateTime.Now:HHmmss}.zip"); - if (File.Exists(zipPath)) - { - File.Delete(zipPath); - } - else - { - string zipDir = Path.GetDirectoryName(zipPath) ?? ""; - if (!string.IsNullOrEmpty(zipDir)) - { - if (!Directory.Exists(zipDir)) - { - Directory.CreateDirectory(zipDir); - } - } - } - await Task.Run(() => - { - ZipFile.CreateFromDirectory(tempZip, zipPath); - }); - - // FixMe - todo: separare in 3 step a questo punto : primo backup, poi download e infine install e mostrare update in pagina - - // step 4: download - - // step 5: unzip + overwrite - - //// step 6: restore conf - //CopyDirectory(destDir, srcDir); + case 4: + await IobWinRestoreConf(reqApp); + break; + default: + break; } - // step 7: rimozione file lock semaforo - File.Delete(updFilePath); - await Task.Delay(10); } public void ResetConf() @@ -406,6 +367,14 @@ namespace EgwControlCenter.Core #endregion Public Methods + #region Protected Fields + + protected UpdateMan updateMan = new UpdateMan(); + + protected UpdateMan updateManAuth = new UpdateMan("SWDownloader", "viaD@nte16"); + + #endregion Protected Fields + #region Protected Properties protected SubLicManager SubLicManager { get; set; } = new SubLicManager(); @@ -456,11 +425,6 @@ namespace EgwControlCenter.Core /// private string backupDir = @"C:\Steamware\backup"; - /// - /// Directory temp di appoggio - /// - private string tempDir = @"C:\Steamware\temp"; - /// /// DataOra ultimo controllo effettuato /// @@ -476,6 +440,11 @@ namespace EgwControlCenter.Core /// private int refPMin = 1; + /// + /// Directory temp di appoggio + /// + private string tempDir = @"C:\Steamware\temp"; + /// /// File segnaposto x lock fasi di update /// @@ -506,12 +475,30 @@ namespace EgwControlCenter.Core private ReleaseChecker CurrCheck { get; set; } = null!; - private string DataDir { get; set; } = ""; - #endregion Private Properties #region Private Methods + /// + /// Elimina contenuto directory + /// + /// + private void DeleteDirectoryContents(string directoryPath) + { + // recupero info dir + DirectoryInfo directory = new DirectoryInfo(directoryPath); + // elimina file + foreach (FileInfo file in directory.GetFiles()) + { + file.Delete(); + } + // elimina ulteriori directory + foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + { + subDirectory.Delete(true); + } + } + /// /// Effettua un force kill dato nome processo /// @@ -556,6 +543,146 @@ namespace EgwControlCenter.Core } } + /// + /// Step preliminare di backup conf e app corrente + /// + /// + /// + private async Task IobWinDoBackup(VersStatusDTO reqApp) + { + // controllo di avere un file di riferimento... + string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; + if (!string.IsNullOrEmpty(appDir)) + { + string tempZip = Path.Combine(tempDir, "FULL", reqApp.CodApp); + // step 0: inserisce i file semaforo di segnalazione update in corso + using (FileStream fs = File.Create(updFilePath)) + { + // File is created and access is released when the using block ends + } + + // step 1: backup configurazioni partendo da path applicazione... + string srcDir = Path.Combine(appDir, "DATA", "CONF"); + string destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "DATA", "CONF"); + // se MAN uso folder "accorciata" + if (reqApp.CodApp == "IOB-MAN") + { + srcDir = Path.Combine(appDir, "CONF"); + destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "CONF"); + } + CopyDirectory(srcDir, destDir); + + // step 2: kill processi + ForceKillByName(reqApp.CodApp); + + // step 3: backup + zip intera folder applicativo + if (Directory.Exists(tempZip)) + { + Directory.Delete(tempZip, true); + } + Directory.CreateDirectory(tempZip); + CopyDirectory(appDir, tempZip); + + string zipPath = Path.Combine(backupDir, "Archive", reqApp.CodApp, $"{DateTime.Now:yyyyMMdd}_{DateTime.Now:HHmmss}.zip"); + if (File.Exists(zipPath)) + { + File.Delete(zipPath); + } + else + { + string zipDir = Path.GetDirectoryName(zipPath) ?? ""; + if (!string.IsNullOrEmpty(zipDir)) + { + if (!Directory.Exists(zipDir)) + { + Directory.CreateDirectory(zipDir); + } + } + } + await Task.Run(() => + { + ZipFile.CreateFromDirectory(tempZip, zipPath); + }); + } + await Task.Delay(10); + } + + /// + /// Step di download da remoto + /// + /// + /// + private async Task IobWinDownload(VersStatusDTO reqApp) + { + // step 4: download // controllo di avere un file di riferimento... + string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; + if (!string.IsNullOrEmpty(appDir)) + { + string installerDir = Path.Combine(backupDir, "installer", reqApp.CodApp); + // se il path fosse vuoto --> uso quello standard da nexus... + string urlDownload = reqApp.UrlDownload; + if (string.IsNullOrEmpty(urlDownload)) + { + urlDownload = $"https://nexus.steamware.net/repository/utility/MAPO/{reqApp.CodApp}/stable/{reqApp.CodApp}.zip"; + } + long size = updateMan.DownloadApp(urlDownload, installerDir, reqApp.CodApp, reqApp.VersNumLast); + // se non andata provo con auth... + if (size == 0) + { + size = updateManAuth.DownloadApp(urlDownload, installerDir, reqApp.CodApp, reqApp.VersNumLast); + } + } + await Task.Delay(10); + } + + /// + /// Step di fix backup configurazioni + unlock file update run + /// + /// + /// + private async Task IobWinRestoreConf(VersStatusDTO reqApp) + { + // step 6: restore conf + string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; + if (!string.IsNullOrEmpty(appDir)) + { + string srcDir = Path.Combine(appDir, "DATA", "CONF"); + string destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "DATA", "CONF"); + // se MAN uso folder "accorciata" + if (reqApp.CodApp == "IOB-MAN") + { + srcDir = Path.Combine(appDir, "CONF"); + destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "CONF"); + } + + CopyDirectory(destDir, srcDir); + } + // step 7: rimozione file lock semaforo + File.Delete(updFilePath); + await Task.Delay(10); + } + + /// + /// Step di unzip file scaricato + /// + /// + /// + private async Task IobWinUnzip(VersStatusDTO reqApp) + { + // step 5: unzip + overwrite + string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; + if (!string.IsNullOrEmpty(appDir)) + { + string installerDir = Path.Combine(backupDir, "installer", reqApp.CodApp); + string srcZip = Path.Combine(installerDir, $"{reqApp.CodApp}.zip"); + // svuoto la dir di destinazione preliminarmente... + DeleteDirectoryContents(appDir); + // esegue unzip + ZipFile.ExtractToDirectory(srcZip, appDir, true); + } + await Task.Delay(10); + } + private void ReportConfigUpd() { if (EA_ConfigUpdated != null) diff --git a/EgwControlCenter.Core/TaskRunStats.cs b/EgwControlCenter.Core/TaskRunStats.cs new file mode 100644 index 0000000..44cb238 --- /dev/null +++ b/EgwControlCenter.Core/TaskRunStats.cs @@ -0,0 +1,160 @@ +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EgwControlCenter.Core +{ + public class TaskRunStats + { + #region Public Constructors + + /// + /// init da vuoto + /// + public TaskRunStats() + { + // init generico senza reload file + } + + /// + /// Init classe con rilettura dati + /// + public TaskRunStats(string appDir, string confName) + { + AppDir = appDir; + ConfName = confName; + // inizio rileggendo (se presenti) dati conf da disco + tryReload(); + } + + #endregion Public Constructors + + #region Public Properties + + /// + /// Tempo medio complessivo registrato + /// + public TimeSpan AvgTotalTime + { + get + { + TimeSpan answ = new TimeSpan(); + if (TaskData != null && TaskData.Count > 0) + { + foreach (var record in TaskData) + { + answ += record.Value.AvgTime; + } + } + return answ; + } + } + + /// + /// nome del task registrato + /// + public Dictionary TaskData { get; set; } = new Dictionary(); + + #endregion Public Properties + + #region Public Methods + + /// + /// Tempo medio per un Task + /// + public TimeSpan AvgTaskTime(string taskName) + { + TimeSpan answ = new TimeSpan(); + if (TaskData != null && TaskData.Count > 0) + { + if (TaskData.ContainsKey(taskName)) + { + answ = TaskData[taskName].AvgTime; + } + } + return answ; + } + + /// + /// Registro una singola esecuzione x effettuare valutazioni successive... + /// + /// + /// + public void RecordExeData(string taskName, TimeSpan duration) + { + // cerco nel dizionario se presente + if (TaskData.ContainsKey(taskName)) + { + TaskData[taskName].NumRun++; + TaskData[taskName].TotalTime += duration; + } + else + { + TaskData.Add(taskName, new TaskStat() { NumRun = 1, TotalTime = duration }); + } + } + + /// + /// Salva le statistiche su disco + /// + public void SaveToDisk() + { + var rawData = JsonConvert.SerializeObject(TaskData, Formatting.Indented); + if (rawData != null && rawData.Length > 2) + { + File.WriteAllText(StatsPath, rawData); + } + } + + #endregion Public Methods + + #region Private Fields + + private static Logger Log = LogManager.GetCurrentClassLogger(); + + #endregion Private Fields + + #region Private Properties + + private string AppDir { get; set; } = ""; + + private string ConfName { get; set; } = ""; + + /// + /// Path file di conf check + /// + private string StatsPath + { + get => Path.Combine(AppDir, ConfName); + } + + #endregion Private Properties + + #region Private Methods + + private void tryReload() + { + if (File.Exists(StatsPath)) + { + string rawData = File.ReadAllText(StatsPath); + if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) + { + try + { + TaskData = JsonConvert.DeserializeObject>(rawData); + } + catch (Exception exc) + { + Log.Error($"Eccezione in tryReload:{Environment.NewLine}{exc}"); + } + } + } + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/EgwControlCenter.Core/TaskStat.cs b/EgwControlCenter.Core/TaskStat.cs new file mode 100644 index 0000000..005df87 --- /dev/null +++ b/EgwControlCenter.Core/TaskStat.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace EgwControlCenter.Core +{ + /// + /// Statistiche esecuzione task + /// + public class TaskStat + { + /// + /// num di esecuzioni registrate + /// + public long NumRun { get; set; } = 1; + + /// + /// Tempo totale registrato + /// + public TimeSpan TotalTime { get; set; } = new TimeSpan(); + + /// + /// Timespan medio esecuzione + /// + [NotMapped] + public TimeSpan AvgTime + { + get => TotalTime / (NumRun > 1 ? NumRun : 1); + } + } +} diff --git a/EgwControlCenter.Core/UpdateMan.cs b/EgwControlCenter.Core/UpdateMan.cs new file mode 100644 index 0000000..42525fc --- /dev/null +++ b/EgwControlCenter.Core/UpdateMan.cs @@ -0,0 +1,127 @@ +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace EgwControlCenter.Core +{ + public class UpdateMan + { + #region Public Fields + + /// + /// Init classe + /// + public static UpdateMan obj = new UpdateMan(); + + #endregion Public Fields + + #region Public Constructors + + /// + /// Init classe + /// + public UpdateMan() + { } + + /// + /// Init classe + /// + /// + /// + public UpdateMan(string user, string pwd) + { + userName = user; + passwd = pwd; + doAuth = !string.IsNullOrEmpty($"{userName}{passwd}"); + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Effettua download specifica versione applicativo per install locale + /// + /// url del file (tipicamente *.zip) da scaricare + /// Cartella locale x salvataggio file + /// Nome applicativo con cui salvare + /// Versione applicativo (per salvataggio storico versioni) + public long DownloadApp(string downloadUrl, string localDir, string packName, string version) + { + long size = 0; + // verifico directory + if (!Directory.Exists(localDir)) + { + Directory.CreateDirectory(localDir); + } + string localFile = ""; + string localLast = ""; + try + { + localFile = Path.Combine(localDir, $"{packName}_Build_{version}.zip"); + localLast = Path.Combine(localDir, $"{packName}.zip"); + // scarica file e salva in directory locale... + using (var http = new HttpClient()) + { + if (doAuth) + { + //client.Credentials = new System.Net.NetworkCredential(userName, passwd); + http.DefaultRequestHeaders.Accept.Clear(); + var byteArray = System.Text.ASCIIEncoding.UTF8.GetBytes($"{userName}:{passwd}"); + http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } + + // url da chiamare + http.BaseAddress = new Uri(downloadUrl); + // lettura + var data = http.GetAsync(downloadUrl).Result; + using (var myReader = data.Content.ReadAsStreamAsync()) + { + //var myReader = data.Content.ReadAsStreamAsync(); + if (data.StatusCode != HttpStatusCode.OK) + { + Log.Info($"Errore in chiamata DownloadApp per URL {downloadUrl}: status code: {data.StatusCode}"); + } + else + { + using (Stream outStream = File.OpenWrite(localFile)) + { + myReader.Result.CopyTo(outStream); + } + // ora lo copio come latest... + File.Copy(localFile, localLast, true); + // recupero dimensione file + var thisFile = new FileInfo(localLast); + size = thisFile.Length; + } + } + } + } + catch (Exception exc) + { + Log.Error($"Errore in fase di download installers | URL: {downloadUrl}{Environment.NewLine}{exc}"); + } + return size; + } + + #endregion Public Methods + + #region Private Fields + + private static Logger Log = LogManager.GetCurrentClassLogger(); + private bool doAuth = false; + + #endregion Private Fields + + #region Private Properties + + private string passwd { get; set; } = ""; + private string userName { get; set; } = ""; + + #endregion Private Properties + } +} \ No newline at end of file