Files
egwcapp/EgwControlCenter.Core/AppControlService.cs
T
2024-12-24 18:44:52 +01:00

591 lines
19 KiB
C#

using EgwControlCenter.Core.DTO;
using EgwControlCenter.Core.Models;
using EgwCoreLib.Utils;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace EgwControlCenter.Core
{
public class AppControlService : IAppControlService
{
#region Public Constructors
public AppControlService()
{
try
{
Assembly assembly = Assembly.GetExecutingAssembly();
string startDir = Path.GetDirectoryName(assembly.Location)!;
ConfDir = startDir;
//DataDir = Environment.GetEnvironmentVariable("ClickOnce_DataDirectory") ?? startDir;
string appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
if (string.IsNullOrEmpty(appData))
{
appData = "C:\\ProgramData";
}
DataDir = Path.Combine(appData, "EgalWare", "AppControlCenter");
Log.Trace($"appData: {appData} | EnvData: {Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}");
// verifico esistenza directory...
Directory.CreateDirectory(DataDir);
CurrCheck = new ReleaseChecker(ConfDir, DataDir);
Log.Trace($"Folder Setup | {ConfDir} | {DataDir}");
}
catch (Exception exc)
{
Log.Error($"Error in AppControlService.init:{Environment.NewLine}{exc}");
}
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento update configurazione
/// </summary>
public event Action EA_ConfigUpdated = null!;
/// <summary>
/// Evento calling a remote in corso
/// </summary>
public event Action EA_RemoteCalling = null!;
/// <summary>
/// Evento udpate status controlli
/// </summary>
public event Action EA_StatusUpdated = null!;
#endregion Public Events
#region Public Properties
public string AppKey
{
get => CurrCheck.CurrPatrolCont.AppKey;
set
{
if (CurrCheck.CurrPatrolCont.AppKey != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.AppKey = value;
ReportConfigUpd();
}
}
}
/// <summary>
/// verifica autorizzazione al canale, ovvero
/// - deve avere tutte le chiavi/registri di comunicazione
/// - CodImpiego e AppKey devono essere validate
/// </summary>
public bool ChannelAuth
{
get
{
// in primis deve avere i dati di configurazione
bool answ = CurrCheck.CurrPatrolCont.HasCommData;
if (answ)
{
// verifico CodImpiego locale sia valido
answ = CurrCheck.CurrPatrolCont.CodImpiego == SubLicManager.CodImpiego();
// verifica sia valida (scadenza a tempo da ultimo controllo) chiave auth in remoto
if (answ)
{
}
}
return answ;
}
}
public bool CloudCallActive
{
get => cloudCallActive;
set
{
if (cloudCallActive != value)
{
cloudCallActive = value;
ReportRemoteCall();
}
}
}
public string CodApp
{
get => CurrCheck.CurrPatrolCont.CodApp;
set
{
if (CurrCheck.CurrPatrolCont.CodApp != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.CodApp = value;
ReportConfigUpd();
}
}
}
public string CodImpiego
{
get => CurrCheck.CurrPatrolCont.CodImpiego;
set
{
if (CurrCheck.CurrPatrolCont.CodImpiego != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.CodImpiego = value;
ReportConfigUpd();
}
}
}
public List<VersStatusDTO> ListAppStatus
{
get => CurrCheck.ListAppStatus;
set
{
CurrCheck.ListAppStatus = value;
ReportStatusUpd();
}
}
public string MainKey
{
get => CurrCheck.CurrPatrolCont.MainKey;
set
{
if (CurrCheck.CurrPatrolCont.MainKey != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.MainKey = value;
ReportConfigUpd();
}
}
}
public int RefreshPeriod
{
get => CurrCheck.CurrPatrolCont.RefreshIntSec;
set
{
if (CurrCheck.CurrPatrolCont.RefreshIntSec != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.RefreshIntSec = value < refPMin ? refPMin : value > refPMax ? refPMax : value;
ReportConfigUpd();
}
}
}
public List<ControlTarget> TargetList
{
get => CurrCheck.CurrPatrolCont.TargetList;
set
{
CurrCheck.CurrPatrolCont.TargetList = value;
ReportConfigUpd();
}
}
/// <summary>
/// num minuti veto controllo COMPLETO
/// </summary>
public int VetoCheck
{
get => CurrCheck.CurrPatrolCont.VetoCheckMinutes;
set
{
if (CurrCheck.CurrPatrolCont.VetoCheckMinutes != value)
{
// verifico ammissibilità
CurrCheck.CurrPatrolCont.VetoCheckMinutes = value < vetoPMin ? vetoPMin : value > vetoPMax ? vetoPMax : value;
ReportConfigUpd();
}
}
}
/// <summary>
/// num minuti veto controllo refresh
/// </summary>
public int VetoRefresh
{
get => CurrCheck.CurrPatrolCont.VetoRefreshMinutes;
}
#endregion Public Properties
#region Public Methods
public async Task<bool> CheckAttivazioni()
{
bool fatto = await CurrCheck.CheckAttivazioni();
return fatto;
}
/// <summary>
/// Effettua un controllo completo (loacele remoto)
/// </summary>
/// <param name="doForce">se true esegue anche prima della scadenza veto</param>
public async Task DoFullCheckAsync(bool doForce)
{
DateTime adesso = DateTime.Now;
if (lastCheckDone.AddMinutes(VetoRefresh) < adesso || doForce)
{
lastCheckDone = adesso;
//se non è forzato controllo ultimo check
CloudCallActive = true;
// in primis controllo se il remote è ok sennò mi fermo...
bool remoteOk = await CurrCheck.CheckRemote();
if (remoteOk)
{
// effettua un refresh del controllo status...
bool locCheckOk = CurrCheck.UpdateLocalStatus(doForce);
bool remCheckOk = false;
bool critCheckOk = false;
if (locCheckOk)
{
remCheckOk = await CurrCheck.CheckRemoteReleases();
}
// infine effettua una verifica dello status "release critiche"
critCheckOk = await CurrCheck.CheckCriticalReport();
if (remCheckOk || critCheckOk)
{
ReportStatusUpd();
}
}
CloudCallActive = false;
}
}
/// <summary> Effettua rilettura configurazione e setup controlli... </summary
public void DoReloadConfig()
{
try
{
CurrCheck = new ReleaseChecker(ConfDir, DataDir);
Log.Trace($"Riletta config Setup | {ConfDir} | {DataDir}");
}
catch (Exception exc)
{
Log.Equals($"Eccezione in DoReloadConfig{Environment.NewLine}{exc}");
}
ReportConfigUpd();
}
/// <summary>
/// Effettua salvataggio configurazione
/// </summary>
public void DoSaveConfig()
{
try
{
var rawData = JsonConvert.SerializeObject(CurrCheck.CurrPatrolCont, Formatting.Indented);
if (rawData != null && rawData.Length > 2)
{
File.WriteAllText(CurrCheck.ConfPath, rawData);
Log.Trace($"Effettuato salvataggio Config Setup! | {ConfDir} | {DataDir}");
}
}
catch (Exception exc)
{
Log.Equals($"Eccezione in DoSaveConfig{Environment.NewLine}{exc}");
}
ReportConfigUpd();
}
/// <summary>
/// metodo update specifico x app IobWin
/// </summary>
/// <param name="reqApp"></param>
/// <returns></returns>
public async Task DoUpdateIobWin(VersStatusDTO reqApp)
{
// controllo di avere un file di riferimento...
string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? "";
if (!string.IsNullOrEmpty(appDir))
{
// 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
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);
});
// step 4: download
// step 5: unzip + overwrite
//// step 6: restore conf
//CopyDirectory(destDir, srcDir);
}
// step 7: rimozione file lock semaforo
File.Delete(updFilePath);
await Task.Delay(10);
}
public void ResetConf()
{
bool fatto = CurrCheck.ResetConf();
if (fatto)
{
ReportConfigUpd();
ReportStatusUpd();
}
}
/// <summary>
/// Reimposta CodImpiego / AppKey da librerie Egw secondo richiesta...
/// </summary>
/// <param name="resCodImpiego"></param>
/// <param name="resAppKey"></param>
public void ResetSubLic(bool resCodImpiego, bool resAppKey)
{
if (resCodImpiego)
{
CurrCheck.CurrPatrolCont.CodImpiego = SubLicManager.CodImpiego();
}
if (resAppKey)
{
CurrCheck.CurrPatrolCont.AppKey = SubLicManager.GenKey(CodApp);
}
ReportConfigUpd();
}
#endregion Public Methods
#region Protected Properties
protected SubLicManager SubLicManager { get; set; } = new SubLicManager();
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Helper copia directory + contenuto
/// </summary>
/// <param name="sourceDir"></param>
/// <param name="destDir"></param>
protected void CopyDirectory(string sourceDir, string destDir)
{
// Create the destination directory if it doesn't exist
if (!Directory.Exists(destDir))
{
Directory.CreateDirectory(destDir);
}
// Copy all files
foreach (var file in Directory.GetFiles(sourceDir))
{
string destFile = Path.Combine(destDir, Path.GetFileName(file));
File.Copy(file, destFile, true);
}
// Copy all subdirectories
foreach (var dir in Directory.GetDirectories(sourceDir))
{
string destSubDir = Path.Combine(destDir, Path.GetFileName(dir));
CopyDirectory(dir, destSubDir);
}
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Classe logger
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Directory di backup x IobWin
/// </summary>
private string backupDir = @"C:\Steamware\backup";
/// <summary>
/// Directory temp di appoggio
/// </summary>
private string tempDir = @"C:\Steamware\temp";
/// <summary>
/// DataOra ultimo controllo effettuato
/// </summary>
private DateTime lastCheckDone = DateTime.Today.AddMonths(-1);
/// <summary>
/// Valore massimo ammesso refresh sec
/// </summary>
private int refPMax = 3600;
/// <summary>
/// Valore minimo ammesso refresh sec
/// </summary>
private int refPMin = 1;
/// <summary>
/// File segnaposto x lock fasi di update
/// </summary>
private string updFilePath = @"c:\Steamware\IOB-MAN\update.run";
/// <summary>
/// Valore massimo ammesso veto minuti
/// </summary>
private int vetoPMax = 14400;
/// <summary>
/// Valore minimo ammesso veto minuti
/// </summary>
private int vetoPMin = 30;
/// <summary>
/// Ms di attesa x uscita processo (std)
/// </summary>
private int waitForExitMsec = 250;
#endregion Private Fields
#region Private Properties
private bool cloudCallActive { get; set; } = false;
private string ConfDir { get; set; } = "";
private ReleaseChecker CurrCheck { get; set; } = null!;
private string DataDir { get; set; } = "";
#endregion Private Properties
#region Private Methods
/// <summary>
/// Effettua un force kill dato nome processo
/// </summary>
/// <param name="nomeProc"></param>
private void ForceKillByName(string nomeProc)
{
Process[] stillRunningProc = Process.GetProcessesByName(nomeProc);
if (stillRunningProc != null)
{
if (stillRunningProc.Length > 0)
{
foreach (var item in stillRunningProc)
{
try
{
Process p = Process.GetProcessById(item.Id);
{
if (!p.HasExited)
{
p.Kill();
p.WaitForExit(waitForExitMsec);
}
if (!p.HasExited)
{
Log.Error($"Process not Killed, 2nd try p.kill()");
p.Kill();
p.WaitForExit(waitForExitMsec * 2);
}
if (!p.HasExited)
{
Log.Error($"Process not Killed, 3th try p.kill()");
p.Kill();
}
}
}
catch (Exception exc)
{
Log.Error($"Errore in fase di kill processo da nome {exc}");
}
}
}
}
}
private void ReportConfigUpd()
{
if (EA_ConfigUpdated != null)
{
EA_ConfigUpdated?.Invoke();
}
}
private void ReportRemoteCall()
{
if (EA_RemoteCalling != null)
{
EA_RemoteCalling?.Invoke();
}
}
private void ReportStatusUpd()
{
if (EA_StatusUpdated != null)
{
// controllo se almeno 1 app abbia update...
foreach (var item in ListAppStatus)
{
if (item.HasUpdate)
{
EA_StatusUpdated?.Invoke();
break;
}
}
}
}
#endregion Private Methods
}
}