using Newtonsoft.Json; using Steamware.Scheduler; using SteamWare.IO; using SteamWare.Logger; using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace IOB_MAN { public partial class IOBManPanel : Form { #region Private Fields private const int SW_SHOWMAXIMIZED = 3; private const int SW_SHOWMINIMIZED = 2; private const int SW_SHOWNORMAL = 1; /// /// Context x sync thread /// private readonly SynchronizationContext synchronizationContext; /// /// Oggetto semaforico di lock /// private SemaphoreSlim _sync = new SemaphoreSlim(1); /// /// Binding source degli elementi gestiti.. /// private BindingSource ElencoIOB = new BindingSource(); #endregion Private Fields #region Protected Fields /// /// elenco item da rimuovere x check andato male... /// protected static List item2rem = new List(); /// /// Ramo applicazione (x update) /// protected string branchName = "master"; /// /// Counter del timer di base /// protected int checkPeriod = 2000; /// /// semaforo check... /// protected bool checkRunning = false; /// /// Counter del timer di forceCheck (30 sec, era 5 sec) /// protected int forceCheckPeriodMult = 30; /// /// Indica ultimo stato del check di restart... /// protected bool lastHlRestart = false; /// /// Totale processi avviati - ULTIMO dato mostrato /// protected int lastNumProcAvviati = 0; /// /// Totale processi running - ULTIMO dato mostrato /// protected int lastNumProcRunning = 0; /// /// Numero di Lock effettuati con successo /// protected int numLockFail = 0; /// /// Numero di lock falliti /// protected int numLockSuccess = 0; /// /// Totale processi avviati /// protected int numProcAvviati; /// /// Totale processi running /// protected int numProcRunning; /// /// Path dell'exe da chiamare /// protected string TargetExe = ""; /// /// Name dell'exe da chiamare /// protected string TargetName = ""; /// /// Dataora prossima scadenza riavvio automatico /// protected DateTime tOutAutocheck = DateTime.Now; /// /// Counter del timer UI /// protected int uiPeriod = 250; /// /// Ms di attesa x uscita processo (std) /// protected int waitForExitMsec = 250; #endregion Protected Fields #region Public Fields /// /// Elenco ARGS (uno per child da avviare) /// public List ArgsList = new List(); #endregion Public Fields #region Public Constructors /// /// Init classe /// public IOBManPanel() { InitializeComponent(); synchronizationContext = SynchronizationContext.Current; preInit(); loadConfig(); initTimers(); initControls(); updateStatus(); } #endregion Public Constructors #region Protected Properties /// /// URL stringa di UPDATE... /// protected string updateUrl { get { return string.Format("http://seriate.steamware.net:8083/SWS/MAPO/IOB-MAN/{0}/manifest.xml", branchName); } } #endregion Protected Properties #region Private Methods [DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); /// /// Apro un child x ogni args specificato /// private void apriChild() { // preventivamente CHIUDO TUTTO forceKillByName(TargetName); //closeAllChild(true); Thread.Sleep(250); // avvio i child foreach (var item in ArgsList) { startChildProc(item); } numProcAvviati = ArgsList.Count; updateNumRunning(); } private void apriChildSel() { // SOLO SE selezionato in dgv... if (dgvManagedItems.SelectedRows.Count > 0) { // ciclo su row selezionate foreach (DataGridViewRow riga in dgvManagedItems.SelectedRows) { // verifico che sia già chiuso... if (((iobAdapt)ElencoIOB[riga.Index]).isRunning == false) { startChildProc(((iobAdapt)ElencoIOB[riga.Index]).CodIOB, riga.Index); // rimuovo vecchia riga... ElencoIOB.RemoveAt(riga.Index); } } } updateNumRunning(); updateStatus(); } /// /// Apro un child x fare update con parametro che impedisca avvio IOB /// private void apriOneUpdate() { ProcessStartInfo psi = null; // da testare x aprire chiudere risorsa... psi = new ProcessStartInfo { FileName = TargetExe, Arguments = "MODE=UPD IOB=NONE", WindowStyle = ProcessWindowStyle.Normal }; // avvio processo con using... using (Process p = Process.Start(psi)) { p.WaitForExit(); // ora chiudo current... SE configurato if (utils.CRB("closeOnChildUpdate")) { this.Close(); } } updateNumRunning(); } private void AutoUpdater_ApplicationExitEvent() { utils.lgInfo("Chiusura IOB-WIN"); Thread.Sleep(100); // chiudo tutto closeAllChild(true); Thread.Sleep(1000); utils.lgInfo("Chiusura Applicazione"); // attendo 1 sec... Thread.Sleep(1000); // ESCO! Application.Exit(); } /// /// Chiudo primo processo child (se ce ne sono) /// /// /// private void btnClose_Click(object sender, EventArgs e) { chiudiChildSel(); } private void btnCloseAll_Click(object sender, EventArgs e) { // chiude tutto closeAllChild(false); //apriChild(); updateNumRunning(); updateStatus(); } private void btnMaximixeAll_Click(object sender, EventArgs e) { foreach (iobAdapt item in ElencoIOB.List) { if (item.isRunning) { try { Process p = Process.GetProcessById(item.pID); // cerco e chiudo quelli che mi interessano... var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, SW_SHOWNORMAL); } catch (Exception exc) { // errore era già chiuso.. utils.lgError($"Errore in SHOW windows:{Environment.NewLine}{exc}"); } } } } private void btnMinimizeAll_Click(object sender, EventArgs e) { foreach (iobAdapt item in ElencoIOB.List) { if (item.isRunning) { try { Process p = Process.GetProcessById(item.pID); // cerco e chiudo quelli che mi interessano... var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, SW_SHOWMINIMIZED); } catch (Exception exc) { // errore era già chiuso.. utils.lgError($"Errore in HIDE windows:{Environment.NewLine}{exc}"); } } } } private void btnMoreTOut_Click(object sender, EventArgs e) { tOutAutocheck = tOutAutocheck.AddMinutes(utils.CRI("autoRestartTimeoutMin")); } private void btnOpenAll_Click(object sender, EventArgs e) { // per iscurezza chiudo tutto closeAllChild(true); Thread.Sleep(1000); // lettura conf file... loadConfig(); // apertura apriChild(); updateNumRunning(); updateStatus(); } private void btnRestartAll_Click(object sender, EventArgs e) { // chiude tutto closeAllChild(true); apriChild(); updateNumRunning(); updateStatus(); } private void btnStartSel_Click(object sender, EventArgs e) { // riapro child (SOLO SE non era già aperto...) apriChildSel(); } /// /// verifica buttons attivi data selezione su gridview... /// private void checkButtons() { bool selected = (dgvManagedItems.SelectedRows.Count > 0); btnClose.Enabled = selected; } private bool checkIstance(iobAdapt item, Process[] processList) { bool needRem; // verifico se non sia già stato segnato x rimozione...) if (item2rem.Find(x => x.pID == item.pID) != null) { needRem = true; } else { // verifico se esista il processo... try { if (processList.Length > 0) { Process p = myGetProcByID(processList, item.pID); if (p != null) { needRem = p.HasExited; } else { needRem = true; } } else { needRem = true; } } catch { needRem = true; } if (needRem) { if (!item2rem.Contains(item)) { item2rem.Add(item); } item.isRunning = false; } else { item.isRunning = true; } } return needRem; } /// /// Controllo periodico dei processi attivi /// private async Task checkProcessStatusAsync() { await _sync.WaitAsync(); if (!checkRunning) { // reset variabili appoggio checkRunning = true; // eseguo task! await Task.Run(() => checkRunningchild()).ConfigureAwait(false); updateStatus(); checkRunning = false; } else { utils.lgInfo($"Error: checkRunning: {checkRunning}"); } _sync.Release(); } /// /// Verifica se i proc child siano ancora in RUN /// private void checkRunningchild() { IList allItems = (IList)ElencoIOB.List; bool needRem = false; // 2020.02.01 passato chiamata specifica x leggere in 1 sola volta TUTTO elenco processi (x nome)... Process[] processList = Process.GetProcessesByName(TargetName); // ciclo Parallel.ForEach(allItems, item => { needRem = checkIstance(item, processList); } ); // aggiorno datagrid! dgvManagedItems.Invalidate(); } /// /// Controllo periodico dei processi DA RIATTIVARE /// private void checkWatchdog() { processAutoRestart(); // aggiorno datagrid! dgvManagedItems.Invalidate(); } private void chiudiChildSel() { int pid = -1; // SOLO SE selezionato in dgv... if (dgvManagedItems.SelectedRows.Count > 0) { foreach (DataGridViewRow riga in dgvManagedItems.SelectedRows) { // chiudo! _ = int.TryParse(riga.Cells["pID"].Value.ToString(), out pid); if (pid >= 0) { // provo a vedere SE ci sia il processo e di conseguenza lo chiudo... try { Process p = Process.GetProcessById(pid); // cerco e chiudo quelli che mi interessano... p.CloseMainWindow(); } catch { // errore era già chiuso.. } // indico NON running su datasource ((iobAdapt)ElencoIOB[riga.Index]).isRunning = false; } } } updateNumRunning(); updateStatus(); } private void chkAutoRestart_CheckedChanged(object sender, EventArgs e) { // se tolgo autorestart --> imposto NUOVA scadenza x forzare check tOutAutocheck = DateTime.Now.AddMinutes(utils.CRI("autoRestartTimeoutMin")); txtTOutAutoCheck.Visible = !chkAutoRestart.Checked; btnMoreTOut.Visible = !chkAutoRestart.Checked; // fa subito controllo riavvio... processAutoRestart(); } /// /// Chiude tutti i child /// /// resetta elenco private void closeAllChild(bool doReset) { item2rem.Clear(); foreach (iobAdapt item in ElencoIOB.List) { item2rem.Add(item); } // processod a elenco noto foreach (var item in item2rem) { if (item.isRunning) { try { Process p = Process.GetProcessById(item.pID); p.CloseMainWindow(); p.WaitForExit(waitForExitMsec); if (!p.HasExited) { utils.lgError($"Process not exited, now calling p.Kill()"); p.Kill(); } // indico NON running su datasource if (doReset) { ElencoIOB.Remove(item); } else { item.isRunning = false; } } catch (Exception exc) { Logging.Instance.Error($"Errore in fase di chiusura processo (pid: {item.pID} ) da elenco {exc}"); } } } if (item2rem.Count > 0) { // attendo 2*waitForExitMsec che i processi possano chiudersi gracefully... Thread.Sleep(waitForExitMsec * 2); forceKillByName(TargetName); // ripeto seconda volta x sicurezza Thread.Sleep(waitForExitMsec * 4); forceKillByName(TargetName); } // verifico se resettare if (doReset) { // resetto elenco! ElencoIOB.Clear(); numProcAvviati = 0; } // resetto item2rem.Clear(); numProcRunning = 0; // update! updateStatus(); } /// /// Chiude il PID selezionato /// /// Chiude item richiesto private void closeSingleChild(iobAdapt item) { utils.lgInfo($"Chiusura processo | IOB: {item.CodIOB} | pid: {item.pID}"); // rimuovo item ElencoIOB.Remove(item); try { Process p = Process.GetProcessById(item.pID); p.CloseMainWindow(); } catch { } updateNumRunning(); } private void closeTimers() { MainTimer.Dispose(); forceCheckTimer.Dispose(); UI_Timer.Dispose(); } private void dgvManagedItems_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { // seleziono riga... int pid = -1; if (e.RowIndex >= 0) { dgvManagedItems.Rows[e.RowIndex].Selected = true; using (var riga = dgvManagedItems.Rows[e.RowIndex]) { int.TryParse(riga.Cells["pID"].Value.ToString(), out pid); if (pid >= 0) { // provo a vedere SE ci sia il processo e di conseguenza lo chiudo... try { Process p = Process.GetProcessById(pid); // cerco e chiudo quelli che mi interessano... var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, SW_SHOWNORMAL); } catch { // errore era già chiuso.. } } } } } private void dgvManagedItems_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (this.dgvManagedItems.Columns[e.ColumnIndex].Name == "iobOnline") { if (e.Value != null) { bool isOk = false; bool.TryParse(e.Value.ToString(), out isOk); e.CellStyle.BackColor = isOk ? Color.White : Color.Red; } } else if (this.dgvManagedItems.Columns[e.ColumnIndex].Name == "lastPlcRead") { if (e.Value != null) { DateTime adesso = DateTime.Now; DateTime lastRead = adesso.AddMinutes(-60); DateTime.TryParse(e.Value.ToString(), out lastRead); if (Math.Abs(adesso.Subtract(lastRead).TotalSeconds) > 180) { e.CellStyle.BackColor = Color.Red; } else if (Math.Abs(adesso.Subtract(lastRead).TotalSeconds) > 120) { e.CellStyle.BackColor = Color.OrangeRed; } else if (Math.Abs(adesso.Subtract(lastRead).TotalSeconds) > 60) { e.CellStyle.BackColor = Color.Yellow; } else if (Math.Abs(adesso.Subtract(lastRead).TotalSeconds) > 15) { e.CellStyle.BackColor = Color.YellowGreen; } else { e.CellStyle.BackColor = Color.White; } } } else if (this.dgvManagedItems.Columns[e.ColumnIndex].Name.StartsWith("queueElLen")) { if (e.Value != null) { int coda = 0; int.TryParse(e.Value.ToString(), out coda); e.CellStyle.ForeColor = (coda > 0) ? Color.Red : Color.Green; } } else if (this.dgvManagedItems.Columns[e.ColumnIndex].Name == "isRunning") { if (e.Value != null) { bool isOk = false; bool.TryParse(e.Value.ToString(), out isOk); if (!isOk) { e.CellStyle.ForeColor = Color.Red; DataGridViewCellStyle currstyle = dgvManagedItems[0, e.RowIndex].Style; currstyle.ForeColor = Color.Red; dgvManagedItems[1, e.RowIndex].Style = currstyle; dgvManagedItems[2, e.RowIndex].Style = currstyle; dgvManagedItems[3, e.RowIndex].Style = currstyle; } } } } private void dgvManagedItems_SelectionChanged(object sender, EventArgs e) { checkButtons(); } private void ElencoIOB_AddingNew(object sender, System.ComponentModel.AddingNewEventArgs e) { } private void ElencoIOB_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) { updateNumRunning(); } private void forceCheckTimer_Tick(object sender, EventArgs e) { if (!checkRunning) { try { Task result = Task.Run(() => checkProcessStatusAsync().ConfigureAwait(false)); result.Wait(); } catch (Exception exc) { Logging.Instance.Error($"forceCheckTimer_Tick {exc}"); } } checkWatchdog(); // riavvio i timer x sicurezza... UI_Timer.Stop(); UI_Timer.Start(); MainTimer.Stop(); MainTimer.Start(); } private void forceCloseALLToolStripMenuItem_Click(object sender, EventArgs e) { // per iscurezza chiudo tutto closeAllChild(true); Thread.Sleep(1000); updateStatus(); } /// /// Effettua un force kill dato nome processo /// /// 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) { utils.lgError($"Process not Killed, 2nd try p.kill()"); p.Kill(); p.WaitForExit(waitForExitMsec * 2); } if (!p.HasExited) { utils.lgError($"Process not Killed, 3th try p.kill()"); p.Kill(); } } } catch (Exception exc) { Logging.Instance.Error($"Errore in fase di kill processo da nome {exc}"); } } } } } private void initControls() { // gestione eventi binding source ElencoIOB.AddingNew += ElencoIOB_AddingNew; ElencoIOB.ListChanged += ElencoIOB_ListChanged; // collego tab a binding dgvManagedItems.DataSource = ElencoIOB; utils.lgInfo("Timer started"); if (utils.CRB("autoStartProc")) { apriChild(); utils.lgInfo("Start processi effettuato"); } } /// /// Inizializzazione timers /// private void initTimers() { MainTimer.Interval = checkPeriod; MainTimer.Start(); UI_Timer.Interval = uiPeriod; UI_Timer.Start(); forceCheckTimer.Interval = checkPeriod * forceCheckPeriodMult; forceCheckTimer.Start(); // avvio il task con apposita classe Steamware... da 00:30 int fullRestartHour = memLayer.ML.CRI("fullRestartHour"); int fullRestartMinute = memLayer.ML.CRI("fullRestartMinute"); int fullRestartIntervMin = memLayer.ML.CRI("fullRestartIntervMin"); TaskSched.IntervalInMinutes(fullRestartHour, fullRestartMinute, fullRestartIntervMin, () => { // eseguo nel contesto di sincronizzazione synchronizationContext.Post(new SendOrPostCallback(o => { // effettuo reload conf e restart reloadConfAndRestart(); }), ""); }); } private void IOBManPanel_FormClosing(object sender, FormClosingEventArgs e) { closeAllChild(true); Thread.Sleep(250); closeAllChild(true); closeTimers(); } private void loadArgList() { // in primis cerco SE ESISTA il file json di configuraizone parametri avvio string fileName = utils.CRS("ArgsConfFile"); string jsonFileName = $"{Application.StartupPath}{fileName}"; // verifico se esista il file richeisto if (File.Exists(jsonFileName)) { // leggo il file json StreamReader reader = new StreamReader(jsonFileName); string jsonData = reader.ReadToEnd(); if (!string.IsNullOrEmpty(jsonData)) { ArgsList = JsonConvert.DeserializeObject>(jsonData); } } else { // se non lo trovassi --> uso la chaive in web.config e GENERO un nuovo file x prox avvio... string ArgsString = utils.CRS("ArgsList"); utils.lgInfo($"Args found: {ArgsString}"); if (string.IsNullOrEmpty(ArgsString)) { var rand = new Random(); // ne creo rand (5-15) di default... for (int i = 0; i < rand.Next(5, 10); i++) { ArgsList.Add($"127.0.0.{i + 1}"); } } else { var elenco = ArgsString.Split(','); foreach (var item in elenco) { ArgsList.Add(item); } } // serializzo e salvo file! string jsonData = JsonConvert.SerializeObject(ArgsList); File.WriteAllText(jsonFileName, jsonData); } } /// /// Caricamento configurazione /// private void loadConfig() { ArgsList.Clear(); waitForExitMsec = utils.CRI("waitForExitMsec"); checkPeriod = utils.CRI("checkPeriod"); uiPeriod = utils.CRI("uiPeriod"); forceCheckPeriodMult = utils.CRI("forceCheckPeriodMult"); TargetExe = utils.CRS("targetExe"); TargetName = utils.CRS("appNameExt"); if (string.IsNullOrEmpty(TargetExe)) { //TargetExe = string.Format(@"{0}\Resources\Test.bat", Application.StartupPath); TargetExe = $@"{Application.StartupPath}\Resources\Test.bat"; } utils.lgInfo($"Target exe: {TargetExe}"); // caricamento configurazione argomenti di avvio processi loadArgList(); } private void loadConfToolStripMenuItem_Click(object sender, EventArgs e) { // per iscurezza chiudo tutto closeAllChild(true); Thread.Sleep(500); // lettura conf file... loadConfig(); // apertura apriChild(); updateStatus(); } /// /// Effettua tutte le verifiche periodiche a timer... /// /// /// private void MainTimer_Tick(object sender, EventArgs e) { if (!checkRunning) { try { //Task result = checkProcessStatusAsync(); //result.Wait(); Task result = Task.Run(() => checkProcessStatusAsync().ConfigureAwait(false)); result.Wait(); } catch (Exception exc) { Logging.Instance.Error($"MainTimer_Tick {exc}"); } } } /// /// Chiama apertura + update status... /// /// /// private void openALLToolStripMenuItem_Click(object sender, EventArgs e) { } private void performBarAdvance() { synchronizationContext.Post(new SendOrPostCallback(o => { tsProgBar.PerformStep(); if (tsProgBar.Value >= tsProgBar.Maximum) { tsProgBar.Value = 0; } tsProgBar.Invalidate(); }), ""); } private void preInit() { utils.lgInfo("Starting App"); lblApp.Text = $"{ConfigurationManager.AppSettings.Get("appName")}"; lblVers.Text = $" v.{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}"; // init prog bar tsProgBar.Maximum = 100; tsProgBar.Step = 4; } /// /// Effettua processing autorestart /// private void processAutoRestart() { // verifico se ci siano processi (da ARGS LIST) NON running --> li riavvio! Dictionary proc2restart = new Dictionary(); int indice = 0; foreach (iobAdapt item in ElencoIOB.List) { // se NON E' running if (!item.isRunning) { // segno da eliminare e riavviare utils.lgInfo($"Processo non in running | IOB: {item.CodIOB} | pid: {item.pID}"); proc2restart.Add(indice, item); } // se NON comunica da troppo (ultima comunicazione è > 5 minuti fa...) else if (!item.plcOk) { utils.lgInfo($"Processo non in PLC-Online | IOB: {item.CodIOB} | pid: {item.pID}"); proc2restart.Add(indice, item); } indice++; } // SE abilitato autorestart... if (chkAutoRestart.Checked) { // se ci sono processi da riavviare... if (proc2restart.Count > 0) { foreach (var item in proc2restart) { ElencoIOB.Remove(item); // chiudo! closeSingleChild(item.Value); } foreach (var item in proc2restart) { // riavvio! startChildProc(item.Value.CodIOB, item.Key); } // update! updateStatus(); } } else { // se autorestart scaduto e NON checked --> lo imposto if (tOutAutocheck < DateTime.Now) { // lo riattivo chkAutoRestart.Checked = true; } } } /// /// Effettua chiusura completa + rilettura conf + riavvio /// private void reloadConfAndRestart() { utils.lgInfo("Call reloadConfAndRestart: start full reload"); // per sicurezza chiudo tutto closeAllChild(true); Thread.Sleep(500); updateStatus(); // rileggo conf loadConfig(); Thread.Sleep(500); // riapro apriChild(); utils.lgInfo("Call reloadConfAndRestart: completed full reload"); } private void reloadConfRestartToolStripMenuItem_Click(object sender, EventArgs e) { reloadConfAndRestart(); } /// /// Chiama Restart (close/start) + update status... /// /// /// private void restartALLToolStripMenuItem_Click(object sender, EventArgs e) { // chiude tutto closeAllChild(false); apriChild(); updateStatus(); } /// /// Avvio di un child process da parametro ARG /// /// /// private void startChildProc(string procArg, int indice = -1) { ProcessStartInfo psi = null; // da testare x aprire chiudere risorsa... psi = new ProcessStartInfo { FileName = TargetExe, Arguments = $"{utils.CRS("BaseArg")}{procArg}", WindowStyle = ProcessWindowStyle.Minimized }; //childProc.StartInfo = psi; Process p = Process.Start(psi); // accodo nuovo IOB... iobAdapt newIob = new iobAdapt(); DateTime adesso = DateTime.Now; newIob.redisMan = new RedisIobCache("", procArg); newIob.CodIOB = procArg; newIob.startTime = adesso; newIob.pID = p.Id; newIob.isRunning = true; // aggiungo a datasource, se indice -1 aggiungendo e basta, altrimenti alla posizione richiesta... if (indice == -1) { ElencoIOB.Add(newIob); } else { ElencoIOB.Insert(indice, newIob); } utils.lgInfo($"Avviato child process per {procArg} | pid: {p.Id}"); } private void txtTOutAutoCheck_TextChanged(object sender, EventArgs e) { } private void UI_Timer_Tick(object sender, EventArgs e) { Task result = updateProgBarAsync(); result.Wait(); } private void updateIOBWINToolStripMenuItem_Click(object sender, EventArgs e) { // chiude tutte closeAllChild(true); Thread.Sleep(500); updateStatus(); // apre solo 1 con conf "fake" x condurre update... apriOneUpdate(); } private void updateModeToolStripMenuItem_Click(object sender, EventArgs e) { // chiude tutte closeAllChild(true); Thread.Sleep(500); updateStatus(); // apre solo 1 con conf "fake" x condurre update... apriOneUpdate(); } private void updateNumRunning() { // ricalcolo running... numProcRunning = 0; // ciclo su row selezionate foreach (DataGridViewRow riga in dgvManagedItems.Rows) { // verifico che sia già chiuso... if (((iobAdapt)ElencoIOB[riga.Index]).isRunning) { numProcRunning++; } } } private async Task updateProgBarAsync() { await Task.Run(() => performBarAdvance()).ConfigureAwait(false); } private void updateStatus() { synchronizationContext.Post(new SendOrPostCallback(o => { // aggiorno SOLO SE sono variati... if (lastNumProcAvviati != numProcAvviati || lastNumProcRunning != numProcRunning) { // aggiorno labels tsslNumProc.Text = $"Configurati {ArgsList.Count} processi | Avviati: {numProcAvviati} | Attivi: {numProcRunning}"; lastNumProcAvviati = numProcAvviati; lastNumProcRunning = numProcRunning; } bool hlRestart = false; // colore da num proc... if (numProcRunning == ArgsList.Count) { tsslNumProc.ForeColor = Color.Green; } else if (numProcAvviati < ArgsList.Count) { tsslNumProc.ForeColor = Color.DarkRed; hlRestart = true; } else { tsslNumProc.ForeColor = Color.OrangeRed; hlRestart = true; } // se NON checked aggiorno contatore... if (!chkAutoRestart.Checked) { txtTOutAutoCheck.Text = $"{tOutAutocheck.Subtract(DateTime.Now).TotalSeconds:N0}"; } // se è variato... if (hlRestart != lastHlRestart) { tOutAutocheck = DateTime.Now.AddMinutes(utils.CRI("autoRestartTimeoutMin")); lastHlRestart = hlRestart; // fix autorestart... if (hlRestart) { chkAutoRestart.ForeColor = System.Drawing.Color.Red; chkAutoRestart.Text = "Auto Restart!!!"; } else { chkAutoRestart.ForeColor = DefaultForeColor; chkAutoRestart.Text = "Auto Restart"; } txtTOutAutoCheck.Visible = !chkAutoRestart.Checked; btnMoreTOut.Visible = !chkAutoRestart.Checked; } }), ""); } #endregion Private Methods #region Public Methods /// /// Cerca nell'elenco il processo corrente /// /// /// /// public static Process myGetProcByID(Process[] processlist, int id) { return processlist.FirstOrDefault(pr => pr.Id == id); } #endregion Public Methods } }