using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using MapoSDK; using MathNet.Numerics.Distributions; using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; using System.Windows.Forms; namespace IOB_WIN_FORM { public partial class MainForm : Form { #region Public Fields /// /// oggetto logging /// public static Logger lg; /// /// Data ultimo controllo comunicazione /// public DateTime lastComCheck; #endregion Public Fields #region Public Constructors /// /// Init Main Form /// public MainForm(string[] args) { // salvo parametri avvio... saveArgs(args); // continuo iNIT!!! try { InitializeComponent(); myInit(); if (args != null) { if (args.Length > 0) { lgInfo("Argomenti di avvio:"); foreach (var item in myArgs) { lgInfo(item); } } else { lgInfo("NESSUN Argomento di avvio trovato"); } } } catch (Exception exc) { lgError($"Errore in avvio MainForm:{Environment.NewLine}{exc}"); } } #endregion Public Constructors #region Public Properties /// /// Visualizzazione stato di comunicazione attiva con PLC /// public bool commPlcActive { set { // se true --> comunica/verde, altrimenti grigio lblApp.ForeColor = value ? Color.SeaGreen : Color.Black; lblVers.ForeColor = value ? Color.SeaGreen : Color.DarkSlateGray; if (!ShouldUpdateUI()) return; this.UIThread(delegate { statusStrip1.Refresh(); }); } } /// /// Verifica il server: esiste IP, risponde a ping, risponde a pagina ALIVE /// public bool testServer { get { bool answ = false; if (!string.IsNullOrEmpty(MPIP)) { if (utils.dtVetoPing < DateTime.Now) { IPStatus pingStatus = GetPingStatus(); // se passa il ping faccio il resto... if (pingStatus == IPStatus.Success) { try { // chiamo URL, se restituisce "OK" è alive! string callResp = utils.callUrl(urlAlive); answ = (callResp == "OK"); } catch (Exception exc) { lgError($"Errore in CheckServerAlive:{Environment.NewLine}{exc}"); } // verifico SE è variato stato online/offline... if (utils.MPIO_Online != answ) { // se ORA sono online riporto... if (answ) { lgInfo("SERVER ONLINE"); utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 2); } else { lgInfo("SERVER OFFLINE in MainForm:testServer"); } // salvo nuovo status... utils.MPIO_Online = answ; } else { // allungo periodo controllo... utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); } } else { lgInfo($"SERVER NOT RESPONDING (PING at {MPIP})"); utils.MPIO_Online = false; updateComStats(0, 0, false); // imposto veto a 10 volte reinvio dati standard... utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10); utils.dtVetoSend = utils.dtVetoPing; } } else { // altrimenti passo ultimo valore noto... answ = utils.MPIO_Online; } } return answ; } } /// /// URL per check alive... /// public string urlAlive { get { return $@"http://{MPIP}{MPURL}/IOB"; } } #endregion Public Properties #region Public Methods /// /// Avanza la barra di stato... /// public void advProgBar() { // solo se non minimizzata if (!ShouldUpdateUI()) return; try { // aggiorno runtime... TimeSpan uptime = DateTime.Now.Subtract(formStartTime); tslRunTime.Text = string.Format("Running: {0}gg {1:00}:{2:00}:{3:00}", uptime.Days, uptime.Hours, uptime.Minutes, uptime.Seconds); if (MainProgrBar.Style != ProgressBarStyle.Marquee) { // se è arrivato a MAX resetto... MainProgrBar.PerformStep(); // verifico SE deve resettare progress bar if (resetProgBar > 10) { MainProgrBar.Value = 0; resetProgBar = 0; } // se arrivoa max imposto richiesta reset... if (MainProgrBar.Value == MainProgrBar.Maximum) { resetProgBar++; } // invalido x ridisegnare... MainProgrBar.Invalidate(); } } catch (Exception exc) { lgError($"Eccezione in advProgBar:{Environment.NewLine}{exc}"); } } /// /// mostra un testo sulla status bar + LOG /// /// public void displayTaskAndLog(string txt2show) { lgInfo(txt2show); // mostro solo se non minimizzata if (!ShouldUpdateUI()) return; lblStatus.Text = txt2show; lblStatus.Invalidate(); } /// /// Mostra update delle statistiche di comunicazione (num plc connessi, indirizzo server) /// con colore ad indicare anomalie... /// /// /// /// public void updateComStats(int numPLC, int numAttivi, bool serverOk) { // testo lblComStats.Text = $"{numAttivi}/{numPLC} PLC --> {MPIP} (MP/IO)"; // colore secondo valori... server !="ND" è ok, PLC > 0 è OK int score = 0; if (serverOk) { score++; } if (numPLC > 0 && numAttivi == numPLC) { score++; } if (!ShouldUpdateUI()) return; switch (score) { case 0: lblComStats.ForeColor = Color.Red; break; case 1: lblComStats.ForeColor = Color.Orange; break; case 2: lblComStats.ForeColor = Color.Green; break; default: lblComStats.ForeColor = Color.Gray; break; } } #endregion Public Methods #region Protected Fields protected static string baseUrl = @"https://liman.egalware.com/MP/IO/"; /// /// Ramo applicazione (x update) /// protected string branchName = "master"; /// /// chiamata x check alive di base /// protected string CMDALIVE = ""; /// /// chiamata x ricevere un codice IOB da gestire/acquisire /// protected string CMDIOB2CALL = ""; /// /// chiamata x invio comunicazioen reboot dell'IOBMAN /// protected string CMDREBO = ""; /// /// Data Avvio form /// protected DateTime formStartTime; /// /// Lista delle IOB da avviare /// protected List IOB2START; /// /// Num max di CNC da gestire /// protected int MAXCNC = 1; /// /// Valore minimo x considerare variazione delta parametri IOB solo x dati DataOra esecuzione /// protected int MinDeltaSec = 2; /// /// Modalità di avvio /// protected StartMode ModoAvvio = StartMode.STD; /// /// Indirizzo server /// protected string MPIP = ""; /// /// URL base applicazione /// protected string MPURL = ""; /// /// Parametri (opzionali) di avvio /// protected string[] myArgs; /// /// var x gestione reset progBar... arrivato a 3 faccio reset /// protected int resetProgBar = 0; /// /// Generatore numeri random /// protected Random rndGen = new Random(); /// /// Dizionario dei parametri di avvio (da elenco args...) /// protected Dictionary startParams = new Dictionary(); #endregion Protected Fields #region Protected Properties /// /// IP del PC /// protected static string currIP { get { string answ = "127.0.0.1"; return answ; } } protected bool pingDisabled { get { return utils.CRB("serverPingDisabled"); } } /// /// URL stringa di UPDATE... /// protected string updateUrl { get { return string.Format("http://seriate.steamware.net:8083/SWS/MAPO/IOB-WIN/{0}/manifest.xml", branchName); } } /// /// URL per recuperare i file dell'IOB (SENZA IOB) /// protected string urlDownloadFile { get => $"http://{MPIP}{MPURL}/IOB/getFiles/"; } /// /// URL per recuperare i file dell'IOB su CLOUD (SENZA IOB) /// protected string urlDownloadFileCloud { get { return $"{baseUrl}IOB/getFiles/"; } } /// /// URL per chiedere quale sia la IOB da acquisire/gestire (se c'è...) /// protected string urlIob2call { get { string answ = ""; try { answ = string.Format(@"http://{0}{1}{2}{3}", MPIP, MPURL, CMDIOB2CALL, utils.GetIP()); } catch { } return answ; } } /// /// URL per segnalazione reboot... /// protected string urlReboot { get { string answ = ""; try { answ = string.Format(@"http://{0}{1}{2}{3}&mac={4}", MPIP, MPURL, CMDREBO, utils.GetIP(), utils.GetMACAddress()); } catch { answ = string.Format(@"http://{0}{1}{2}{3}", MPIP, MPURL, CMDREBO, utils.GetIP()); } return answ; } } /// /// URL per salvare i file dell'IOB (SENZA IOB) /// protected string urlUploadFile { get => $"http://{MPIP}{MPURL}/IOB/uploadFile/"; } /// /// URL per salvare i file dell'IOB su CLOUD (SENZA IOB) /// protected string urlUploadFileCloud { get => $"{baseUrl}IOB/uploadFile/"; } #endregion Protected Properties #region Protected Methods /// /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... /// /// protected static void lgError(string txt2log) { lg.Factory.Configuration.Variables["codIOB"] = "MAIN"; lg.Error(txt2log); } /// /// Effettua logging INFO corretto impostando anche la variabile IOB prima di scrivere... /// /// protected static void lgInfo(string txt2log) { lg.Factory.Configuration.Variables["codIOB"] = "MAIN"; lg.Info(txt2log); } /// /// Fix finestre restanti post chiusura di una finestra... /// /// /// protected void Child_FormClosed(object sender, FormClosedEventArgs e) { this.LayoutMdi(MdiLayout.TileHorizontal); } /// /// Sistemazione finestre POST visualizzazione... /// /// /// protected void Child_Shown(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.TileHorizontal); } protected override void OnFormClosing(FormClosingEventArgs e) { MainTimer?.Stop(); MainTimer?.Dispose(); closeActiveChild(); base.OnFormClosing(e); } /// /// Chiude le finestre child attive /// protected void closeActiveChild() { lgInfo("Chiamato closeActiveChild"); if (this.HasChildren) { try { if (this.ActiveMdiChild != null) { this.ActiveMdiChild.Close(); this.LayoutMdi(MdiLayout.TileHorizontal); } } catch { } } } protected void myInit() { DateTime adesso = DateTime.Now; resetProgBar = 0; formStartTime = adesso; lastComCheck = adesso.AddHours(-1); lblStatus.Text = "Loading"; // fix versione e nome APP! nomeApp = $"{ConfigurationManager.AppSettings.Get("appName")}"; string versione = $" v.{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}"; // fix icon! notifyIcon1.Text = $"{nomeApp} | {versione}"; Icon = Icon.ExtractAssociatedIcon(utils.defIconFilePath); notifyIcon1.Icon = Icon.ExtractAssociatedIcon(utils.defIconFilePath); this.Text = nomeApp; lblApp.Text = nomeApp; lblVers.Text = versione; LogManager.ReconfigExistingLoggers(); lg = LogManager.GetCurrentClassLogger(); displayTaskAndLog("MainForm Starting"); // salvo versione SW! lgInfo("------ STARTUP DATA ------"); lgInfo($"App Mode : {nomeApp} ({ModoAvvio})"); lgInfo($"Versione : {versione}"); lgInfo("--------------------------"); // se abilitato autoload conf leggo file corretto... if (utils.CRB("autoLoadConf")) { try { loadIniFile(utils.mainConfFilePath); lgInfo("INI LOADED"); // avvio child come richiesto! startAdapters(); } catch (Exception exc) { displayTaskAndLog(string.Format("Eccezione in myInit: {0}", exc)); } } else { displayTaskAndLog("Waiting for config file selection"); } displayTaskAndLog("Program Running"); createTrayMenu(); displayTaskAndLog("Tray Menu OK"); // avvio minimizzato se richiesto if (utils.CRB("startMinimized")) { // imposto minimized se necessario! if (WindowState != FormWindowState.Minimized) { WindowState = FormWindowState.Minimized; } displayTaskAndLog("Minimized"); } try { IPStatus pingStatus = GetPingStatus(); // se passa il ping faccio il resto... if (pingStatus == IPStatus.Success) { // segnalo reboot (programma)... utils.callUrl(urlReboot); } else { displayTaskAndLog("Server unreachable, cannot send urlReboot for MAIN FORM"); } } catch (Exception exc) { lgError($"MainForm myInit EXCEPTION in fase di chiamata URL di reboot:{urlReboot}{Environment.NewLine}{exc}"); } displayTaskAndLog("Main Form OK"); } /// /// Apre la finestra child con conf /// protected virtual void openChild(string IOB) { if (IOB == null) { throw new ArgumentNullException(nameof(IOB)); } AdapterForm child = new AdapterForm(IOB); child.MdiParent = this; child.Text = IOB; child.Show(); child.Shown += Child_Shown; child.FormClosed += Child_FormClosed; } /// /// effettua salvataggio argomenti /// /// protected void saveArgs(string[] args) { if (args != null) { // salvo args di avvio (sono parametri tipo param1=val1 param2=test1,test2,test3 myArgs = args; if (myArgs.Length > 0) { // salvo dictionary! foreach (var item in myArgs) { var kvp = item.Split('='); startParams.Add(kvp[0], kvp[1]); } } // verifico gli args... if (startParams.ContainsKey("MODE")) { try { ModoAvvio = (StartMode)Enum.Parse(typeof(StartMode), startParams["MODE"]); } catch { } } } } #endregion Protected Methods #region Private Fields /// /// Semaforo x chiusura form /// private static bool closed = false; /// /// Nome applicazione (da app.config) /// private string nomeApp = ""; #endregion Private Fields #region Private Properties /// /// Configurazione gerarchica completa (v 4.x.x.x) /// public IobConfTree IOBConfFull = new IobConfTree(); /// /// test ping all'indirizzo impostato nei parametri /// /// private IPStatus GetPingStatus() { IPStatus answ = IPStatus.Unknown; // se disabilitato salto... if (IOBConfFull != null && IOBConfFull.MapoMes.DisabPing) { answ = IPStatus.Success; } else { IPAddress address; PingReply reply; using (Ping pingSender = new Ping()) { address = IPAddress.Loopback; string ipAdrr = MPIP; IPAddress.TryParse(ipAdrr, out address); try { // se != null --> uso address... if (address != null) { reply = pingSender.Send(address, 500); } else { reply = pingSender.Send(MPIP, 500); } } catch { reply = pingSender.Send(IPAddress.Loopback, 100); } } answ = reply.Status; } return answ; } #endregion Private Properties #region Private Methods private void arrangeWindowsToolStripMenuItem_Click(object sender, EventArgs e) { // ferma tutti i child form... foreach (var ChildForm in this.MdiChildren) { try { //ChildForm.Show(); ChildForm.WindowState = FormWindowState.Normal; } catch { } } this.LayoutMdi(MdiLayout.TileHorizontal); } /// /// Effettua verifiche varie comunicazione verso PLC e verso server /// private void checkCom() { // eseguo update se è passato almeno comCheckTOut secondi da ultimo check... if (DateTime.Now.Subtract(lastComCheck).TotalSeconds > utils.CRI("comCheckTOut")) { lastComCheck = DateTime.Now; // conta quanti child form di adapter verso PLC siano aperti !!!FARE!!! chiedere con // un metodo che siano ATTIVI e contare quello... int numPLC = this.MdiChildren.Length; int numAttivi = 0; foreach (var item in this.MdiChildren) { try { var currForm = (AdapterForm)item; if (currForm.iobObj != null) { numAttivi += currForm.iobObj.IobOnline ? 1 : 0; } } catch { } } // update stato! updateComStats(numPLC, numAttivi, testServer); } } /// /// Verifica stato windows (minimized/normal) e visibilità con tray... /// private void checkFormVisibility() { // se non può massimizzare imposto COMUNQUE a minimized... if (!utils.CRB("windowCanMax")) { WindowState = FormWindowState.Minimized; } // SOLO SE se sono in modo STD if (ModoAvvio == StartMode.STD) { // controllo cosa devo mostrare... if (WindowState == FormWindowState.Minimized) { notifyIcon1.Visible = false; sendToTray(); } else { notifyIcon1.Visible = false; } } else { notifyIcon1.Visible = false; } // fix child! this.LayoutMdi(MdiLayout.TileHorizontal); } private void closeALLAdaptersToolStripMenuItem_Click(object sender, EventArgs e) { // chiudo tutte closeAllChild(); } /// /// Chiusura applicazione /// private void closeAllChild() { lgInfo($"Chiamato closeAllChild per {this.MdiChildren.Count()} child form."); // ferma tutti i child form... foreach (var ChildForm in this.MdiChildren) { try { ChildForm.Close(); } catch { } } } /// /// crea menù tray x applicazione /// private void createTrayMenu() { // Fix testi menù tray... trayMenu.Items.Clear(); // SE permessa massimizzazione... if (utils.CRB("windowCanMax")) { trayMenu.Items.Add($"Show {nomeApp} App"); } // se è permesso tray close... if (utils.CRB("trayClose")) { trayMenu.Items.Add($"Close {nomeApp} App"); } } private void doConfDownload() { // ciclo su IOB configurati foreach (var currIob in IOB2START) { fileEmbed objFiles = new fileEmbed(); string rawData = ""; string url2call = ""; if (utils.CRB("ConfToCloud")) { // invio su cloud... url2call = $"{urlDownloadFileCloud}{currIob}"; } else { // invio in locale! url2call = $"{urlDownloadFile}{currIob}"; } rawData = utils.callUrlNow(url2call); if (!string.IsNullOrEmpty(rawData)) { // deserializzo objFiles = JsonConvert.DeserializeObject(rawData); if (objFiles != null) { // salvo! foreach (var item in objFiles.fileList) { fileMover.obj.salvaFileString(utils.confDir, item.fileName, item.content); } displayTaskAndLog("Download conf files aggiornati"); } else { displayTaskAndLog($"Error: invalid file data received, deserialized to null ({rawData})"); } } else { displayTaskAndLog("Error: no file data downloaded"); } } } private async void downloadIOBConfToolStripMenuItem_Click(object sender, EventArgs e) { try { displayTaskAndLog("Attesa chiusura Adapters"); // fermo gli IOB closeAllChild(); await Task.Delay(200); // download da su server di TUTTI i file CONF delle IOB doConfDownload(); await Task.Delay(100); // riavvio gli IOB startAdapters(); } catch (Exception exc) { lgError($"Eccezzione in download IOB conf files{Environment.NewLine}{exc}"); } } private void getIOBAssignmentsToolStripMenuItem_Click(object sender, EventArgs e) { // !!!FARE!!! fa un check su server di quali IOB siano assegnate al PC (e modifica il // file MAIN di avvio ripartendo...) } /// /// Carica file ini della configurazione richiesta /// /// private void loadIniFile(string iniConfFile) { displayTaskAndLog($"[STARTUP] Loading iniConfFile: {iniConfFile}"); // da rivedere come AdapterConf x rileggere IobConfTree... FixMe ToDo !!! IniFile fIni = new IniFile(iniConfFile); // salvo valori letti da INI file... branchName = fIni.ReadString("BRANCH", "NAME", "master"); MPIP = fIni.ReadString("SERVER", "MPIP", "ND"); MPURL = fIni.ReadString("SERVER", "MPURL", "/MP/IO"); CMDALIVE = fIni.ReadString("SERVER", "CMDALIVE", "/IOB"); CMDREBO = fIni.ReadString("SERVER", "CMDREBO", "/IOB/sendRebootGateway?GWIP="); CMDIOB2CALL = fIni.ReadString("SERVER", "CMDIOB2CALL", "/IOB/getIob2call?GWIP="); MAXCNC = fIni.ReadInteger("IOB", "MAXCNC", 1); MinDeltaSec = fIni.ReadInteger("IOB", "MinDeltaSec", 1); string STARTLIST = fIni.ReadString("IOB", "STARTLIST", ""); // Gestione IOB da avviare IOB2START = new List(); // SE se sono in modo UPD --> nessuno if (ModoAvvio == StartMode.UPD) { IOB2START.Add("NONE"); } else { string[] elenco = null; // se modalità MAN CERCO se sostituire STARTLIST if (ModoAvvio == StartMode.MAN) { if (!string.IsNullOrEmpty(startParams["IOB"])) { STARTLIST = startParams["IOB"]; } } // se ho qualcosa... if (!string.IsNullOrEmpty(STARTLIST)) { elenco = STARTLIST.Split(','); // inserisco da elenco! foreach (var item in elenco) { IOB2START.Add(item); } } else { IOB2START.Add("NONE"); } } // init redis... string firstIob = "00"; if (IOB2START.Count > 0) { firstIob = IOB2START[0]; } } /// /// evento chiusura /// /// /// private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (!closed) { closeAllChild(); closed = true; } } /// /// evento resize /// /// /// private void MainForm_Resize(object sender, EventArgs e) { checkFormVisibility(); // Verifichiamo se la finestra è appena stata ripristinata if (this.WindowState != FormWindowState.Minimized && _lastState == FormWindowState.Minimized) { displayTaskAndLog("Main Form Restored from Minimized"); } _lastState = this.WindowState; } private FormWindowState _lastState = FormWindowState.Minimized; /// /// evento visualizzazione /// /// /// private void MainForm_Shown(object sender, EventArgs e) { // SOLO SE se sono in modo STD if (ModoAvvio == StartMode.STD) { // avvio minimizzato se richiesto if (utils.CRB("startMinimized")) { // controllo e mando a tray... sendToTray(); } } displayTaskAndLog("Main Form SHOWN (MDI)"); } /// /// timer principale /// /// /// private void MainTimer_Tick(object sender, EventArgs e) { // inizio a riportare che sto funzionando.. advProgBar(); checkCom(); } private void minimizeALLToolStripMenuItem_Click(object sender, EventArgs e) { // ferma tutti i child form... foreach (var ChildForm in this.MdiChildren) { try { ChildForm.WindowState = FormWindowState.Minimized; } catch { } } } private void MShowMainLog_Click(object sender, EventArgs e) { try { string logPath = $"{System.AppDomain.CurrentDomain.BaseDirectory}logs\\MAIN\\{DateTime.Today:yyyy-MM-dd}.log"; Process.Start(logPath); } catch (Exception exc) { lgError($"Eccezione in show log (MAIN):{Environment.NewLine}{exc}"); } } /// /// doppio click su tray icon /// /// /// private void notifyIcon1_DoubleClick(object sender, EventArgs e) { // SOLO SE PERMESSO mostrare full... if (utils.CRB("windowCanMax")) { Show(); WindowState = FormWindowState.Normal; } } private async void restartALLAdaptersToolStripMenuItem_Click(object sender, EventArgs e) { // chiudo closeAllChild(); await Task.Delay(100); // riapro startAdapters(); } private void sendIOBAssignmentsToolStripMenuItem_Click(object sender, EventArgs e) { // !!!FARE!!! salva su server quali IOB siano gestite dal PC (da conf su MAIN...) } /// /// Gestisce "andata nel tray" della form /// private void sendToTray() { if (!notifyIcon1.Visible) { notifyIcon1.BalloonTipTitle = utils.CRS("appName"); notifyIcon1.BalloonTipText = string.Format("{0} running on tray", utils.CRS("appName")); notifyIcon1.Visible = true; notifyIcon1.ShowBalloonTip(100); } Hide(); } private bool ShouldUpdateUI() { // 1. Controlla se la form stessa è minimizzata if (this.WindowState == FormWindowState.Minimized) return false; // 2. Se è una MDI Child, controlla se il Padre è minimizzato if (this.MdiParent != null && this.MdiParent.WindowState == FormWindowState.Minimized) return false; // 3. (Opzionale) Controlla se la form è visibile if (!this.Visible) return false; return true; } private void ShowLogToolStripMenuItem_Click(object sender, EventArgs e) { } /// /// Avvio gli adapters richeisti /// private void startAdapters() { // avvio child richiesti foreach (var item in IOB2START) { openChild(item); } #if false // effettuo upload dei file di configurazione... uploadIobConfFile(); #endif } /// /// click su menù contestuale in tray /// /// /// private void trayMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { lgInfo("Chiamato trayMenu_ItemClicked"); if (e.ClickedItem.Text.StartsWith("Close")) { // stop child adapters... closeAllChild(); // chiudo! Close(); } else if (e.ClickedItem.Text.StartsWith("Show")) { if (utils.CRB("windowCanMax")) { Show(); WindowState = FormWindowState.Normal; } } } private void uploadIOBConfToolStripMenuItem_Click(object sender, EventArgs e) { #if false uploadIobConfFile(); #endif } #endregion Private Methods } }