diff --git a/CVCncLib/CVCncLib.dll b/CVCncLib/CVCncLib.dll index 484da646..3e4e713e 100644 Binary files a/CVCncLib/CVCncLib.dll and b/CVCncLib/CVCncLib.dll differ diff --git a/IOB-WIN/AdapterForm.cs b/IOB-WIN/AdapterForm.cs index 1e0e8604..c19d2d92 100644 --- a/IOB-WIN/AdapterForm.cs +++ b/IOB-WIN/AdapterForm.cs @@ -15,7 +15,7 @@ namespace IOB_WIN { public partial class AdapterForm : Form { - #region inizializzazione contatori + #region Protected Fields /// /// contatore veloce @@ -27,6 +27,16 @@ namespace IOB_WIN /// protected DateTime firstStart; + /// + /// Oggetto ultimo inviato stato IOB x REDIS + /// + protected IobWinStatus lastIobStatus = new IobWinStatus(); + + /// + /// Oggetto ultimo inviato stato MP-IO x REDIS + /// + protected ServerMpStatus lastSrvStatus = new ServerMpStatus(); + /// /// ultimo tentativo riavvio... /// @@ -52,9 +62,14 @@ namespace IOB_WIN /// protected int verySlowCount; - #endregion inizializzazione contatori + /// + /// Temnpo attesa std in MS + /// + protected int waitRecMSec = 30000; - #region inizializzazione oggetti base + #endregion Protected Fields + + #region Public Fields /// /// oggetto logging @@ -86,184 +101,9 @@ namespace IOB_WIN /// public tipoAdapter tipoScelto = tipoAdapter.SIMULA; - /// - /// Oggetto ultimo inviato stato IOB x REDIS - /// - protected IobWinStatus lastIobStatus = new IobWinStatus(); + #endregion Public Fields - /// - /// Oggetto ultimo inviato stato MP-IO x REDIS - /// - protected ServerMpStatus lastSrvStatus = new ServerMpStatus(); - - /// - /// Temnpo attesa std in MS - /// - protected int waitRecMSec = 30000; - - /// - /// Codice IOB della macchina cui connettersi (x scegliere corretto file di conf...) - /// - protected string CurrIOB { get; set; } - - #endregion inizializzazione oggetti base - - #region utils ed helpers - - /// - /// Log verboso da configurazione (SOLO CHAIVE "verbose"... - /// - public bool isVerboseLog { get; set; } = utils.CRB("verbose"); - - /// - /// Logwatcher (in modalità "accodamento in testa" ultimi messaggi...) - /// - public string logWatcher - { - get - { - return lblLogfile.Text; - } - set - { - try - { - logWatchString = limitLine2show($"{value}{Environment.NewLine}{logWatchString}"); - DateTime adesso = DateTime.Now; - if (logWatchWriteVeto < adesso) - { - lblLogfile.Text = logWatchString; - lblLogfile.Refresh(); - logWatchWriteVeto = adesso.AddMilliseconds(delayShowLogMs); - } - } - catch (Exception exc) - { - lgError($"Errore in esecuzione logWatcher{Environment.NewLine}--> {value}"); - if (isVerboseLog) - { - lgError($"{exc}"); - } - } - } - } - - /// - /// Task watcher (in modalità "accodamento in testa" ultimi messaggi...) - /// - public string taskWatcher - { - get - { - return lblTaskLog.Text; - } - set - { - try - { - lblTaskLog.Text = limitLine2show($"{value}{Environment.NewLine}{lblTaskLog.Text}"); - lblTaskLog.Refresh(); - } - catch (Exception exc) - { - lgError($"Errore in esecuzione taskWatcher{Environment.NewLine}--> {value}"); - if (isVerboseLog) - { - lgError($"{exc}"); - } - } - } - } - - protected int delayShowLogMs { get; set; } = utils.CRI("delayShowLogMs"); - - /// - /// Stringa corrente di log... - /// - protected string logWatchString { get; set; } = ""; - - /// - /// Veto a NUOVE scritture in logWatch... - /// - protected DateTime logWatchWriteVeto { get; set; } = DateTime.Now; - - /// - /// mostra un testo sulla status bar + LOG - /// - /// - public void displayTaskAndLog(string txt2show) - { - lblStatus.Text = txt2show; - lblStatus.Invalidate(); - lgInfo(txt2show); - } - - /// - /// Effettua un trim della stringa al numero max di linee da mostrare a video - /// - /// - /// - public string limitLine2show(string newString) - { - if (!string.IsNullOrEmpty(newString)) - { - // se num righe superiore a limite trimmo... - if (newString.Split('\n').Length > nLine2show) - { - //int idx = newString.LastIndexOf('\r'); - int idx = newString.LastIndexOf(Environment.NewLine); - newString = newString.Substring(0, idx); - } - } - return newString; - } - - /// - /// Mostra update delle statistiche di comunicazione (numero chiamate, tempo medio...) - /// - /// - public void updateComStats(string txt2show) - { - lblComStats.Text = string.Format("{0} | ", txt2show); - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgError(string txt2log) - { - if (!string.IsNullOrEmpty(txt2log)) - { - lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB; - lg.Error(txt2log); - // salvo anche in logwatcher... SE non si dimostra ricorsivo... - if (!txt2log.Contains("logWatcher")) - { - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | ERROR | {txt2log}"; - updateFormDisplay(currDispData); - } - } - } - - /// - /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgInfo(string txt2log) - { - lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB; - lg.Info(txt2log); - // salvo anche in logwatcher... - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | INFO | {txt2log}"; - updateFormDisplay(currDispData); - } - - #endregion utils ed helpers - - #region gestione form e visibilità + #region Public Constructors /// /// Avvio MainForm @@ -370,539 +210,13 @@ namespace IOB_WIN displayTaskAndLog("Main Form OK"); } - /// - /// File configurazione default - /// - public string defConfFilePath - { - get - { - return string.Format(@"{0}\{1}.ini", utils.confDir, CurrIOB); - } - } + #endregion Public Constructors - /// - /// Init form (grafica) con helper x testi e varie - /// - protected void myGraphInitForm() - { - lblStatus.Text = "Loading"; - - // aggiunta tooltip x send forzato - ToolTip ttForceSend = new ToolTip(); - - // imposto delay. - ttForceSend.AutoPopDelay = 3000; - ttForceSend.InitialDelay = 500; - ttForceSend.ReshowDelay = 500; - // sempre visibile (che sia o meno attiva la form). - ttForceSend.ShowAlways = true; - - // imposto tooltip - ttForceSend.SetToolTip(this.chkForceDequeue, "Forza invio eventi allo stop"); - } - - /// - /// Fase chiusura Form - /// - /// - /// - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) - { - closeAdapter(); - } - - /// - /// Completato resize form - /// - /// - /// - private void MainForm_Resize(object sender, EventArgs e) - { - } - - /// - /// Mostrata form - /// - /// - /// - private void MainForm_Shown(object sender, EventArgs e) - { - displayTaskAndLog("Main Form SHOWN (Adapter)"); - } - - #endregion gestione form e visibilità - - #region gestione metodi specifici FORM - - /// - /// Salva su file l'oggetto di persistenza dati - /// - /// - public void savePersistLayer(string filePath) - { - // in primis check semaforo salvataggio... - if (!iobObj.adpSaving) - { - // alzo semaforo salvataggio - iobObj.adpSaving = true; - // se HO dei dati... - if (iobObj.persistenceLayer != null) - { - try - { - utils.WritePlain(iobObj.persistenceLayer, filePath); - } - catch (Exception exc) - { - lgError(string.Format("Errore salvataggio file{0}{1}", Environment.NewLine, exc)); - } - } - else - { - lgInfo("persistenceLayer null, non salvato..."); - } - // abbasso semaforo salvataggio - iobObj.adpSaving = false; - } - } - - private void checkFastTask() - { - // decremento... - fastCount--; - // se il counter è a zero eseguo... - if (fastCount <= 0) - { - fastCount = utils.CRI("fastCount"); - - // avvio fase raccolta dati e invio con adapter - iobObj.getAndSend(gatherCycle.HF); - } - } - - private void checkNormTask() - { - // decremento... - normCount--; - // se il counter è a zero eseguo... - if (normCount <= 0) - { - normCount = utils.CRI("normCount"); - - // avvio fase raccolta dati e invio con adapter - iobObj.getAndSend(gatherCycle.MF); - } - } - - private void checkSampleMem() - { - // decremento contatore... - sampleMemCount--; - if (sampleMemCount <= 0) - { - sampleMemCount = utils.CRI("sampleMemCount"); - // avvio fase raccolta dati e invio con adapter - iobObj.saveMemDump(dumpType.SAMPLE); - } - } - - private void checkSendTask() - { - // avvio fase invio con adapter (code MST) - iobObj.getAndSend(gatherCycle.VHF); - } - - private void checkSlowTask() - { - slowCount--; - if (slowCount <= 0) - { - slowCount = utils.CRI("slowCount"); - - // avvio fase raccolta dati e invio con adapter - iobObj.getAndSend(gatherCycle.LF); - } - } - - private void checkVerySlowData() - { - verySlowCount--; - if (verySlowCount <= 0) - { - verySlowCount = utils.CRI("verySlowCount"); - // avvio fase raccolta dati e invio con adapter - iobObj.getAndSend(gatherCycle.VLF); - } - } - - /// - /// Chiusura adapter - /// - private void closeAdapter() - { - fermaTutto(true, false, true, false); - } - - /// - /// Ferma tutti i componenti adapter + update buttons - /// - /// indica se fermare il timer (gather) principale (solo se non si chiude) - /// indica se tentare di riconnettersi - /// indica se sia richeisto di SVUOTARE le code delel info - /// indica se si debba aggiornare la form (no se si sta chiudendo...) - private void fermaTutto(bool stopTimer, bool tryRestart, bool forceDequeue, bool updateForm) - { - iobObj.stopAdapter(tryRestart, forceDequeue); - // salvo! - savePersistLayer(utils.defPersLayerFile); - savePersistLayer(utils.histPersLayerFile); - - stop.Enabled = false; - start.Enabled = true; - restart.Enabled = false; - - if (stopTimer) - { - gather.Enabled = false; - iobObj.tryDisconnect(); - } - - newDisplayData currDispData = new newDisplayData(); - currDispData.semIn = Semaforo.SS; - currDispData.semOut = Semaforo.SS; - if (updateForm) - { - updateFormDisplay(currDispData); - } - } - - private void gather_Tick(object sender, EventArgs e) - { - bool doLog = false; - if (iobObj != null) - { - if (iobObj.periodicLog) - { - doLog = true; - } - try - { - refreshFormData(); - // check esecuzione SendTask (VHF) COMUNQUE... - checkSendTask(); - // eseguo cicli attivi SOLO se adapter è in EFFETTIVO running... - if (iobObj.adpRunning) - { - if (iobObj.connectionOk) - { - // se richiesto faccio memory DUMP INIZIALE! - if (iobObj.doStartMemDump) - { - lgInfo("Inizio dump memoria"); - iobObj.saveMemDump(dumpType.STARTUP); - // fatto! non ripeto... - iobObj.doStartMemDump = false; - lgInfo("Finito dump memoria"); - } - // controllo se sia abilitato sampleDump della meoria (periodico) - if (iobObj.doSampleMemory) - { - checkSampleMem(); - } - // check esecuzione FastTask - checkFastTask(); - // check esecuzione NormTask - checkNormTask(); - // check esecuzione SlowTask - checkSlowTask(); - // check esecuzione AlarmSync - checkVerySlowData(); - if (utils.CRI("waitEndCycle") > 0) - { - Thread.Sleep(utils.CRI("waitEndCycle")); - } - } - else - { - DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec); - if (iobObj.adpTryRestart && (DateTime.Now > dtVeto)) - { - if (doLog) - { - lgInfo($"Retry Time Elapsed ({waitRecMSec} ms)--> tryConnect"); - } - lastStartTry = DateTime.Now; - iobObj.tryConnect(); - } - } - } - else - { - if (doLog) - { - lgInfo("Adapter stopped"); - } - // verifico SE debba tentare il riavvio, ovvero NON running ma adpTryRestart e non ho riprovato x oltre waitRecMSec - DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec); - if (iobObj.adpTryRestart && (DateTime.Now > dtVeto)) - { - lastStartTry = DateTime.Now; - avviaAdapter(chkForceDequeue.Checked); - } - } - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in fase di gatherTick: {0}{1}", Environment.NewLine, exc)); - } - } - } - - /// - /// Carica file ini della configurazione richiesta - /// - /// - private void loadIniFile(string iniConfFile) - { - // out di cosa faccio... - displayTaskAndLog(string.Format("Loading iniConfFile: {0}", iniConfFile)); - // leggo file - IniFile fIni = new IniFile(iniConfFile); - - // leggo vendor e modello... - curVendor = fIni.ReadString("MACHINE", "VENDOR", "ACME"); - curModel = fIni.ReadString("MACHINE", "MODEL", "NONE"); - // verifico tipo adapter - try - { - tipoScelto = (tipoAdapter)Enum.Parse(typeof(tipoAdapter), fIni.ReadString("IOB", "CNCTYPE", "DEMO")); - } - catch (Exception exc) - { - lgError($"Eccezione in conversione tipo adapter: richiesto un tipo non codificato...{Environment.NewLine}{exc}"); - tipoScelto = tipoAdapter.ND; - } - // carivo vettore parametri opzionai - Dictionary optParRead = new Dictionary(); - string[] optParRows = fIni.ReadSection("OPTPAR"); - if (optParRows.Length > 0) - { - try - { - string[] kvp; - foreach (var item in optParRows) - { - kvp = item.Split('='); - optParRead.Add(kvp[0], kvp[1]); - } - lgInfo($"Caricati {optParRead.Count} parametri opzionali da OPTPAR"); - } - catch (Exception exc) - { - lgError(string.Format("EXCEPTION in fase di lettura OPTPAR: {0}{1}", Environment.NewLine, exc)); - } - } - // inizializzio conf IOB - IOBConf = new IobConfiguration - { - pingMsTimeout = fIni.ReadInteger("IOB", "PING_MS_TIMEOUT", 500) - , - vendor = curVendor - , - model = curModel - , - tipoIob = tipoScelto - , - optPar = optParRead - , - versIOB = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() - , - codIOB = CurrIOB - , - cncIpAddr = fIni.ReadString("CNC", "IP", "::1") - , - cncPort = fIni.ReadString("CNC", "PORT", "0") - , - iniFileName = iniConfFile - , - cpuType = fIni.ReadString("CNC", "CPUTYPE", "") - , - rack = (short)fIni.ReadInteger("CNC", "RACK", 0) - , - slot = (short)fIni.ReadInteger("CNC", "SLOT", 0) - , - serverData = new serverMapo(fIni.ReadString("SERVER", "MPIP", "::1"), fIni.ReadString("SERVER", "MPURL", "/"), fIni.ReadString("SERVER", "CMDBASE", "/"), fIni.ReadString("SERVER", "CMDFLOG", "/"), fIni.ReadString("SERVER", "CMDALIVE", "/"), fIni.ReadString("SERVER", "CMDENABLED", "/"), fIni.ReadString("SERVER", "CMDREBO", "/"), fIni.ReadString("SERVER", "CMD_ODL_STARTED", "/IOB/getCurrOdlStart/")) - , - MAX_COUNTER_BLINK = Convert.ToInt32(fIni.ReadString("BLINK", "MAX_COUNTER_BLINK", "1")) - , - BLINK_FILT = Convert.ToInt32(fIni.ReadString("BLINK", "BLINK_FILT", "0")) - , - TCMaxDelayFactor = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_TC_FACTOR", "1.2").Replace(".", ",")) - , - TCLambda = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_LAMBDA", "0.5").Replace(".", ",")) - , - TCMaxIncrPz = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_INCR", "5").Replace(".", ",")) - }; - lgInfo($"Creato IOBConf!"); - - loadIobType(); - // avvio macchina con adapter specificato... - if (utils.CRB("autoStartOnLoad")) - { - displayTaskAndLog("Auto Starting..."); - // avvio! - avviaAdapter(chkForceDequeue.Checked); - displayTaskAndLog("Auto Started!"); - } - try - { - // segnalo reboot (programma)... metto in coda invio... - //iobObj.sendToMoonPro(urlType.FLog, "IOB INI Loaded"); - //iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB INI Loaded")); - } - catch (Exception exc) - { - lgError(string.Format("EXCEPTION in fase di chiamata URL di segnalazione caricamento INI file:{0}{1}", Environment.NewLine, exc)); - } - } - - private void refreshFormData() - { - // aggiorno visualizzazioni varie in form... - evQueueLen = iobObj.QueueIN.Count; - flQueueLen = iobObj.QueueFLog.Count; - alQueueLen = iobObj.QueueAlarm.Count; - msQueueLen = iobObj.QueueMessages.Count; - // aggiorno labels counters... - counterIob = $"pz IOB {iobObj.contapezziIOB}"; - counterMac = $"pz PLC {iobObj.contapezziPLC}"; - // verifico IOB status - IobWinStatus currIobStatus = new IobWinStatus() - { - CodIob = iobObj.cIobConf.codIOB, - queueEvLen = evQueueLen, - queueFlLen = flQueueLen, - queueAlLen = alQueueLen, - queueMsLen = msQueueLen, - counterIOB = iobObj.contapezziIOB, - counterMAC = iobObj.contapezziPLC, - lastUpdate = lastIobStatus.lastUpdate, - online = utils.IOB_Online, - lastDataIn = iobObj.lastReadPLC - }; - // se diverso SALVO! - if (lastIobStatus.online != currIobStatus.online || lastIobStatus.lastDataIn != currIobStatus.lastDataIn || lastIobStatus.counterIOB != currIobStatus.counterIOB || lastIobStatus.counterMAC != currIobStatus.counterMAC || lastIobStatus.queueEvLen != currIobStatus.queueEvLen || lastIobStatus.queueFlLen != currIobStatus.queueFlLen || lastIobStatus.queueAlLen != currIobStatus.queueAlLen || lastIobStatus.queueMsLen != currIobStatus.queueMsLen) - { - // aggiorno data - currIobStatus.lastUpdate = DateTime.Now; - // salvo su redis e in obj corrente - iobObj.redisMan.iobStatus = currIobStatus; - lastIobStatus = currIobStatus; - } - // se diverso SALVO MP IO status - if (lastSrvStatus.online != utils.MPIO_Online) - { - // aggiorno - ServerMpStatus currSrvStatus = iobObj.redisMan.servStatus; - currSrvStatus.online = utils.MPIO_Online; - currSrvStatus.lastUpdate = DateTime.Now; - // salvo su redis e in obj corrente - iobObj.redisMan.servStatus = currSrvStatus; - lastSrvStatus = currSrvStatus; - } - } - - #endregion gestione metodi specifici FORM - - #region Area BackGroundWorker - - /// - /// Effettua update form una volta ricevuto OBJ che contiene le varie innovazioni... - /// - /// - public void updateFormDisplay(newDisplayData currDispData) - { - // ciclo x ogni oggetto x caricare le innovazioni... - if (currDispData != null && currDispData.hasData) - { - // RealTime display - if (!string.IsNullOrWhiteSpace(currDispData.newInData)) - { - dataMonitor_0 = limitLine2show($"{currDispData.newInData}{Environment.NewLine}{dataMonitor_0}"); - } - if (!string.IsNullOrWhiteSpace(currDispData.newSignalData)) - { - dataMonitor_1 = limitLine2show($"{currDispData.newSignalData}{Environment.NewLine}{dataMonitor_1}"); - } - if (!string.IsNullOrWhiteSpace(currDispData.newFLogData)) - { - dataMonitor_3 = limitLine2show($"{currDispData.newFLogData}{Environment.NewLine}{dataMonitor_3}"); - } - if (!string.IsNullOrWhiteSpace(currDispData.newUrlCallData)) - { - dataMonitor_2 = limitLine2show($"{currDispData.newUrlCallData}{Environment.NewLine}{dataMonitor_2}"); - } - - // Bitmap lettura attuale - if (currDispData.counter >= 0) - { - lblCounter.Text = currDispData.counter.ToString(); - } - // Bitmap lettura attuale - if (!string.IsNullOrWhiteSpace(currDispData.currBitmap)) - { - lblBitmap.Text = currDispData.currBitmap; - lblBitmap.Refresh(); - } - // LiveLog - if (!string.IsNullOrWhiteSpace(currDispData.newLiveLogData)) - { - logWatcher = currDispData.newLiveLogData; - } - // semafori - if (currDispData.semOut != Semaforo.ND) - { - //aggiorno comunque! - sOUT = currDispData.semOut; - } - if (currDispData.semIn != Semaforo.ND) - { - //aggiorno comunque! - sIN = currDispData.semIn; - } - } - } - - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #endregion Area BackGroundWorker - - #region Area gestione controlli threadSafe + #region Private Delegates private delegate void SafeCallDelegate(string text); - public void WriteTextSafe(string text) - { - if (lblOutMessage3.InvokeRequired) - { - var d = new SafeCallDelegate(WriteTextSafe); - lblOutMessage3.Invoke(d, new object[] { text }); - } - else - { - lblOutMessage3.Text = text; - } - } - - #endregion Area gestione controlli threadSafe + #endregion Private Delegates #region Protected Properties @@ -926,6 +240,23 @@ namespace IOB_WIN /// protected Semaforo _sOUT { get; set; } = Semaforo.ND; + /// + /// Codice IOB della macchina cui connettersi (x scegliere corretto file di conf...) + /// + protected string CurrIOB { get; set; } + + protected int delayShowLogMs { get; set; } = utils.CRI("delayShowLogMs"); + + /// + /// Stringa corrente di log... + /// + protected string logWatchString { get; set; } = ""; + + /// + /// Veto a NUOVE scritture in logWatch... + /// + protected DateTime logWatchWriteVeto { get; set; } = DateTime.Now; + protected int maxAlQueue { get; set; } protected int maxEvQueue { get; set; } @@ -952,6 +283,8 @@ namespace IOB_WIN #endregion Protected Properties + #region Public Properties + public int alQueueLen { set @@ -1130,6 +463,17 @@ namespace IOB_WIN } } + /// + /// File configurazione default + /// + public string defConfFilePath + { + get + { + return string.Format(@"{0}\{1}.ini", utils.confDir, CurrIOB); + } + } + public bool enableEditMes2Plc { get; set; } public int evQueueLen @@ -1182,6 +526,44 @@ namespace IOB_WIN } } + /// + /// Log verboso da configurazione (SOLO CHAIVE "verbose"... + /// + public bool isVerboseLog { get; set; } = utils.CRB("verbose"); + + /// + /// Logwatcher (in modalità "accodamento in testa" ultimi messaggi...) + /// + public string logWatcher + { + get + { + return lblLogfile.Text; + } + set + { + try + { + logWatchString = limitLine2show($"{value}{Environment.NewLine}{logWatchString}"); + DateTime adesso = DateTime.Now; + if (logWatchWriteVeto < adesso) + { + lblLogfile.Text = logWatchString; + lblLogfile.Refresh(); + logWatchWriteVeto = adesso.AddMilliseconds(delayShowLogMs); + } + } + catch (Exception exc) + { + lgError($"Errore in esecuzione logWatcher{Environment.NewLine}--> {value}"); + if (isVerboseLog) + { + lgError($"{exc}"); + } + } + } + } + public int msQueueLen { set @@ -1241,8 +623,12 @@ namespace IOB_WIN set { _sIN = value; - bIN.BackColor = decSemaforo(value); - bIN.Refresh(); + var newColor = decSemaforo(value); + if (newColor != bIN.BackColor) + { + bIN.BackColor = newColor; + bIN.Refresh(); + } } } @@ -1258,107 +644,45 @@ namespace IOB_WIN set { _sOUT = value; - bOUT.BackColor = decSemaforo(value); - bOUT.Refresh(); + var newColor = decSemaforo(value); + if (newColor != bOUT.BackColor) + { + bOUT.BackColor = newColor; + bOUT.Refresh(); + } } } /// - /// Decodifica colore da valore semaforico + /// Task watcher (in modalità "accodamento in testa" ultimi messaggi...) /// - /// - /// - public static Color decSemaforo(Semaforo valore) + public string taskWatcher { - Color colore = Color.LightGray; - switch (valore) + get { - case Semaforo.SV: - colore = Color.LightGreen; - break; - - case Semaforo.SG: - colore = Color.LightGoldenrodYellow; - break; - - case Semaforo.SR: - colore = Color.Red; - break; - - case Semaforo.SS: - colore = Color.DarkGray; - break; - - default: - colore = Color.LightGray; - break; + return lblTaskLog.Text; } - return colore; - } - - /// - /// Avvio l'adapter - /// - /// indica se sia richeisto di SVUOTARE le code delel info - public void avviaAdapter(bool resetQueue) - { - displayTaskAndLog("Adapter starting"); - // se NON sta girando... - if (!iobObj.adpRunning) + set { - iobObj.startAdapter(resetQueue); - displayTaskAndLog("Adapter started!"); - - // fix buttons start/stop/dump - start.Enabled = false; - stop.Enabled = true; - restart.Enabled = true; - - displayTaskAndLog("Start Timers"); - // inizializzo contatori fast/mid/slow - fastCount = utils.CRI("fastCount"); - normCount = utils.CRI("normCount"); - slowCount = utils.CRI("slowCount"); - verySlowCount = utils.CRI("verySlowCount"); - displayTaskAndLog("Adapter Running..."); - // init max queue - maxEvQueue = 1; - maxFlQueue = 1; - try { - // segnalo reboot (programma)... - iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB Started")); + lblTaskLog.Text = limitLine2show($"{value}{Environment.NewLine}{lblTaskLog.Text}"); + lblTaskLog.Refresh(); } catch (Exception exc) { - lgError(string.Format("EXCEPTION in fase di chiamata URL di segnalazione AVVIO IOB:{0}{1}", Environment.NewLine, exc)); + lgError($"Errore in esecuzione taskWatcher{Environment.NewLine}--> {value}"); + if (isVerboseLog) + { + lgError($"{exc}"); + } } } - else - { - displayTaskAndLog("Adapter STILL Running..."); - } } - /// - /// Ferma l'adapter - /// - /// determina se si debba tentare riavvio automatico (per caduta connessione) - /// indica se sia richeisto di SVUOTARE le code delel info - /// indica se aggiornare la form prima di fermare - public void fermaAdapter(bool tryRestart, bool forceDequeue, bool updateForm) - { - fermaTutto(false, tryRestart, forceDequeue, updateForm); - } + #endregion Public Properties - /// - /// MOstra info su coda complessiva - /// - protected void showQueueData() - { - lblQueueLenTop.Text = totQueue == 0 ? "realtime" : $"ev: {qEvLen} | flog: {qFlLen} | tot: {totQueue}"; - } + #region Private Methods private void BtnOpenLog_Click(object sender, EventArgs e) { @@ -1413,15 +737,125 @@ namespace IOB_WIN } } + private void checkFastTask() + { + // decremento... + fastCount--; + // se il counter è a zero eseguo... + if (fastCount <= 0) + { + fastCount = utils.CRI("fastCount"); + + // avvio fase raccolta dati e invio con adapter + iobObj.getAndSend(gatherCycle.HF); + } + } + + private void checkNormTask() + { + // decremento... + normCount--; + // se il counter è a zero eseguo... + if (normCount <= 0) + { + normCount = utils.CRI("normCount"); + + // avvio fase raccolta dati e invio con adapter + iobObj.getAndSend(gatherCycle.MF); + } + } + + private void checkSampleMem() + { + // decremento contatore... + sampleMemCount--; + if (sampleMemCount <= 0) + { + sampleMemCount = utils.CRI("sampleMemCount"); + // avvio fase raccolta dati e invio con adapter + iobObj.saveMemDump(dumpType.SAMPLE); + } + } + + private void checkSendTask() + { + // avvio fase invio con adapter (code MST) + iobObj.getAndSend(gatherCycle.VHF); + } + + private void checkSlowTask() + { + slowCount--; + if (slowCount <= 0) + { + slowCount = utils.CRI("slowCount"); + + // avvio fase raccolta dati e invio con adapter + iobObj.getAndSend(gatherCycle.LF); + } + } + + private void checkVerySlowData() + { + verySlowCount--; + if (verySlowCount <= 0) + { + verySlowCount = utils.CRI("verySlowCount"); + // avvio fase raccolta dati e invio con adapter + iobObj.getAndSend(gatherCycle.VLF); + } + } + private void ChkEdit_CheckedChanged(object sender, EventArgs e) { toggleEditMes2Plc(); } + /// + /// Chiusura adapter + /// + private void closeAdapter() + { + fermaTutto(true, false, true, false); + } + private void displTimer_Tick(object sender, EventArgs e) { } + /// + /// Ferma tutti i componenti adapter + update buttons + /// + /// indica se fermare il timer (gather) principale (solo se non si chiude) + /// indica se tentare di riconnettersi + /// indica se sia richeisto di SVUOTARE le code delel info + /// indica se si debba aggiornare la form (no se si sta chiudendo...) + private void fermaTutto(bool stopTimer, bool tryRestart, bool forceDequeue, bool updateForm) + { + iobObj.stopAdapter(tryRestart, forceDequeue); + // salvo! + savePersistLayer(utils.defPersLayerFile); + savePersistLayer(utils.histPersLayerFile); + + stop.Enabled = false; + start.Enabled = true; + restart.Enabled = false; + + if (stopTimer) + { + gather.Enabled = false; + iobObj.tryDisconnect(); + } + + newDisplayData currDispData = new newDisplayData(); + currDispData.semIn = Semaforo.SS; + currDispData.semOut = Semaforo.SS; + if (updateForm) + { + updateFormDisplay(currDispData); + } + } + private void fixComboParameters() { if (iobObj != null) @@ -1444,6 +878,88 @@ namespace IOB_WIN } } + private void gather_Tick(object sender, EventArgs e) + { + bool doLog = false; + if (iobObj != null) + { + if (iobObj.periodicLog) + { + doLog = true; + } + try + { + refreshFormData(); + // check esecuzione SendTask (VHF) COMUNQUE... + checkSendTask(); + // eseguo cicli attivi SOLO se adapter è in EFFETTIVO running... + if (iobObj.adpRunning) + { + if (iobObj.connectionOk) + { + // se richiesto faccio memory DUMP INIZIALE! + if (iobObj.doStartMemDump) + { + lgInfo("Inizio dump memoria"); + iobObj.saveMemDump(dumpType.STARTUP); + // fatto! non ripeto... + iobObj.doStartMemDump = false; + lgInfo("Finito dump memoria"); + } + // controllo se sia abilitato sampleDump della meoria (periodico) + if (iobObj.doSampleMemory) + { + checkSampleMem(); + } + // check esecuzione FastTask + checkFastTask(); + // check esecuzione NormTask + checkNormTask(); + // check esecuzione SlowTask + checkSlowTask(); + // check esecuzione AlarmSync + checkVerySlowData(); + if (utils.CRI("waitEndCycle") > 0) + { + Thread.Sleep(utils.CRI("waitEndCycle")); + } + } + else + { + DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec); + if (iobObj.adpTryRestart && (DateTime.Now > dtVeto)) + { + if (doLog) + { + lgInfo($"Retry Time Elapsed ({waitRecMSec} ms)--> tryConnect"); + } + lastStartTry = DateTime.Now; + iobObj.tryConnect(); + } + } + } + else + { + if (doLog) + { + lgInfo("Adapter stopped"); + } + // verifico SE debba tentare il riavvio, ovvero NON running ma adpTryRestart e non ho riprovato x oltre waitRecMSec + DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec); + if (iobObj.adpTryRestart && (DateTime.Now > dtVeto)) + { + lastStartTry = DateTime.Now; + avviaAdapter(chkForceDequeue.Checked); + } + } + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in fase di gatherTick: {0}{1}", Environment.NewLine, exc)); + } + } + } + /// /// GEstione evento refresh /// @@ -1455,6 +971,114 @@ namespace IOB_WIN updateFormDisplay(e.DisplayDataObject); } + /// + /// Carica file ini della configurazione richiesta + /// + /// + private void loadIniFile(string iniConfFile) + { + // out di cosa faccio... + displayTaskAndLog(string.Format("Loading iniConfFile: {0}", iniConfFile)); + // leggo file + IniFile fIni = new IniFile(iniConfFile); + + // leggo vendor e modello... + curVendor = fIni.ReadString("MACHINE", "VENDOR", "ACME"); + curModel = fIni.ReadString("MACHINE", "MODEL", "NONE"); + // verifico tipo adapter + try + { + tipoScelto = (tipoAdapter)Enum.Parse(typeof(tipoAdapter), fIni.ReadString("IOB", "CNCTYPE", "DEMO")); + } + catch (Exception exc) + { + lgError($"Eccezione in conversione tipo adapter: richiesto un tipo non codificato...{Environment.NewLine}{exc}"); + tipoScelto = tipoAdapter.ND; + } + // carivo vettore parametri opzionai + Dictionary optParRead = new Dictionary(); + string[] optParRows = fIni.ReadSection("OPTPAR"); + if (optParRows.Length > 0) + { + try + { + string[] kvp; + foreach (var item in optParRows) + { + kvp = item.Split('='); + optParRead.Add(kvp[0], kvp[1]); + } + lgInfo($"Caricati {optParRead.Count} parametri opzionali da OPTPAR"); + } + catch (Exception exc) + { + lgError(string.Format("EXCEPTION in fase di lettura OPTPAR: {0}{1}", Environment.NewLine, exc)); + } + } + // inizializzio conf IOB + IOBConf = new IobConfiguration + { + pingMsTimeout = fIni.ReadInteger("IOB", "PING_MS_TIMEOUT", 500) + , + vendor = curVendor + , + model = curModel + , + tipoIob = tipoScelto + , + optPar = optParRead + , + versIOB = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + , + codIOB = CurrIOB + , + cncIpAddr = fIni.ReadString("CNC", "IP", "::1") + , + cncPort = fIni.ReadString("CNC", "PORT", "0") + , + iniFileName = iniConfFile + , + cpuType = fIni.ReadString("CNC", "CPUTYPE", "") + , + rack = (short)fIni.ReadInteger("CNC", "RACK", 0) + , + slot = (short)fIni.ReadInteger("CNC", "SLOT", 0) + , + serverData = new serverMapo(fIni.ReadString("SERVER", "MPIP", "::1"), fIni.ReadString("SERVER", "MPURL", "/"), fIni.ReadString("SERVER", "CMDBASE", "/"), fIni.ReadString("SERVER", "CMDFLOG", "/"), fIni.ReadString("SERVER", "CMDALIVE", "/"), fIni.ReadString("SERVER", "CMDENABLED", "/"), fIni.ReadString("SERVER", "CMDREBO", "/"), fIni.ReadString("SERVER", "CMD_ODL_STARTED", "/IOB/getCurrOdlStart/")) + , + MAX_COUNTER_BLINK = Convert.ToInt32(fIni.ReadString("BLINK", "MAX_COUNTER_BLINK", "1")) + , + BLINK_FILT = Convert.ToInt32(fIni.ReadString("BLINK", "BLINK_FILT", "0")) + , + TCMaxDelayFactor = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_TC_FACTOR", "1.2").Replace(".", ",")) + , + TCLambda = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_LAMBDA", "0.5").Replace(".", ",")) + , + TCMaxIncrPz = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_INCR", "5").Replace(".", ",")) + }; + lgInfo($"Creato IOBConf!"); + + loadIobType(); + // avvio macchina con adapter specificato... + if (utils.CRB("autoStartOnLoad")) + { + displayTaskAndLog("Auto Starting..."); + // avvio! + avviaAdapter(chkForceDequeue.Checked); + displayTaskAndLog("Auto Started!"); + } + try + { + // segnalo reboot (programma)... metto in coda invio... + //iobObj.sendToMoonPro(urlType.FLog, "IOB INI Loaded"); + //iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB INI Loaded")); + } + catch (Exception exc) + { + lgError(string.Format("EXCEPTION in fase di chiamata URL di segnalazione caricamento INI file:{0}{1}", Environment.NewLine, exc)); + } + } + /// /// carica IOB richiesto /// @@ -1583,6 +1207,35 @@ namespace IOB_WIN displayTaskAndLog(string.Format("Caricata conf per adapter {0}", tipoScelto)); } + /// + /// Fase chiusura Form + /// + /// + /// + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + closeAdapter(); + } + + /// + /// Completato resize form + /// + /// + /// + private void MainForm_Resize(object sender, EventArgs e) + { + } + + /// + /// Mostrata form + /// + /// + /// + private void MainForm_Shown(object sender, EventArgs e) + { + displayTaskAndLog("Main Form SHOWN (Adapter)"); + } + private void mLoadConf_Click(object sender, EventArgs e) { // mostro selettore file x leggere adapter.. @@ -1608,6 +1261,52 @@ namespace IOB_WIN } } + private void refreshFormData() + { + // aggiorno visualizzazioni varie in form... + evQueueLen = iobObj.QueueIN.Count; + flQueueLen = iobObj.QueueFLog.Count; + alQueueLen = iobObj.QueueAlarm.Count; + msQueueLen = iobObj.QueueMessages.Count; + // aggiorno labels counters... + counterIob = $"pz IOB {iobObj.contapezziIOB}"; + counterMac = $"pz PLC {iobObj.contapezziPLC}"; + // verifico IOB status + IobWinStatus currIobStatus = new IobWinStatus() + { + CodIob = iobObj.cIobConf.codIOB, + queueEvLen = evQueueLen, + queueFlLen = flQueueLen, + queueAlLen = alQueueLen, + queueMsLen = msQueueLen, + counterIOB = iobObj.contapezziIOB, + counterMAC = iobObj.contapezziPLC, + lastUpdate = lastIobStatus.lastUpdate, + online = utils.IOB_Online, + lastDataIn = iobObj.lastReadPLC + }; + // se diverso SALVO! + if (lastIobStatus.online != currIobStatus.online || lastIobStatus.lastDataIn != currIobStatus.lastDataIn || lastIobStatus.counterIOB != currIobStatus.counterIOB || lastIobStatus.counterMAC != currIobStatus.counterMAC || lastIobStatus.queueEvLen != currIobStatus.queueEvLen || lastIobStatus.queueFlLen != currIobStatus.queueFlLen || lastIobStatus.queueAlLen != currIobStatus.queueAlLen || lastIobStatus.queueMsLen != currIobStatus.queueMsLen) + { + // aggiorno data + currIobStatus.lastUpdate = DateTime.Now; + // salvo su redis e in obj corrente + iobObj.redisMan.iobStatus = currIobStatus; + lastIobStatus = currIobStatus; + } + // se diverso SALVO MP IO status + if (lastSrvStatus.online != utils.MPIO_Online) + { + // aggiorno + ServerMpStatus currSrvStatus = iobObj.redisMan.servStatus; + currSrvStatus.online = utils.MPIO_Online; + currSrvStatus.lastUpdate = DateTime.Now; + // salvo su redis e in obj corrente + iobObj.redisMan.servStatus = currSrvStatus; + lastSrvStatus = currSrvStatus; + } + } + /// /// Button restart /// @@ -1692,5 +1391,318 @@ namespace IOB_WIN enableEditMes2Plc = !enableEditMes2Plc; checkEditMes2Plc(); } + + #endregion Private Methods + + #region Protected Methods + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgError(string txt2log) + { + if (!string.IsNullOrEmpty(txt2log)) + { + lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB; + lg.Error(txt2log); + // salvo anche in logwatcher... SE non si dimostra ricorsivo... + if (!txt2log.Contains("logWatcher")) + { + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | ERROR | {txt2log}"; + updateFormDisplay(currDispData); + } + } + } + + /// + /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgInfo(string txt2log) + { + lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB; + lg.Info(txt2log); + // salvo anche in logwatcher... + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | INFO | {txt2log}"; + updateFormDisplay(currDispData); + } + + /// + /// Init form (grafica) con helper x testi e varie + /// + protected void myGraphInitForm() + { + lblStatus.Text = "Loading"; + + // aggiunta tooltip x send forzato + ToolTip ttForceSend = new ToolTip(); + + // imposto delay. + ttForceSend.AutoPopDelay = 3000; + ttForceSend.InitialDelay = 500; + ttForceSend.ReshowDelay = 500; + // sempre visibile (che sia o meno attiva la form). + ttForceSend.ShowAlways = true; + + // imposto tooltip + ttForceSend.SetToolTip(this.chkForceDequeue, "Forza invio eventi allo stop"); + } + + /// + /// MOstra info su coda complessiva + /// + protected void showQueueData() + { + lblQueueLenTop.Text = totQueue == 0 ? "realtime" : $"ev: {qEvLen} | flog: {qFlLen} | tot: {totQueue}"; + } + + #endregion Protected Methods + + #region Public Methods + + /// + /// Decodifica colore da valore semaforico + /// + /// + /// + public static Color decSemaforo(Semaforo valore) + { + Color colore = Color.LightGray; + switch (valore) + { + case Semaforo.SV: + colore = Color.LightGreen; + break; + + case Semaforo.SG: + colore = Color.LightGoldenrodYellow; + break; + + case Semaforo.SR: + colore = Color.Red; + break; + + case Semaforo.SS: + colore = Color.DarkGray; + break; + + default: + colore = Color.LightGray; + break; + } + return colore; + } + + /// + /// Avvio l'adapter + /// + /// indica se sia richeisto di SVUOTARE le code delel info + public void avviaAdapter(bool resetQueue) + { + displayTaskAndLog("Adapter starting"); + // se NON sta girando... + if (!iobObj.adpRunning) + { + iobObj.startAdapter(resetQueue); + displayTaskAndLog("Adapter started!"); + + // fix buttons start/stop/dump + start.Enabled = false; + stop.Enabled = true; + restart.Enabled = true; + + displayTaskAndLog("Start Timers"); + // inizializzo contatori fast/mid/slow + fastCount = utils.CRI("fastCount"); + normCount = utils.CRI("normCount"); + slowCount = utils.CRI("slowCount"); + verySlowCount = utils.CRI("verySlowCount"); + displayTaskAndLog("Adapter Running..."); + // init max queue + maxEvQueue = 1; + maxFlQueue = 1; + + try + { + // segnalo reboot (programma)... + iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB Started")); + } + catch (Exception exc) + { + lgError(string.Format("EXCEPTION in fase di chiamata URL di segnalazione AVVIO IOB:{0}{1}", Environment.NewLine, exc)); + } + } + else + { + displayTaskAndLog("Adapter STILL Running..."); + } + } + + /// + /// mostra un testo sulla status bar + LOG + /// + /// + public void displayTaskAndLog(string txt2show) + { + lblStatus.Text = txt2show; + lblStatus.Invalidate(); + lgInfo(txt2show); + } + + /// + /// Ferma l'adapter + /// + /// determina se si debba tentare riavvio automatico (per caduta connessione) + /// indica se sia richeisto di SVUOTARE le code delel info + /// indica se aggiornare la form prima di fermare + public void fermaAdapter(bool tryRestart, bool forceDequeue, bool updateForm) + { + fermaTutto(false, tryRestart, forceDequeue, updateForm); + } + + /// + /// Effettua un trim della stringa al numero max di linee da mostrare a video + /// + /// + /// + public string limitLine2show(string newString) + { + if (!string.IsNullOrEmpty(newString)) + { + // se num righe superiore a limite trimmo... + if (newString.Split('\n').Length > nLine2show) + { + //int idx = newString.LastIndexOf('\r'); + int idx = newString.LastIndexOf(Environment.NewLine); + newString = newString.Substring(0, idx); + } + } + return newString; + } + + /// + /// Salva su file l'oggetto di persistenza dati + /// + /// + public void savePersistLayer(string filePath) + { + // in primis check semaforo salvataggio... + if (!iobObj.adpSaving) + { + // alzo semaforo salvataggio + iobObj.adpSaving = true; + // se HO dei dati... + if (iobObj.persistenceLayer != null) + { + try + { + utils.WritePlain(iobObj.persistenceLayer, filePath); + } + catch (Exception exc) + { + lgError(string.Format("Errore salvataggio file{0}{1}", Environment.NewLine, exc)); + } + } + else + { + lgInfo("persistenceLayer null, non salvato..."); + } + // abbasso semaforo salvataggio + iobObj.adpSaving = false; + } + } + + /// + /// Mostra update delle statistiche di comunicazione (numero chiamate, tempo medio...) + /// + /// + public void updateComStats(string txt2show) + { + lblComStats.Text = string.Format("{0} | ", txt2show); + } + + /// + /// Effettua update form una volta ricevuto OBJ che contiene le varie innovazioni... + /// + /// + public void updateFormDisplay(newDisplayData currDispData) + { + // ciclo x ogni oggetto x caricare le innovazioni... + if (currDispData != null && currDispData.hasData) + { + // RealTime display + if (!string.IsNullOrWhiteSpace(currDispData.newInData)) + { + dataMonitor_0 = limitLine2show($"{currDispData.newInData}{Environment.NewLine}{dataMonitor_0}"); + } + if (!string.IsNullOrWhiteSpace(currDispData.newSignalData)) + { + dataMonitor_1 = limitLine2show($"{currDispData.newSignalData}{Environment.NewLine}{dataMonitor_1}"); + } + if (!string.IsNullOrWhiteSpace(currDispData.newFLogData)) + { + dataMonitor_3 = limitLine2show($"{currDispData.newFLogData}{Environment.NewLine}{dataMonitor_3}"); + } + if (!string.IsNullOrWhiteSpace(currDispData.newUrlCallData)) + { + dataMonitor_2 = limitLine2show($"{currDispData.newUrlCallData}{Environment.NewLine}{dataMonitor_2}"); + } + + // Bitmap lettura attuale + if (currDispData.counter >= 0) + { + lblCounter.Text = currDispData.counter.ToString(); + } + // Bitmap lettura attuale + if (!string.IsNullOrWhiteSpace(currDispData.currBitmap)) + { + lblBitmap.Text = currDispData.currBitmap; + lblBitmap.Refresh(); + } + // LiveLog + if (!string.IsNullOrWhiteSpace(currDispData.newLiveLogData)) + { + logWatcher = currDispData.newLiveLogData; + } + // semafori + if (currDispData.semOut != Semaforo.ND) + { + //aggiorno comunque! + sOUT = currDispData.semOut; + } + if (currDispData.semIn != Semaforo.ND) + { + //aggiorno comunque! + sIN = currDispData.semIn; + } + } + } + + public void WriteTextSafe(string text) + { + if (lblOutMessage3.InvokeRequired) + { + var d = new SafeCallDelegate(WriteTextSafe); + lblOutMessage3.Invoke(d, new object[] { text }); + } + else + { + lblOutMessage3.Text = text; + } + } + + #endregion Public Methods } } \ No newline at end of file diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 0b7db10f..e2661221 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -21,16 +21,113 @@ namespace IOB_WIN { public class IobGeneric { - #region variabili serializzate in REDIS + #region Protected Fields /// - /// Oggetto connessione REDIS + /// wrapper di log /// - public RedisIobCache redisMan; + protected static Logger lg; - #endregion variabili serializzate in REDIS + protected bool _connOk = false; - #region variabili ed oggetti base + /// + /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC + /// + protected bool adpCommAct; + + /// + /// porta x adapter (x restart) + /// + protected int adpPortNum; + + /// + /// DataOra ultimo avvio adapter x watchdog + /// + protected DateTime adpStartRun; + + /// + /// Vettore 32 BIT valori in ingresso al filtro + /// + protected int B_input; + + /// + /// Vettore 32 BIT valori in uscita dal filtro + /// + protected int B_output; + + /// + /// Vettore 32 BIT valori precedenti + /// + protected int B_previous; + + /// + /// Dizionario valori impostati x produzione + /// + protected Dictionary currProdData = new Dictionary(); + + /// + /// Array dei contatori x segnali blinking + /// + protected int[] i_counters; + + /// + /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) + /// + protected bool inSetup = false; + + /// + /// ultimo tentativo connessione... + /// + protected DateTime lastConnectTry; + + /// + /// Ultimo invio contapezzi (x invio delayed) + /// + protected DateTime lastPzCountSend; + + /// + /// Dizionario ultimi valori (double) delle TSVC + /// + protected Dictionary LastTSVC = new Dictionary(); + + /// + /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) + /// + protected DateTime lastWarnODL; + + /// + /// indica se serva refresh parametri e quindi PLC... + /// + protected bool needRefresh = true; + + /// + /// Form chiamante + /// + protected AdapterForm parentForm; + + /// + /// TImeout x ping al server + /// + protected int pingServerMsTimeout = utils.CRI("pingMsTimeout"); + + /// + /// Ritardo minimo x invio contapezzi + /// + protected int pzCountDelay; + + /// + /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) + /// + protected Dictionary TSVC_Data = new Dictionary(); + + /// + /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a scadenza periodo...) + /// + protected Dictionary VarArray = new Dictionary(); + + #endregion Protected Fields + + #region Public Fields /// /// valore booleano di check se sia stato AVVIATO l'adapter (Running) @@ -47,6 +144,11 @@ namespace IOB_WIN /// public bool adpTryRestart; + /// + /// Conf adapter corrente + /// + public IobConfiguration cIobConf; + /// /// Conteggio ATTUALE ore macchina IN LAVORO /// @@ -57,6 +159,11 @@ namespace IOB_WIN /// public double contOreMaccOn; + /// + /// contatore x simulazione valori input + /// + public int countSim = 0; + /// /// ODL attualmente sulla macchina /// @@ -137,6 +244,11 @@ namespace IOB_WIN /// public string lastSignInVal = ""; + /// + /// DateTime Ultimo valore simulazione generato + /// + public DateTime lastSim; + /// /// dataOra ultimo segnale inviato... /// @@ -147,6 +259,11 @@ namespace IOB_WIN /// public int maxSendPzCountBlock = 10; + /// + /// Struttura memoria PLC x lettura/scrittura da JSON file + /// + public plcMemMap memMap; + /// /// Minimo numero di px da inviare in blocco /// @@ -157,6 +274,11 @@ namespace IOB_WIN /// public bool needRefreshPzCount = true; + /// + /// Dizionario di persistenza per i valori da salvare da/su file + /// + public Dictionary persistenceLayer; + /// /// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing "monoblocco" semplificato"= /// @@ -197,6 +319,11 @@ namespace IOB_WIN /// public byte[] RawOutput = new byte[32]; + /// + /// Oggetto connessione REDIS + /// + public RedisIobCache redisMan; + /// /// Oggetto cronometro x campionamento durate chiamate /// @@ -217,96 +344,322 @@ namespace IOB_WIN /// public bool W = true; - /// - /// wrapper di log - /// - protected static Logger lg; + #endregion Public Fields + + #region Public Constructors /// - /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC + /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto /// - protected bool adpCommAct; + /// + /// + public IobGeneric(AdapterForm caller, IobConfiguration IOBConf) + { + if (IOBConf != null) + { + // init oggetto redis... + redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB); - /// - /// porta x adapter (x restart) - /// - protected int adpPortNum; + // initi oggetto TCMan + tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz); - /// - /// DataOra ultimo avvio adapter x watchdog - /// - protected DateTime adpStartRun; + // salvo il form chiamante + parentForm = caller; + // configurazione... + cIobConf = IOBConf; - /// - /// Vettore 32 BIT valori in ingresso al filtro - /// - protected int B_input; + lastConnectTry = DateTime.Now; - /// - /// Vettore 32 BIT valori in uscita dal filtro - /// - protected int B_output; + // aggiungo nel logger IDX Macchina + lg = LogManager.GetCurrentClassLogger(); - /// - /// Vettore 32 BIT valori precedenti - /// - protected int B_previous; + lgInfo("Avvio preliminare AdapterGeneric"); + // aggiungo altri defaults + setDefaults(true); - /// - /// Dizionario valori impostati x produzione - /// - protected Dictionary currProdData = new Dictionary(); + setParamPlc(); - /// - /// Array dei contatori x segnali blinking - /// - protected int[] i_counters; + // checkLogDir x shrink! + checkShrinkDir(); - /// - /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) - /// - protected bool inSetup = false; + // concluso! + lgInfo("Istanziata classe preliminare IOBGeneric"); + } + else + { + lgError("Error: IobCOnf is null!"); + } + } - /// - /// Ultimo invio contapezzi (x invio delayed) - /// - protected DateTime lastPzCountSend; + #endregion Public Constructors - /// - /// Dizionario ultimi valori (double) delle TSVC - /// - protected Dictionary LastTSVC = new Dictionary(); - - /// - /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) - /// - protected DateTime lastWarnODL; - - /// - /// TImeout x ping al server - /// - protected int pingServerMsTimeout = utils.CRI("pingMsTimeout"); - - /// - /// Ritardo minimo x invio contapezzi - /// - protected int pzCountDelay; - - /// - /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) - /// - protected Dictionary TSVC_Data = new Dictionary(); - - /// - /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a scadenza periodo...) - /// - protected Dictionary VarArray = new Dictionary(); + #region Public Events /// /// Evento Iob ha subito un refresh /// public event EventHandler eh_refreshed; + #endregion Public Events + + #region Private Properties + + /// + /// Verifica se la IOB sia ENABLED (da server o Demo) + /// + private bool checkIobEnabled + { + get + { + bool answ = false; + // controllo se ho veto al check... + if (dtVetoCheckIOB < DateTime.Now) + { + if (DemoOut) + { + answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); + } + else + { + try + { + // chiamo URL, se restituisce "OK" è enabled! + string callResp = callUrl(urlIobEnabled, true); + answ = (callResp == "OK"); + // attesa casuale se necessario + var rand = new Random(); + // primi 2 test + int maxTry = 2; + while (maxTry > 0 && !answ) + { + Thread.Sleep(rand.Next(250, 500)); + callResp = callUrl(urlIobEnabled, true); + answ = (callResp == "OK"); + maxTry--; + } + // se NON OK riprovo ANCORA 1 volta... + if (!answ) + { + resetWebClients(); + Thread.Sleep(rand.Next(250, 1000)); + callResp = callUrl(urlIobEnabled, false); + answ = (callResp == "OK"); + } + // altri 2 + maxTry = 2; + while (maxTry > 0 && !answ) + { + Thread.Sleep(rand.Next(250, 500)); + callResp = callUrl(urlIobEnabled, false); + answ = (callResp == "OK"); + maxTry--; + } + // salvo status... + IobOnline = answ; + // se online imposto veto check a 5 x tempo reinvio... + if (answ) + { + lastIobOnline = DateTime.Now; + } + dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); + } + catch + { } + } + // verifico SE è variato stato online/offline... + if (IobOnline != answ) + { + // se ORA sono online riporto... + if (answ) + { + lgInfo("IOB ONLINE for server MP/IO"); + } + else + { + lgInfo("IOB OFFLINE for server MP/IO"); + } + } + // fix colore + if (answ) + { + parentForm.commSrvActive = 2; + } + else + { + parentForm.commSrvActive = 1; + } + } + else + { + // altrimenti passo ultimo valore noto + answ = IobOnline; + } + return answ; + } + } + + /// + /// test ping all'indirizzo impostato nei parametri + /// + /// + private IPStatus testPingServer + { + get + { + IPStatus answ = IPStatus.Unknown; ; + IPAddress address; + PingReply reply; + using (Ping pingSender = new Ping()) + { + address = IPAddress.Loopback; + int maxRetry = maxPingRetry + 1; + int numRetry = 1; ; + string ipAdrr = cIobConf.serverData.MPIP.Replace("http://", "").Replace("https://", ""); + IPAddress.TryParse(ipAdrr, out address); + reply = pingSender.Send(address, pingServerMsTimeout); + // se ho timeout riprovo... + while (reply.Status != IPStatus.Success && numRetry < maxRetry) + { + lgInfo($"Ping KO | reply: {reply.Status} --> retry"); + reply = pingSender.Send(address, pingServerMsTimeout * numRetry / 2); + numRetry++; + if (reply.Status == IPStatus.Success) + { + lgInfo("PING OK!"); + break; + } + } + } + answ = reply.Status; + return answ; + } + } + + #endregion Private Properties + + #region Protected Properties + + /// + /// Valore del num max invii consecutivi da coda... + /// + protected static int nMaxSend + { + get + { + int answ = 5; + try + { + answ = utils.CRI("nMaxSend"); + } + catch + { } + return answ; + } + } + + /// + /// Valore limite MASSIMO di invio di dati come array Json + /// + protected int maxJsonData { get; set; } = utils.CRI("maxJsonData"); + + /// + /// Valore limite MASSIMO di invio di dati come array Json x EVENTI + /// + protected int maxJsonDataEv { get; set; } = utils.CRI("maxJsonDataEv"); + + /// + /// Max tentativi ping permessi (default: 5) + /// + protected int maxPingRetry { get; set; } = 5; + + /// + /// Coda massima ammessa per FLog (se <=0 disattivata...) + /// + protected int maxQueueFLog { get; set; } = utils.CRI("maxQueueFLog"); + + /// + /// Valore MINIMO limite x decidere invio di dati come array Json + /// + protected int minJsonData { get; set; } = utils.CRI("minJsonData"); + + /// + /// Numero letture IN da avvio + /// + protected int nReadFilt { get; set; } + + /// + /// Numero letture IN da avvio + /// + protected int nReadIN { get; set; } + + /// + /// Numero invii OUT (svuotamento coda) + /// + protected int nSendOut { get; set; } + + /// + /// Numero simulazioni ammesse... + /// + protected int numSim { get; set; } + + /// + /// Fornisce il valore letto da BITMAP in formato valido x messa in coda nel formato dtEve#value#cont + /// + protected string qEncodeIN + { + get + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN); + } + catch + { } + return answ; + } + } + + /// + /// test ping all'indirizzo PLC/CNC impostato nei parametri + /// + /// + protected IPStatus testPingMachine + { + get + { + IPStatus answ = IPStatus.Unknown; + // se disabilitato salto... + if (pingDisabled) + { + answ = IPStatus.Success; + } + else + { + IPAddress address; + PingReply reply; + using (Ping pingSender = new Ping()) + { + address = IPAddress.Loopback; + IPAddress.TryParse(cIobConf.cncIpAddr, out address); + int pingMsTimeout = cIobConf.pingMsTimeout; + reply = pingSender.Send(address, pingMsTimeout); + answ = reply.Status; + } + } + return answ; + } + } + + /// + /// Secondi standard x veto check status e log + /// + protected int vetoSeconds { get; set; } = utils.CRI("vetoSeconds"); + + #endregion Protected Properties + + #region Public Properties + /// /// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...) /// @@ -374,1983 +727,6 @@ namespace IOB_WIN } } - /// - /// Contapezzi attuale - /// - public Int32 contapezziIOB - { - get - { - return tcMan.pzCountIOB; - } - set - { - tcMan.pzCountIOB = value; - } - } - - /// - /// Ultima lettura variabile contapezzi da CNC - /// - public Int32 contapezziPLC - { - get - { - return tcMan.pzCountPLC; - } - set - { - tcMan.pzCountPLC = value; - } - } - - /// - /// Contatore x invio dati FluxLog - /// - public int counterFLog { get; set; } - - /// - /// Contatore x invio dati SignalIN - /// - public int counterSigIN { get; set; } - - /// - /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA... - /// - public bool DemoIn - { - get - { - return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn"); - } - } - - /// - /// stato Online/Offline della IOB - /// - public bool IobOnline - { - get - { - return utils.IOB_Online; - } - set - { - utils.IOB_Online = value; - } - } - - /// - /// Log verboso da configurazione (SOLO CHAIVE "verbose"... - /// - public bool isVerboseLog { get; set; } = utils.CRB("verbose"); - - /// - /// Ultimo Alarm letto - /// - public string lastAlarm { get; set; } - - /// - /// Ultimo ARRAY DynData letto - /// - public Dictionary lastDynData { get; set; } = new Dictionary(); - - /// - /// Ultimo DynData (sunto) letto - /// - public string lastDynDataCtrlVal { get; set; } - - /// - /// Ultimo Override set letto - /// - public string lastOverrideFS { get; set; } - - /// - /// Ultimo Override set letto - /// - public string lastOverrideRapid { get; set; } - - /// - /// Ultimo programma letto - /// - public string lastPrgName { get; set; } - - /// - /// Ultimo SysInfo letto - /// - public string lastSysInfo { get; set; } - - /// - /// Ultimo URL - /// - public string lastUrl { get; set; } - - /// - /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...) - /// - public bool periodicLog - { - get - { - bool answ = false; - answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); - if (answ) - { - lastPeriodicLog = DateTime.Now; - } - - return answ; - } - } - - /// - /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN) - /// - public bool verboseLog - { - get - { - bool answ = false; - int logEvery = utils.CRI("logEvery"); - if (logEvery < 1) - { - logEvery = 10; - } - - answ = utils.CRB("verbose") && (nReadIN % logEvery == 0); - return answ; - } - } - - /// - /// Coda massima ammessa per FLog (se <=0 disattivata...) - /// - protected int maxQueueFLog { get; set; } = utils.CRI("maxQueueFLog"); - - /// - /// Numero letture IN da avvio - /// - protected int nReadFilt { get; set; } - - /// - /// Numero letture IN da avvio - /// - protected int nReadIN { get; set; } - - /// - /// Numero invii OUT (svuotamento coda) - /// - protected int nSendOut { get; set; } - - /// - /// Numero simulazioni ammesse... - /// - protected int numSim { get; set; } - - /// - /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati - /// - public virtual void reportRawInput(ref newDisplayData currDispData) - { - // processo eventualmente aggiungendo ad elementi esistenti... - if (currDispData == null) - { - currDispData = new newDisplayData(); - } - try - { - StringBuilder sb = new StringBuilder(); - sb.Append($"B_input --> {(short)B_input}{Environment.NewLine}"); - sb.Append($"{baseUtils.binaryForm(B_input)}{Environment.NewLine}"); - sb.Append($"{Environment.NewLine}"); - sb.Append($"----------- RAW Data Memory -----------{Environment.NewLine}"); - int i = 0; - foreach (var item in RawInput) - { - sb.Append($"B{i:00} --> {baseUtils.binaryForm(item)} = {(short)item}{Environment.NewLine}"); - i++; - } - sb.Append("-------------------------------"); - currDispData.currBitmap = sb.ToString(); - } - catch - { } - } - - /// - /// Salva valori indicati in prod data - /// - /// Item KVP di cui salvare i dati in currProdData come chiave/valore - /// - public void saveProdData(KeyValuePair item) - { - // imposto i valori... - if (currProdData.ContainsKey(item.Key)) - { - currProdData[item.Key] = item.Value; - } - else - { - currProdData.Add(item.Key, item.Value); - } - } - - #endregion variabili ed oggetti base - - #region Protected Fields - - /// - /// ultimo tentativo connessione... - /// - protected DateTime lastConnectTry; - - /// - /// indica se serva refresh parametri e quindi PLC... - /// - protected bool needRefresh = true; - - /// - /// Form chiamante - /// - protected AdapterForm parentForm; - - #endregion Protected Fields - - /// - /// Conf adapter corrente - /// - public IobConfiguration cIobConf; - - /// - /// Struttura memoria PLC x lettura/scrittura da JSON file - /// - public plcMemMap memMap; - - /// - /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto - /// - /// - /// - public IobGeneric(AdapterForm caller, IobConfiguration IOBConf) - { - if (IOBConf != null) - { - // init oggetto redis... - redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB); - - // initi oggetto TCMan - tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz); - - // salvo il form chiamante - parentForm = caller; - // configurazione... - cIobConf = IOBConf; - - lastConnectTry = DateTime.Now; - - // aggiungo nel logger IDX Macchina - lg = LogManager.GetCurrentClassLogger(); - - lgInfo("Avvio preliminare AdapterGeneric"); - // aggiungo altri defaults - setDefaults(true); - - setParamPlc(); - - // checkLogDir x shrink! - checkShrinkDir(); - - // concluso! - lgInfo("Istanziata classe preliminare IOBGeneric"); - } - else - { - lgError("Error: IobCOnf is null!"); - } - } - - /// - /// Valore limite MASSIMO di invio di dati come array Json - /// - protected int maxJsonData { get; set; } = utils.CRI("maxJsonData"); - - /// - /// Valore limite MASSIMO di invio di dati come array Json x EVENTI - /// - protected int maxJsonDataEv { get; set; } = utils.CRI("maxJsonDataEv"); - - /// - /// Valore MINIMO limite x decidere invio di dati come array Json - /// - protected int minJsonData { get; set; } = utils.CRI("minJsonData"); - - /// - /// Secondi standard x veto check status e log - /// - protected int vetoSeconds { get; set; } = utils.CRI("vetoSeconds"); - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgError(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(message); - if (sendToForm) - { - sendToLogWatch("ERROR", message); - } - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgError(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(message, args); - sendToLogWatch("ERROR", message, args); - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - /// - protected void lgError(Exception exception, string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(exception, message, args); - sendToLogWatch("ERROR", message, exception, args); - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgFatal(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(message); - if (sendToForm) - { - sendToLogWatch("FATAL", message); - } - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgFatal(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(message, args); - sendToLogWatch("FATAL", message, args); - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - /// - protected void lgFatal(Exception exception, string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(exception, message, args); - sendToLogWatch("FATAL", message, exception, args); - } - - /// - /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgInfo(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Info(message); - if (sendToForm) - { - sendToLogWatch("INFO", message); - } - } - - /// - /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgInfo(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Info(message, args); - sendToLogWatch("INFO", message, args); - } - - /// - /// Lettura memorie conf speciali (json) - /// ...SE inserito in [OPTPAR] come PARAM_CONF=nome.json - /// - protected virtual void loadMemConf() - { - lgInfo("BEGIN loadMemConf"); - // variabili x gestione send contapezzi in blocco - string currPar = getOptPar("ENABLE_SEND_PZC_BLOCK"); - if (!string.IsNullOrEmpty(currPar)) - { - bool.TryParse(currPar, out enableSendPzCountBlock); - // se abilitato leggo num pezzi da reinviare in blocco - if (enableSendPzCountBlock) - { - int.TryParse(getOptPar("MAX_SEND_PZC_BLOCK"), out maxSendPzCountBlock); - int.TryParse(getOptPar("MIN_SEND_PZC_BLOCK"), out minSendPzCountBlock); - } - } - else - { - lgError("loadMemConf: parametro ENABLE_SEND_PZC_BLOCK non trovato, verificare anche MAX_SEND_PZC_BLOCK e MIN_SEND_PZC_BLOCK"); - } - // inizializzo LUT decodifica - string jsonConf = getOptPar("PARAM_CONF"); - if (!string.IsNullOrEmpty(jsonConf)) - { - string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonConf}"; - lgInfo($"Apertura file {jsonFileName}"); - StreamReader reader = new StreamReader(jsonFileName); - string jsonData = reader.ReadToEnd(); - if (!string.IsNullOrEmpty(jsonData)) - { - lgInfo($"File json composto da {jsonData.Length} caratteri"); - try - { - memMap = JsonConvert.DeserializeObject(jsonData); - lgInfo($"Decodifica aree memMap: trovati {memMap.mMapRead.Count} valori TSVC"); - lgInfo($"Decodifica aree memMap: trovati {memMap.mMapWrite.Count} parametri "); - // se ho variabili read --> genero dati TSVC... - if (memMap.mMapRead.Count > 0) - { - TSVC_Data.Clear(); - LastTSVC.Clear(); - VCData currConf; - int periodo = 0; - VC_func funz = VC_func.POINT; - // accodo nella conf... - foreach (var item in memMap.mMapRead) - { - funz = item.Value.func; - periodo = item.Value.period; - currConf = new VCData() - { - Funzione = funz, - Period = periodo, - DTStart = DateTime.Now.AddHours(-1), - dataArray = new List() - }; - TSVC_Data.Add(item.Key, currConf); - } - // documento... - foreach (var item in TSVC_Data) - { - lgInfo($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}"); - // salvo i valori PREC... - LastTSVC.Add(item.Key, 0); - } - } - // infine se obj memoria valido salvo in MP-IO x sue applicazioni - if (memMap != null) - { - // invio su cloud conf memoria... - string rawData = JsonConvert.SerializeObject(memMap); - utils.callUrlNow($"{urlSaveMemMap}", rawData); - // salvo ANCHE come parametri i valori... - objItem currItem = new objItem(); - List allParam = new List(); - // valori WRITE - foreach (var item in memMap.mMapWrite) - { - currItem = new objItem() - { - uid = item.Value.name, - name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, - writable = true - }; - allParam.Add(currItem); - } - // valori READ - foreach (var item in memMap.mMapRead) - { - currItem = new objItem() - { - uid = item.Value.name, - name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, - writable = false - }; - allParam.Add(currItem); - } - // invio su cloud parametri! - rawData = JsonConvert.SerializeObject(allParam); - utils.callUrl($"{urlSaveAllParams}", rawData); - } - } - catch (Exception exc) - { - lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}"); - } - } - else - { - lgError("Errore in loadMemConf: file json vuoto!"); - } - reader.Dispose(); - } - else - { - lgInfo("loadMemConf: non trovata opzione PARAM_CONF in file INI"); - } - // loggo - lgInfo("DONE loadMemConf"); - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - protected void sendToLogWatch(string messType, string message) - { - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; - parentForm.updateFormDisplay(currDispData); - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - /// - protected void sendToLogWatch(string messType, string message, params object[] args) - { - try - { - string expString = string.Format(message, args); - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}"; - parentForm.updateFormDisplay(currDispData); - } - catch - { } - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - /// - /// - protected void sendToLogWatch(string messType, string message, Exception exception, params object[] args) - { - try - { - string expString = string.Format(message, args); - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}{Environment.NewLine}{exception}"; - parentForm.updateFormDisplay(currDispData); - } - catch - { } - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - protected void sendToTaskWatch(string messType, string message) - { - parentForm.taskWatcher = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; - } - - /// - /// Impostazioni parametri PLC - /// - protected virtual void setParamPlc() - { - loadMemConf(); - fixDefaultPar(); - } - - /// - /// Imposta eventuali altri valori default - /// - private void fixDefaultPar() - { - // parametro max tentativi PING... - string s_maxPingRetry = getOptPar("MAX_PING_RETRY"); - if (!string.IsNullOrEmpty(s_maxPingRetry)) - { - int numRetry = 5; - int.TryParse(s_maxPingRetry, out numRetry); - maxPingRetry = numRetry; - } - } - - /// - /// Imposto alcuni valori di default - /// - /// indica se sia richeisto di SVUOTARE le code delel info - private void setDefaults(bool resetQueue) - { - numSim = utils.CRI("numSim"); - lastPrgName = ""; - nReadIN = 0; - nReadFilt = 0; - nSendOut = 0; - currMode = 0; - lastAlarm = ""; - doStartMemDump = utils.CRB("doStartMemDump"); - doSampleMemory = utils.CRB("doSampleMemory"); - // svuoto code se richiesto - if (resetQueue) - { - QueueIN = new ConcurrentQueue(); - QueueFLog = new ConcurrentQueue(); - QueueAlarm = new ConcurrentQueue(); - QueueMessages = new ConcurrentQueue(); - } - // imposto contatori blink a zero... - i_counters = new int[32]; - lastPeriodicLog = DateTime.Now; - // fix parametri generali... - enablePrgName = true; - } - - #region metodi adapter - - protected bool _connOk = false; - - /// - /// Salva verifica stato connessione OK - /// - /// - public virtual bool connectionOk - { - get - { - return _connOk || DemoIn; - } - set - { - _connOk = value; - } - } - - /// - /// indica se ping disabilitato da optPar - /// - public bool pingDisabled - { - get - { - bool answ = false; - bool.TryParse(getOptPar("NO_PING"), out answ); - return answ; - } - } - - /// - /// Max tentativi ping permessi (default: 5) - /// - protected int maxPingRetry { get; set; } = 5; - - /// - /// test ping all'indirizzo PLC/CNC impostato nei parametri - /// - /// - protected IPStatus testPingMachine - { - get - { - IPStatus answ = IPStatus.Unknown; - // se disabilitato salto... - if (pingDisabled) - { - answ = IPStatus.Success; - } - else - { - IPAddress address; - PingReply reply; - using (Ping pingSender = new Ping()) - { - address = IPAddress.Loopback; - IPAddress.TryParse(cIobConf.cncIpAddr, out address); - int pingMsTimeout = cIobConf.pingMsTimeout; - reply = pingSender.Send(address, pingMsTimeout); - answ = reply.Status; - } - } - return answ; - } - } - - /// - /// processa dataLayer e se necessario salva/mostra - /// - public static void checkSavePersDataLayer() - { - } - - public static void resetDebugConsole() - { - } - - /// - /// Esecuzione dei task richiesti e pulizia coda richieste eseguite - /// - /// - public virtual Dictionary executeTasks(Dictionary task2exe) - { - // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti... - Dictionary taskDone = new Dictionary(); - if (task2exe != null) - { - bool taskOk = false; - string taskVal = ""; - // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4 - foreach (var item in task2exe) - { - taskOk = false; - taskVal = ""; - // converto richiesta in enum... - taskType tName = taskType.nihil; - Enum.TryParse(item.Key, out tName); - // controllo sulla KEY... - switch (tName) - { - case taskType.nihil: - case taskType.fixStopSetup: - case taskType.forceSetPzCount: - case taskType.sendWatchDogMes2Plc: - taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; - lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.setArt: - case taskType.setComm: - case taskType.setProg: - case taskType.setPzComm: - // recupero dati da memMap... - if (memMap.mMapWrite.ContainsKey(item.Key)) - { - dataConf currMem = memMap.mMapWrite[item.Key]; - string addr = currMem.memAddr; - taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte"; - // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà - memMap.mMapWrite[item.Key].value = item.Value; - } - else - { - taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}"; - } - break; - - case taskType.forceResetPzCount: - // reset contapezzi inizio setup - taskOk = resetcontapezziPLC(); - taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.startSetup: - // reset contapezzi inizio setup - taskOk = resetcontapezziPLC(); - taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.stopSetup: - // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO - if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE") - { - taskOk = resetcontapezziPLC(); - } - taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.setParameter: - // richiedo da URL i parametri WRITE da popolare - lgInfo("Chiamata processMemWriteRequests"); - taskVal = processMemWriteRequests(); - // se restituiscce "" faccio altra prova... - if (string.IsNullOrEmpty(taskVal)) - { - // i parametri me li aspetto come stringa composta paramName|paramvalue - if (item.Value.Contains("|")) - { - string[] paramsJob = item.Value.Split('|'); - taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; - } - else - { - taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; - } - } - break; - - default: - taskVal = "SKIPPED | NO EXEC"; - lgInfo($"Chiamata default senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - } - // aggiungo task! - taskDone.Add(item.Key, taskVal); - } - } - return taskDone; - } - - /// - /// effettua recupero dati ed invio valori modificati... - /// - /// - public void getAndSend(gatherCycle ciclo) - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!! - try - { - trySendValues(); - } - catch (Exception exc) - { - lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria"); - currDispData.semOut = Semaforo.SR; - } - // controllo connessione/connettività - if (connectionOk) - { - // controllo non sia già in esecuzione... - if (!adpCommAct) - { - // provo ad avviare - try - { - // imposto flag adapter running.. - adpCommAct = true; - adpStartRun = DateTime.Now; - } - catch (Exception exc) - { - string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}"; - adpCommAct = false; - adpStartRun = DateTime.Now; - currDispData.newLiveLogData = errore; - } - if (adpCommAct) - { - // try / catch generale altrimenti segno che è disconnesso... - try - { - bool showDebugData = false; - if (ciclo == gatherCycle.VHF) - { - processVHF(); - } - // processing dati memoria (lettura, filtraggio, enqueque) - else if (ciclo == gatherCycle.HF) - { - processWhatchDog(); - processAllMemory(); - processMode(); - } - else if (ciclo == gatherCycle.MF) - { - processServerRequests(); - processOverride(); - processContapezzi(); - processCncAlarms(); - processDynData(); - } - else if (ciclo == gatherCycle.LF) - { - processOtherCounters(); - processProgram(); - // verifico se devo gestire cambio ODL in modo automatico - processAutoOdl(); - } - else if (ciclo == gatherCycle.VLF) - { - if (utils.CRB("enableContapezzi")) - { - // rilettura contapezzi da server... - lgInfo("Ciclo VLF: pzCntReload(true)"); - if (!isMulti) - { - pzCntReload(true); - } - // refresh associazione Macchina - IOB - sendM2IOB(); - } - // recupero dati SETUP (sysinfo) e li invio/mostro se variati... - processSysInfo(); - // checkLogDir x shrink! - checkShrinkDir(); - // eventuale log! - if (utils.CRB("recTime")) - { - logTimeResults(); - } - } - // mostra eventuali altri dati di processo... - reportDataProc(); - if (showDebugData) - { - // verifica se debba salvare e mostrare dati - checkSavePersDataLayer(); - } - } - catch (Exception exc) - { - // segnalo eccezione e indico disconnesso... - lgError(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc)); - parentForm.fermaAdapter(true, false, true); - } - // tolgo flag running - adpCommAct = false; - } - else - { - if (periodicLog) - { - lgInfo("ADP not running..."); - } - } - } - else - { - // log ADP running - lgError("Non eseguo chiamata: ADP ancora in running"); - // se è bloccato da oltre maxSec lo sblocco... - if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) - { - // tolgo flag running - adpCommAct = false; - adpStartRun = DateTime.Now; - } - } - } - else - { - // provo a riconnettere SE abilitato tryRestart... - if (adpTryRestart && !connectionOk) - { - // controllo se sia scaduto periodi di veto al tryConnect... - int waitRecMSec = utils.CRI("waitRecMSec") * 2; - DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); - if (DateTime.Now > dtVeto) - { - lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect"); - lastConnectTry = DateTime.Now; - tryConnect(); - } - } - currDispData.semIn = Semaforo.SR; - } - raiseRefresh(currDispData); - } - - /// - /// riporta il log di tutti i dati di results temporali registrati - /// - public void logTimeResults() - { - if (TimingData.results.Count > 0) - { - lgInfo("{0}--------------- START TIMING DATA ---------------", Environment.NewLine); - int globNumCall = 0; - TimeSpan globAvgMsec = new TimeSpan(0); - foreach (TimeRec item in TimingData.results) - { - // loggo SOLO se del mio IOB corrente... - if (item.classCall == cIobConf.codIOB) - { - lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); - globNumCall += item.numCall; - globAvgMsec += item.totMsec; - } - } - // riporto conteggio medio al secondo... - lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); - lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine); - // mostro in form statistiche globali! - parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds)); - } - } - - /// - /// Effettua ciclo controllo richieste server - /// - public void processServerRequests() - { - Dictionary task2exe = new Dictionary(); - Dictionary taskDone = new Dictionary(); - // recupero elenco delle cose da fare - string resp = getTask2exe(); - if (!string.IsNullOrEmpty(resp)) - { - try - { - task2exe = JsonConvert.DeserializeObject>(resp); - // se ho da fare chiamo esecuzione.. - if (task2exe.Count > 0) - { - taskDone = processTask(task2exe); - } - } - catch (Exception exc) - { - lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); - } - } - } - - public Dictionary processTask(Dictionary task2exe) - { - Dictionary taskDone = new Dictionary(); - if (task2exe != null) - { - lgInfo($"Task2Exe: trovati {task2exe.Count} task da eseguire, procedo"); - // chiamo procedura esecutiva (diversa x ogni IOB) - taskDone = executeTasks(task2exe); - // loggo tutti i task done... - foreach (var item in taskDone) - { - sendToTaskWatch(item.Key, item.Value); - } - // ora chiamo la cancellazione dei task eseguiti... - foreach (var item in taskDone) - { - remTask2exe(item.Key, item.Value); - } - } - return taskDone; - } - - /// - /// Effettua rilettura del contapezzi dal server MP/IO - /// - /// Forza rilettura da DB tempi ciclo rilevati - public void pzCntReload(bool forceCountRec) - { - // legge da IO server ULTIMO valore CONTPEZZI al riavvio... - string currServerCount = ""; - string lastIdxODL = ""; - if (checkServerAlive) - { - // leggo PRIMA ODL .... - lastIdxODL = utils.callUrl(urlGetCurrODL); - lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL); - // se ho valori in coda da trasmettere uso dati REDIS - if (forceCountRec) - { - // uso dati da TCiclo registrati... - currServerCount = utils.callUrl(urlGetPzCountRec); - lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount); - } - else - { - // uso il contapezzi dichiarato dall'IOB stesso - currServerCount = utils.callUrl(urlGetPzCount); - lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount); - } - // controllo: SE NON HO ODL... - if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0") - { - // NON AGGIORNO - contapezziIOB = contapezziPLC; - lgInfo($"Errore lettura ODL (vuoto) resta tutto invariato contapezzi: {contapezziIOB} | contapezziPLC {contapezziPLC}"); - } - else - { - if (!string.IsNullOrEmpty(currServerCount)) - { - // se "-1" resto a ultimo... - if (currServerCount != "-1") - { - int newVal = -1; - Int32.TryParse(currServerCount, out newVal); - contapezziIOB = newVal > -1 ? newVal : contapezziIOB; - lgInfo("Ricevuta conferma da server di {0} pezzi registrati per ODL", currServerCount); - } - else - { - // NON AGGIORNO - contapezziIOB = contapezziPLC; - lgInfo($"Errore lettura contapezzi (-1) - uso contapezziPLC --> {contapezziPLC}"); - } - } - else - { - // registro che ho UN NUOVO ODL - lgInfo($"Lettura ODL in pzCntReload, currIdxODL {currIdxODL} --> lastIdxODL {lastIdxODL}"); - // se ODL differente e NUOVO è zero --> resetto! - if (currIdxODL.ToString() != lastIdxODL && lastIdxODL == "0") - { - // cambiato ODL quindi reset... - contapezziIOB = 0; - lgInfo("Nuovo ODL==0, RESET contapezzi (-->ZERO)"); - } - // provo a salvare nuovo ODL - int.TryParse(lastIdxODL, out currIdxODL); - } - } - } - else - { - // se server NON pronto... - contapezziIOB = contapezziPLC; - lgError("Errore server NON pronto in pzCntReload"); - } - } - - /// - /// Metodo generico di reset contapezzi... - /// - /// - public virtual bool resetcontapezziPLC() - { - return false; - } - - /// - /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi... - /// - /// Pezzi richiesti - /// - public virtual bool setcontapezziPLC(int newPzCount) - { - return false; - } - - /// - /// Avvia l'adapter sulla porta richiesta - /// - /// indica se sia richeisto di SVUOTARE le code delel info - public virtual void startAdapter(bool resetQueue) - { - lgInfo("Starting adapter..."); - maxJsonData = utils.CRI("maxJsonData"); - maxJsonDataEv = utils.CRI("maxJsonDataEv"); - parentForm.commPlcActive = false; - adpRunning = true; - dtAvvioAdp = DateTime.Now; - lastWatchDog = dtAvvioAdp; - lastPING = dtAvvioAdp; - lastReadPLC = dtAvvioAdp.AddMinutes(-1); - lastDisconnCheck = dtAvvioAdp; - TimingData.resetData(); - // aggiungo altri defaults - setDefaults(resetQueue); - adpTryRestart = true; - parentForm.displayTaskAndLog("Adapter Started!"); - } - - /// - /// ferma l'adapter... - /// - /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato in automatico) - /// indica se sia richeisto di SVUOTARE le code delel info - public virtual void stopAdapter(bool tryRestart, bool forceDequeue) - { - if (forceDequeue) - { - // svuoto le code dei valori letti e non ancora trasmessi... - parentForm.displayTaskAndLog("Svuotamento FORZATO coda segnali..."); - while (QueueIN.Count > 0) - { - // INVIO COMUNQUE...!!! - string valore = ""; - QueueIN.TryDequeue(out valore); - sendToMoonPro(urlType.SignIN, valore); - } - parentForm.displayTaskAndLog("Svuotamento FORZATO coda FluxLOG..."); - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > minJsonData) - { - while (QueueFLog.Count > 0) - { - List listaValori = new List(); - // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) - { - string currVal = ""; - // prendoi primi maxJsonDataValori - for (int i = 0; i < maxJsonData; i++) - { - QueueFLog.TryDequeue(out currVal); - listaValori.Add(currVal); - } - sendDataBlock(urlType.FLog, listaValori); - } - else - { - // invio in blocco - listaValori = QueueFLog.ToList(); - // invio - sendDataBlock(urlType.FLog, listaValori); - // svuoto! - QueueFLog = new ConcurrentQueue(); - } - } - // HO FINITO invio di FLog... - } - else - { - string currVal = ""; - while (QueueFLog.Count > 0) - { - // INVIO COMUNQUE...!!! - QueueFLog.TryDequeue(out currVal); - sendToMoonPro(urlType.FLog, currVal); - } - } - } - parentForm.displayTaskAndLog("Stopping adapter..."); - adpTryRestart = false; - - parentForm.displayTaskAndLog("Stopping adapter - last periodic data read..."); - - // chiudo la connessione all'adapter... - tryDisconnect(); - dtStopAdp = DateTime.Now; - adpTryRestart = tryRestart; - adpRunning = false; - // chiudo! - parentForm.displayTaskAndLog("Adapter Stopped."); - parentForm.commPlcActive = false; - } - - /// - /// Metodo base connessione... - /// - public virtual void tryConnect() - { - dtAvvioAdp = DateTime.Now; - } - - /// - /// Metodo base disconnessione... - /// - public virtual void tryDisconnect() - { - } - - /// - /// Stringa raw dei parametri da scrivere... - /// - /// - protected string getParams2write() - { - string answ = ""; - string url2call = $"{urlGetParams2Write}"; - if (verboseLog) - { - lgInfo("chiamata URL " + url2call); - } - answ = utils.callUrlNow(url2call); - // se vuoto faccio seconda prova... - if (string.IsNullOrEmpty(answ)) - { - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Chiede elenco dei task da eseguire - /// - formato Json - /// - array di KVP / Dictionary - /// - formato definito da API x MP/IO/: - /// - KEY: task - /// - VALUE: array JSon KVP - /// - protected string getTask2exe() - { - string answ = ""; - if (checkServerAlive) - { - string url2call = $"{urlGetTask2Exe}"; - if (verboseLog) - { - lgInfo("chiamata URL " + url2call); - } - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Metodo da overridare x scrivere DAVVERO i aprametri sul PLC - /// - /// - protected virtual void plcWriteParams(List updatedPar) - { - // non faccio nulla di base... - } - - /// - /// Processa le richieste di scrittura memoria - /// - /// - protected string processMemWriteRequests() - { - string answ = ""; - // li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC) - List writeList = new List(); - List updatedPar = new List(); - // recupero elenco delle cose da fare - string resp = getParams2write(); - if (!string.IsNullOrEmpty(resp)) - { - try - { - writeList = JsonConvert.DeserializeObject>(resp); - // se ho da fare chiamo esecuzione.. - if (writeList.Count > 0) - { - foreach (var item in writeList) - { - // scrivo in memoria - if (memMap.mMapWrite.ContainsKey(item.uid)) - { - memMap.mMapWrite[item.uid].value = item.reqValue; - // accodo in stringa taskVal... - answ += $" | Parameter {item.uid} --> {item.reqValue}"; - // sistemo valori - item.value = item.reqValue; - lgInfo($"Effettuato update parametro: actVal = {item.value} | reqVal = {item.reqValue}"); - item.reqValue = ""; - // salvo in lista da ritrasmettere - updatedPar.Add(item); - } - else - { - answ += $" | Error: parameter {item.uid} not found"; - } - } - // richiamo scrittura parametri su PLC - plcWriteParams(updatedPar); - // invio su cloud parametri! - string rawData = JsonConvert.SerializeObject(updatedPar); - utils.callUrl($"{urlUpdateWriteParams}", rawData); - } - } - catch (Exception exc) - { - lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); - } - } - else - { - lgError("Non è stata ricevuta risposta x task da eseguire"); - } - return answ; - } - - protected void raiseRefresh(newDisplayData currDispData) - { - if (currDispData.hasData) - { - // segnalo refresh! - if (eh_refreshed != null) - { - eh_refreshed(this, new iobRefreshedEventArgs(currDispData)); - } - } - } - - /// - /// Cancella dal server i task eseguiti - /// - /// - /// - /// - protected string remTask2exe(string taskName, string esitoTask) - { - string answ = ""; - if (checkServerAlive) - { - string url2call = $"{urlRemTask2Exe}{taskName}"; - lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}"); - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Invia informazioni associazione IOB 2 machine - /// - protected void sendM2IOB() - { - if (checkServerAlive) - { - lgInfo("chiamata URL " + urlSetM2IOB); - utils.callUrlNow(urlSetM2IOB); - } - } - - /// - /// Invia al server IO i valori dei parametri opzionali (es counters) - /// - /// Nome parametro - /// Valore parametro - protected void sendOptVal(string paramName, string paramValue) - { - if (checkServerAlive) - { - string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}"; - lgInfo("chiamata URL " + url2call); - utils.callUrlNow(url2call); - } - } - - /// - /// Invia al server IO i valori dei parametri opzionali (es counters) - /// - /// Nome parametro - /// Valore parametro INT - protected void sendOptVal(string paramName, int paramValueInt) - { - // override! - sendOptVal(paramName, paramValueInt.ToString()); - } - - /// - /// Verifica e se necessario comprime directory log... - /// - private void checkShrinkDir() - { - string path = string.Format("{0}logs\\{1}", AppDomain.CurrentDomain.BaseDirectory, cIobConf.codIOB); - baseUtils.shrinkDir(path); - } - - private void reportDataProc() - { - // update valori visualizzazione... - parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut); - } - - private void svuotaCodaContapezzi() - { - // permetto al max 2 tentativi infruttuosi... - int maxTry = 2; - int oldContapezzi = contapezziIOB; - // se ho contapezzi OLTRE limite... - while ((MPOnline) && (contapezziPLC > contapezziIOB + minSendPzCountBlock)) - { - lgInfo($"Ciclo svuotaCodaContapezzi --> contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}"); - if (!isMulti) - { - pzCntReload(true); - } - // provo invio - trySendPzCountBlock(); - // verifica per evitare loop infinito invio fallito - if (oldContapezzi == contapezziIOB) - { - maxTry--; - } - else - { - maxTry = 2; - oldContapezzi = contapezziIOB; - } - // verifico maxTry: se li ho esauriti esco! - if (maxTry <= 0) - { - return; - } - // aspetto x dare tempo calcolo - Thread.Sleep(400); - } - } - - /// - /// Cerca di inviare su un altro thread i vari dati accumulati... - /// - private void trySendValues() - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - try - { - // verifico se risponde il server... - if (checkServerAlive) - { - bool iobOk = false; - if (utils.CRB("sendDataByThread")) - { - Task taskCheck = TaskEx.Run(() => iobOk = checkIobEnabled); - } - else - { - iobOk = checkIobEnabled; - } - // verifico SE posso inviare dati - if (iobOk) - { - currDispData.semOut = Semaforo.SV; - // verificare come gestire il task secondario senza interferenza (chiamate update su FORM da thread secondari danno errori) - if (utils.CRB("sendDataByThread")) - { - // invio con thread separato... - Task taskSigIN = TaskEx.Run(() => svuotaCodaSignIN()); - Task taskFlog = TaskEx.Run(() => svuotaCodaFLog()); - } - else - { - // gestione queue SignalIN (invio, display) - svuotaCodaSignIN(); - currDispData.counter = contapezziIOB; - raiseRefresh(currDispData); - // provo a svuotare coda contapezzi - svuotaCodaContapezzi(); - currDispData.counter = contapezziIOB; - raiseRefresh(currDispData); - // gestione queue FluxLog (invio, display) - svuotaCodaFLog(); - raiseRefresh(currDispData); - } - } - else - { - // mostro VETO-SEND x invio... GIALLO - currDispData.semOut = Semaforo.SG; - if (periodicLog) - { - lgInfo("IOB - VETO SEND"); - } - } - } - else - { - // mostro SERVER KO x invio... ROSSO - currDispData.semOut = Semaforo.SR; - if (periodicLog) - { - lgInfo("IOB - SERVER NOT READY"); - } - } - } - catch (Exception exc) - { - lgError($"Errore in fase trySendValues{Environment.NewLine}{exc}"); - currDispData.semOut = Semaforo.SR; - } - raiseRefresh(currDispData); - } - - #endregion metodi adapter - - #region layer persistenza dati - - /// - /// Dizionario di persistenza per i valori da salvare da/su file - /// - public Dictionary persistenceLayer; - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) - /// - /// - /// - private string getStoredVal(string keyVal) - { - string value = ""; - try - { - if (persistenceLayer != null) - { - if (!persistenceLayer.TryGetValue(keyVal, out value)) - { - persistenceLayer.Add(keyVal, "0"); - } - } - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc)); - } - return value; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come double - /// - /// - /// - private double getStoredValDouble(string keyVal) - { - double answ = 0; - try - { - answ = Convert.ToDouble(getStoredVal(keyVal)); - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc)); - } - answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come INT - /// - /// - /// - private long getStoredValLong(string keyVal) - { - long answ = 0; - try - { - answ = Convert.ToInt64(getStoredVal(keyVal)); - } - catch - { } - // verifico che il valore sia minore di 9/10 del valore massimo... - answ = (answ < (long.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come UINT - /// - /// - /// - private uint getStoredValUInt(string keyVal) - { - uint answ = 0; - try - { - answ = Convert.ToUInt32(getStoredVal(keyVal)); - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredValUInt: {0}{1}", Environment.NewLine, exc)); - } - // verifico che il valore sia minore di 9/10 del valore massimo... - answ = (answ < (uint.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private double updateValDoubleByIncr(int i, double delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - double contAct = getStoredValDouble(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private long updateValLongByIncr(int i, long delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - long contAct = getStoredValLong(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - /// - /// Aggiorna un valore del dizionario in SOSTITUZIONE - /// - /// - /// - /// - /// Nuovo valore incrementato - private void updateValString(int i, string newVal, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // salvo in ram! - persistenceLayer[keyVal] = newVal; - } - - /// - /// Aggiorna un valore del dizionario in SOSTITUZIONE e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private void updateValUInt(int i, uint newVal, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // salvo in ram! - persistenceLayer[keyVal] = newVal.ToString(); - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private uint updateValUIntByIncr(int i, uint delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - uint contAct = getStoredValUInt(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - #endregion layer persistenza dati - - #region area lettura configurazioni - - /// - /// Cerca parametri opzionali in modalità "like" del nome - /// - /// - /// - public Dictionary findOptPar(string keyStartSearch = "") - { - Dictionary answ = new Dictionary(); - // controllo SE keySearch !="" - if (!string.IsNullOrWhiteSpace(keyStartSearch)) - { - if (cIobConf.optPar.Count > 0) - { - // ciclo su tutti e cerco occorrenze che INIZINO... - foreach (var item in cIobConf.optPar) - { - if (item.Key.StartsWith(keyStartSearch)) - { - answ.Add(item.Key, item.Value); - } - } - } - } - return answ; - } - - /// - /// Cerca se esiste il parametro opzionale e lo restituisce - /// - /// - /// - public string getOptPar(string key) - { - string answ = ""; - if (cIobConf.optPar.Count > 0) - { - // controllo SE HO il parametro - if (cIobConf.optPar.ContainsKey(key)) - { - answ = cIobConf.optPar[key]; - } - } - return answ; - } - - /// - /// Decodifica file MAP (caso .bit) - /// - /// - /// - /// indirizzo Byte: indirizzo di partenza memoria - /// dimensione singolo slot in byte - /// indirizzo bit: numero riga x calcolo indice bit - /// - protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - int resto = 0; - Math.DivRem(BitNum, 8, out resto); - string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - - /// - /// Decodifica file MAP generico - /// - /// - /// - /// - /// - /// - /// - protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - - /// - /// Legge il file di conf di una MAP di informazioni da gestire con lettura set memoria - /// - /// nome vettore memoria - /// file origine - /// dimensione (in byte) della memoria - /// dimensione (in byte) della memoria - protected void loadConfFile(ref otherData[] vettoreConf, string nomeFile, int memSize, ref int numVett) - { - otherData lastData = new otherData(); - int totRighe = 0; - string linea; - totRighe = File.ReadLines(nomeFile).Count(); - // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco... - vettoreConf = new otherData[File.ReadLines(nomeFile).Count()]; - // carica da file... - StreamReader file = new StreamReader(nomeFile); - // leggo 1 linea alla volta... - int numRiga = 0; - int bitNum = 0; - int byteNum = 0; - while ((linea = file.ReadLine()) != null) - { - // SE non è un commento... - if (linea.Substring(0, 1) != "#") - { - // se finisce per BIT allora processo bit-a-bit... - if (linea.EndsWith("BOOL")) - { - try - { - string[] memIdx = linea.Split(utils.CRC("testCharSep"))[0].Split('.'); - // calcolo bit e byte number... - int.TryParse(memIdx[0], out byteNum); - if (memIdx.Length > 1) - { - int.TryParse(memIdx[1], out bitNum); - } - else - { - bitNum = 0; - } - } - catch - { - byteNum = 0; - bitNum = 0; - } - lastData = decodeBitData(linea, utils.CRC("testCharSep"), byteNum, 1, bitNum); - vettoreConf[numRiga] = lastData; - } - else - { - lastData = decodeOtherData(linea, utils.CRC("testCharSep"), "", 1, memSize); - vettoreConf[numRiga] = lastData; - } - numRiga++; - } - } - // salvo lunghezza file... - try - { - numVett = Convert.ToInt32(lastData.memAddr) + 1; - } - catch - { - numVett = numRiga + 1; - } - // chiudo file - file.Close(); - // ora trimmo vettore al solo numero VERO dei valori caricati... - Array.Resize(ref vettoreConf, numRiga); - - if (isVerboseLog) - { - lgInfo(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile)); - } - } - - #endregion area lettura configurazioni - - #region IOB METHODS - - /// - /// contatore x simulazione valori input - /// - public int countSim = 0; - - /// - /// DateTime Ultimo valore simulazione generato - /// - public DateTime lastSim; - /// /// Verifica se il server sia ALIVE (tramite PING) /// @@ -2464,6 +840,88 @@ namespace IOB_WIN } } + /// + /// Salva verifica stato connessione OK + /// + /// + public virtual bool connectionOk + { + get + { + return _connOk || DemoIn; + } + set + { + _connOk = value; + } + } + + /// + /// Contapezzi attuale + /// + public Int32 contapezziIOB + { + get + { + return tcMan.pzCountIOB; + } + set + { + tcMan.pzCountIOB = value; + } + } + + /// + /// Ultima lettura variabile contapezzi da CNC + /// + public Int32 contapezziPLC + { + get + { + return tcMan.pzCountPLC; + } + set + { + tcMan.pzCountPLC = value; + } + } + + /// + /// Contatore x invio dati FluxLog + /// + public int counterFLog { get; set; } + + /// + /// Contatore x invio dati SignalIN + /// + public int counterSigIN { get; set; } + + /// + /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA... + /// + public bool DemoIn + { + get + { + return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn"); + } + } + + /// + /// stato Online/Offline della IOB + /// + public bool IobOnline + { + get + { + return utils.IOB_Online; + } + set + { + utils.IOB_Online = value; + } + } + /// /// Verifica se sia machcina multi = DoppioPallet da CONF /// @@ -2492,6 +950,82 @@ namespace IOB_WIN } } + /// + /// Log verboso da configurazione (SOLO CHAIVE "verbose"... + /// + public bool isVerboseLog { get; set; } = utils.CRB("verbose"); + + /// + /// Ultimo Alarm letto + /// + public string lastAlarm { get; set; } + + /// + /// Ultimo ARRAY DynData letto + /// + public Dictionary lastDynData { get; set; } = new Dictionary(); + + /// + /// Ultimo DynData (sunto) letto + /// + public string lastDynDataCtrlVal { get; set; } + + /// + /// Ultimo Override set letto + /// + public string lastOverrideFS { get; set; } + + /// + /// Ultimo Override set letto + /// + public string lastOverrideRapid { get; set; } + + /// + /// Ultimo programma letto + /// + public string lastPrgName { get; set; } + + /// + /// Ultimo SysInfo letto + /// + public string lastSysInfo { get; set; } + + /// + /// Ultimo URL + /// + public string lastUrl { get; set; } + + /// + /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...) + /// + public bool periodicLog + { + get + { + bool answ = false; + answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); + if (answ) + { + lastPeriodicLog = DateTime.Now; + } + + return answ; + } + } + + /// + /// indica se ping disabilitato da optPar + /// + public bool pingDisabled + { + get + { + bool answ = false; + bool.TryParse(getOptPar("NO_PING"), out answ); + return answ; + } + } + /// /// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)... /// @@ -2854,154 +1388,1279 @@ namespace IOB_WIN } /// - /// Valore del num max invii consecutivi da coda... + /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN) /// - protected static int nMaxSend - { - get - { - int answ = 5; - try - { - answ = utils.CRI("nMaxSend"); - } - catch - { } - return answ; - } - } - - /// - /// Verifica se la IOB sia ENABLED (da server o Demo) - /// - private bool checkIobEnabled + public bool verboseLog { get { bool answ = false; - // controllo se ho veto al check... - if (dtVetoCheckIOB < DateTime.Now) + int logEvery = utils.CRI("logEvery"); + if (logEvery < 1) { - if (DemoOut) - { - answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); - } - else - { - try - { - // chiamo URL, se restituisce "OK" è enabled! - string callResp = callUrl(urlIobEnabled, true); - answ = (callResp == "OK"); - // attesa casuale se necessario - var rand = new Random(); - // primi 2 test - int maxTry = 2; - while (maxTry > 0 && !answ) - { - Thread.Sleep(rand.Next(250, 500)); - callResp = callUrl(urlIobEnabled, true); - answ = (callResp == "OK"); - maxTry--; - } - // se NON OK riprovo ANCORA 1 volta... - if (!answ) - { - resetWebClients(); - Thread.Sleep(rand.Next(250, 1000)); - callResp = callUrl(urlIobEnabled, false); - answ = (callResp == "OK"); - } - // altri 2 - maxTry = 2; - while (maxTry > 0 && !answ) - { - Thread.Sleep(rand.Next(250, 500)); - callResp = callUrl(urlIobEnabled, false); - answ = (callResp == "OK"); - maxTry--; - } - // salvo status... - IobOnline = answ; - // se online imposto veto check a 5 x tempo reinvio... - if (answ) - { - lastIobOnline = DateTime.Now; - } - dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); - } - catch - { } - } - // verifico SE è variato stato online/offline... - if (IobOnline != answ) - { - // se ORA sono online riporto... - if (answ) - { - lgInfo("IOB ONLINE for server MP/IO"); - } - else - { - lgInfo("IOB OFFLINE for server MP/IO"); - } - } - // fix colore - if (answ) - { - parentForm.commSrvActive = 2; - } - else - { - parentForm.commSrvActive = 1; - } - } - else - { - // altrimenti passo ultimo valore noto - answ = IobOnline; + logEvery = 10; } + + answ = utils.CRB("verbose") && (nReadIN % logEvery == 0); return answ; } } + #endregion Public Properties + + #region Private Methods + /// - /// test ping all'indirizzo impostato nei parametri + /// Verifica e se necessario comprime directory log... /// - /// - private IPStatus testPingServer + private void checkShrinkDir() { - get + string path = string.Format("{0}logs\\{1}", AppDomain.CurrentDomain.BaseDirectory, cIobConf.codIOB); + baseUtils.shrinkDir(path); + } + + /// + /// Mostra i dati grezzi letti in esadecimale + /// Parametri da aggiornare x display in form + /// + private void displayRawData(ref newDisplayData currDispData) + { + // mostro update... + string newString = string.Format("{0:X}", B_input); + currDispData.newInData = newString; +#if false + // salvo coda debug... + QueueDebug.Enqueue(B_input); +#endif + } + + /// + /// Esegue filtraggio dati x bit blinking!!! + /// + private void filterData() + { + // effettuo filtraggio dei valori letti... inizializzo OUT! + B_output = 0; + // in primis verifico SE ci siano bit blinkng... se non ci sono OUT=IN... + if (cIobConf.BLINK_FILT == 0) { - IPStatus answ = IPStatus.Unknown; ; - IPAddress address; - PingReply reply; - using (Ping pingSender = new Ping()) + B_output = B_input; + } + else + { + // incomincio con i valori NON blinking: questi "passano invariati", inizio a sommare nel valore OUT... + B_output = B_input & ~cIobConf.BLINK_FILT; + // calcolo il valore dei BIT che "passano la maschera" + int iBlink = B_input & cIobConf.BLINK_FILT; + // ...aggiungo i "bit che passano" + B_output += iBlink; + + // calcolo QUALI valori (tra quelli blink) siano PASSATI da 0 a 1 --> init counters... + BitArray bBlinkStart = new BitArray(new byte[] { Convert.ToByte(iBlink) }); + int[] bitsUp = bBlinkStart.Cast().Select(bit => bit ? 1 : 0).ToArray(); + for (int i = 0; i < bitsUp.Length; i++) { - address = IPAddress.Loopback; - int maxRetry = maxPingRetry + 1; - int numRetry = 1; ; - string ipAdrr = cIobConf.serverData.MPIP.Replace("http://", "").Replace("https://", ""); - IPAddress.TryParse(ipAdrr, out address); - reply = pingSender.Send(address, pingServerMsTimeout); - // se ho timeout riprovo... - while (reply.Status != IPStatus.Success && numRetry < maxRetry) + // SE 1... impostiamo contatori al MAX + if (bitsUp[i] == 1) { - lgInfo($"Ping KO | reply: {reply.Status} --> retry"); - reply = pingSender.Send(address, pingServerMsTimeout * numRetry / 2); - numRetry++; - if (reply.Status == IPStatus.Success) + // se era zero indico START blink... + if (i_counters[i] == 0) { - lgInfo("PING OK!"); - break; + lgInfo("START BLINK: B{0}", i); + } + // imposto comunque contatore al cambio fronte... + i_counters[i] = cIobConf.MAX_COUNTER_BLINK; + } + } + + // quelli che sono zero... LI RECUPERO E LI PROCESSO... + int iZero = ~B_input & cIobConf.BLINK_FILT; + BitArray bBlinkEnd = new BitArray(new byte[] { Convert.ToByte(iZero) }); + int[] bitsDown = bBlinkEnd.Cast().Select(bit => bit ? 1 : 0).ToArray(); + for (int i = 0; i < bitsDown.Length; i++) + { + // se era a zero (invertito...) + if (bitsDown[i] == 1) + { + // SE è in corso il conteggio... + if (i_counters[i] > 0) + { + // decremento! + i_counters[i] -= 1; + // se è zero NON faccio nulla, altrimenti SOMMO... + if (i_counters[i] > 0) + { + B_output += 1 << i; + } + else + { + lgInfo("END BLINK: B{0}", i); + } } } } - answ = reply.Status; - return answ; } } + /// + /// Imposta eventuali altri valori default + /// + private void fixDefaultPar() + { + // parametro max tentativi PING... + string s_maxPingRetry = getOptPar("MAX_PING_RETRY"); + if (!string.IsNullOrEmpty(s_maxPingRetry)) + { + int numRetry = 5; + int.TryParse(s_maxPingRetry, out numRetry); + maxPingRetry = numRetry; + } + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) + /// + /// + /// + private string getStoredVal(string keyVal) + { + string value = ""; + try + { + if (persistenceLayer != null) + { + if (!persistenceLayer.TryGetValue(keyVal, out value)) + { + persistenceLayer.Add(keyVal, "0"); + } + } + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc)); + } + return value; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come double + /// + /// + /// + private double getStoredValDouble(string keyVal) + { + double answ = 0; + try + { + answ = Convert.ToDouble(getStoredVal(keyVal)); + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc)); + } + answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come INT + /// + /// + /// + private long getStoredValLong(string keyVal) + { + long answ = 0; + try + { + answ = Convert.ToInt64(getStoredVal(keyVal)); + } + catch + { } + // verifico che il valore sia minore di 9/10 del valore massimo... + answ = (answ < (long.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come UINT + /// + /// + /// + private uint getStoredValUInt(string keyVal) + { + uint answ = 0; + try + { + answ = Convert.ToUInt32(getStoredVal(keyVal)); + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredValUInt: {0}{1}", Environment.NewLine, exc)); + } + // verifico che il valore sia minore di 9/10 del valore massimo... + answ = (answ < (uint.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... + /// + private void processAllMemory() + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // in primis SALVO valori previous/precedenti + B_previous = B_output; + // poi faccio lettura NUOVI valori + readAllData(ref currDispData); + // eseguo il filtering dei valori (per i bit "blinking") + filterData(); + // effettuo confronto valori vecchi/nuovi... SE trovo variazione OPPURE se è passato + di un timeout di controllo... + if (B_output != B_previous) + { + accodaSigIN(ref currDispData); + } + raiseRefresh(currDispData); + } + + /// + /// Effettua gestioen programma: legge e mostra su display... + /// + private void processProgram() + { + string currPrgName = ""; + // se abilitata lettura prgName + if (enablePrgName) + { + if (connectionOk) + { + currPrgName = getPrgName(); + } + else + { + lgError("Errore connessione mancante x getPrgName"); + } + } + else + { + currPrgName = lastPrgName; + } + // verifico SE sia cambiato il programma... + if (lastPrgName != currPrgName) + { + // salvo! + lastPrgName = currPrgName; + string sVal = string.Format("[PROG]{0}", currPrgName); + + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog("PROG", currPrgName)); + } + } + + /// + /// Processo lettura dati sysinfo + /// + private void processSysInfo() + { + if (utils.CRB("enableSysInfo")) + { + Dictionary currSysInfo = new Dictionary(); + + if (connectionOk) + { + currSysInfo = getSysInfo(); + } + else + { + lgError("Errore connessione mancante x getSysInfo"); + } + // verifico SE sia cambiato il programma... + if (lastSysInfo != currSysInfo["SYSINFO"]) + { + // salvo! + lastSysInfo = currSysInfo["SYSINFO"]; + // per ogni valore del dizionario mostro ed accodo! + string sVal = ""; + foreach (var item in currSysInfo) + { + sVal = string.Format("[SYSINFO]{0}|{1}", item.Key, item.Value); + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); + } + } + } + } + + private void reportDataProc() + { + // update valori visualizzazione... + parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut); + } + + /// + /// Imposto alcuni valori di default + /// + /// indica se sia richeisto di SVUOTARE le code delel info + private void setDefaults(bool resetQueue) + { + numSim = utils.CRI("numSim"); + lastPrgName = ""; + nReadIN = 0; + nReadFilt = 0; + nSendOut = 0; + currMode = 0; + lastAlarm = ""; + doStartMemDump = utils.CRB("doStartMemDump"); + doSampleMemory = utils.CRB("doSampleMemory"); + // svuoto code se richiesto + if (resetQueue) + { + QueueIN = new ConcurrentQueue(); + QueueFLog = new ConcurrentQueue(); + QueueAlarm = new ConcurrentQueue(); + QueueMessages = new ConcurrentQueue(); + } + // imposto contatori blink a zero... + i_counters = new int[32]; + lastPeriodicLog = DateTime.Now; + // fix parametri generali... + enablePrgName = true; + } + + private void svuotaCodaContapezzi() + { + // permetto al max 2 tentativi infruttuosi... + int maxTry = 2; + int oldContapezzi = contapezziIOB; + // se ho contapezzi OLTRE limite... + while ((MPOnline) && (contapezziPLC > contapezziIOB + minSendPzCountBlock)) + { + lgInfo($"Ciclo svuotaCodaContapezzi --> contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}"); + if (!isMulti) + { + pzCntReload(true); + } + // provo invio + trySendPzCountBlock(); + // verifica per evitare loop infinito invio fallito + if (oldContapezzi == contapezziIOB) + { + maxTry--; + } + else + { + maxTry = 2; + oldContapezzi = contapezziIOB; + } + // verifico maxTry: se li ho esauriti esco! + if (maxTry <= 0) + { + return; + } + // aspetto x dare tempo calcolo + Thread.Sleep(400); + } + } + + /// + /// Processo la coda FLog... + /// + private void svuotaCodaFLog() + { + //controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! + if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) + { + string wdStatus = "elapsed"; + string sVal = string.Format("[WDST]{0}", wdStatus); + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); + lastWatchDog = DateTime.Now; + } + // verifico SE la coda abbia dei valori... + if (QueueFLog.Count > 0) + { + // invio pacchetto di dati (max da conf) + for (int i = 0; i < nMaxSend; i++) + { + // SE ho qualcosa in coda... + if (QueueFLog.Count > 0) + { + string currVal = ""; + if (MPOnline) + { + if (IobOnline) + { + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueFLog.Count > 1) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueFLog.Count > maxJsonData) + { + // prendoi primi maxJsonDataValori + for (int j = 0; j < maxJsonData; j++) + { + QueueFLog.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.FLog, listaValori); + } + else + { + // invio in blocco + listaValori = QueueFLog.ToList(); + // invio + sendDataBlock(urlType.FLog, listaValori); + // svuoto! + QueueFLog = new ConcurrentQueue(); + } + } + else + { + // INVIO SINGOLO...!!! + QueueFLog.TryDequeue(out currVal); + sendToMoonPro(urlType.FLog, currVal); + } + } + else + { + break; + } + } + else + { + break; + } + } + else + { + break; + } + } + } + } + + /// + /// Cerca di inviare su un altro thread i vari dati accumulati... + /// + private void trySendValues() + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + try + { + // verifico se risponde il server... + if (checkServerAlive) + { + bool iobOk = false; + if (utils.CRB("sendDataByThread")) + { + Task taskCheck = TaskEx.Run(() => iobOk = checkIobEnabled); + } + else + { + iobOk = checkIobEnabled; + } + // verifico SE posso inviare dati + if (iobOk) + { + currDispData.semOut = Semaforo.SV; + // verificare come gestire il task secondario senza interferenza (chiamate update su FORM da thread secondari danno errori) + if (utils.CRB("sendDataByThread")) + { + // invio con thread separato... + Task taskSigIN = TaskEx.Run(() => svuotaCodaSignIN()); + Task taskFlog = TaskEx.Run(() => svuotaCodaFLog()); + } + else + { + // gestione queue SignalIN (invio, display) + svuotaCodaSignIN(); + currDispData.counter = contapezziIOB; + raiseRefresh(currDispData); + // provo a svuotare coda contapezzi + svuotaCodaContapezzi(); + currDispData.counter = contapezziIOB; + raiseRefresh(currDispData); + // gestione queue FluxLog (invio, display) + svuotaCodaFLog(); + raiseRefresh(currDispData); + } + } + else + { + // mostro VETO-SEND x invio... GIALLO + currDispData.semOut = Semaforo.SG; + if (periodicLog) + { + lgInfo("IOB - VETO SEND"); + } + } + } + else + { + // mostro SERVER KO x invio... ROSSO + currDispData.semOut = Semaforo.SR; + if (periodicLog) + { + lgInfo("IOB - SERVER NOT READY"); + } + } + } + catch (Exception exc) + { + lgError($"Errore in fase trySendValues{Environment.NewLine}{exc}"); + currDispData.semOut = Semaforo.SR; + } + raiseRefresh(currDispData); + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private double updateValDoubleByIncr(int i, double delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + double contAct = getStoredValDouble(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private long updateValLongByIncr(int i, long delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + long contAct = getStoredValLong(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + /// + /// Aggiorna un valore del dizionario in SOSTITUZIONE + /// + /// + /// + /// + /// Nuovo valore incrementato + private void updateValString(int i, string newVal, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // salvo in ram! + persistenceLayer[keyVal] = newVal; + } + + /// + /// Aggiorna un valore del dizionario in SOSTITUZIONE e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private void updateValUInt(int i, uint newVal, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // salvo in ram! + persistenceLayer[keyVal] = newVal.ToString(); + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private uint updateValUIntByIncr(int i, uint delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + uint contAct = getStoredValUInt(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + #endregion Private Methods + + #region Protected Methods + + /// + /// Decodifica file MAP (caso .bit) + /// + /// + /// + /// indirizzo Byte: indirizzo di partenza memoria + /// dimensione singolo slot in byte + /// indirizzo bit: numero riga x calcolo indice bit + /// + protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + int resto = 0; + Math.DivRem(BitNum, 8, out resto); + string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + /// + /// Decodifica file MAP generico + /// + /// + /// + /// + /// + /// + /// + protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + /// + /// Decodifica valore della coda IN nel formato + /// answ[0]=dtEve + /// answ[1]=valore + /// answ[2]=counter + /// + /// dtEve + '#' + value + '#' + cont + /// + protected static string[] qDecodeIN(string queueVal) + { + string[] answ = null; + if (!string.IsNullOrEmpty(queueVal)) + { + try + { + answ = queueVal.Split('#'); + } + catch + { } + } + return answ; + } + + /// + /// Stringa raw dei parametri da scrivere... + /// + /// + protected string getParams2write() + { + string answ = ""; + string url2call = $"{urlGetParams2Write}"; + if (verboseLog) + { + lgInfo("chiamata URL " + url2call); + } + answ = utils.callUrlNow(url2call); + // se vuoto faccio seconda prova... + if (string.IsNullOrEmpty(answ)) + { + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Chiede elenco dei task da eseguire + /// - formato Json + /// - array di KVP / Dictionary + /// - formato definito da API x MP/IO/: + /// - KEY: task + /// - VALUE: array JSon KVP + /// + protected string getTask2exe() + { + string answ = ""; + if (checkServerAlive) + { + string url2call = $"{urlGetTask2Exe}"; + if (verboseLog) + { + lgInfo("chiamata URL " + url2call); + } + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgError(string message, bool sendToForm = true) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(message); + if (sendToForm) + { + sendToLogWatch("ERROR", message); + } + } + + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgError(string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(message, args); + sendToLogWatch("ERROR", message, args); + } + + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + /// + protected void lgError(Exception exception, string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(exception, message, args); + sendToLogWatch("ERROR", message, exception, args); + } + + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgFatal(string message, bool sendToForm = true) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(message); + if (sendToForm) + { + sendToLogWatch("FATAL", message); + } + } + + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgFatal(string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(message, args); + sendToLogWatch("FATAL", message, args); + } + + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + /// + protected void lgFatal(Exception exception, string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(exception, message, args); + sendToLogWatch("FATAL", message, exception, args); + } + + /// + /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgInfo(string message, bool sendToForm = true) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Info(message); + if (sendToForm) + { + sendToLogWatch("INFO", message); + } + } + + /// + /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgInfo(string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Info(message, args); + sendToLogWatch("INFO", message, args); + } + + /// + /// Legge il file di conf di una MAP di informazioni da gestire con lettura set memoria + /// + /// nome vettore memoria + /// file origine + /// dimensione (in byte) della memoria + /// dimensione (in byte) della memoria + protected void loadConfFile(ref otherData[] vettoreConf, string nomeFile, int memSize, ref int numVett) + { + otherData lastData = new otherData(); + int totRighe = 0; + string linea; + totRighe = File.ReadLines(nomeFile).Count(); + // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco... + vettoreConf = new otherData[File.ReadLines(nomeFile).Count()]; + // carica da file... + StreamReader file = new StreamReader(nomeFile); + // leggo 1 linea alla volta... + int numRiga = 0; + int bitNum = 0; + int byteNum = 0; + while ((linea = file.ReadLine()) != null) + { + // SE non è un commento... + if (linea.Substring(0, 1) != "#") + { + // se finisce per BIT allora processo bit-a-bit... + if (linea.EndsWith("BOOL")) + { + try + { + string[] memIdx = linea.Split(utils.CRC("testCharSep"))[0].Split('.'); + // calcolo bit e byte number... + int.TryParse(memIdx[0], out byteNum); + if (memIdx.Length > 1) + { + int.TryParse(memIdx[1], out bitNum); + } + else + { + bitNum = 0; + } + } + catch + { + byteNum = 0; + bitNum = 0; + } + lastData = decodeBitData(linea, utils.CRC("testCharSep"), byteNum, 1, bitNum); + vettoreConf[numRiga] = lastData; + } + else + { + lastData = decodeOtherData(linea, utils.CRC("testCharSep"), "", 1, memSize); + vettoreConf[numRiga] = lastData; + } + numRiga++; + } + } + // salvo lunghezza file... + try + { + numVett = Convert.ToInt32(lastData.memAddr) + 1; + } + catch + { + numVett = numRiga + 1; + } + // chiudo file + file.Close(); + // ora trimmo vettore al solo numero VERO dei valori caricati... + Array.Resize(ref vettoreConf, numRiga); + + if (isVerboseLog) + { + lgInfo(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile)); + } + } + + /// + /// Lettura memorie conf speciali (json) + /// ...SE inserito in [OPTPAR] come PARAM_CONF=nome.json + /// + protected virtual void loadMemConf() + { + lgInfo("BEGIN loadMemConf"); + // variabili x gestione send contapezzi in blocco + string currPar = getOptPar("ENABLE_SEND_PZC_BLOCK"); + if (!string.IsNullOrEmpty(currPar)) + { + bool.TryParse(currPar, out enableSendPzCountBlock); + // se abilitato leggo num pezzi da reinviare in blocco + if (enableSendPzCountBlock) + { + int.TryParse(getOptPar("MAX_SEND_PZC_BLOCK"), out maxSendPzCountBlock); + int.TryParse(getOptPar("MIN_SEND_PZC_BLOCK"), out minSendPzCountBlock); + } + } + else + { + lgError("loadMemConf: parametro ENABLE_SEND_PZC_BLOCK non trovato, verificare anche MAX_SEND_PZC_BLOCK e MIN_SEND_PZC_BLOCK"); + } + // inizializzo LUT decodifica + string jsonConf = getOptPar("PARAM_CONF"); + if (!string.IsNullOrEmpty(jsonConf)) + { + string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonConf}"; + lgInfo($"Apertura file {jsonFileName}"); + StreamReader reader = new StreamReader(jsonFileName); + string jsonData = reader.ReadToEnd(); + if (!string.IsNullOrEmpty(jsonData)) + { + lgInfo($"File json composto da {jsonData.Length} caratteri"); + try + { + memMap = JsonConvert.DeserializeObject(jsonData); + lgInfo($"Decodifica aree memMap: trovati {memMap.mMapRead.Count} valori TSVC"); + lgInfo($"Decodifica aree memMap: trovati {memMap.mMapWrite.Count} parametri "); + // se ho variabili read --> genero dati TSVC... + if (memMap.mMapRead.Count > 0) + { + TSVC_Data.Clear(); + LastTSVC.Clear(); + VCData currConf; + int periodo = 0; + VC_func funz = VC_func.POINT; + // accodo nella conf... + foreach (var item in memMap.mMapRead) + { + funz = item.Value.func; + periodo = item.Value.period; + currConf = new VCData() + { + Funzione = funz, + Period = periodo, + DTStart = DateTime.Now.AddHours(-1), + dataArray = new List() + }; + TSVC_Data.Add(item.Key, currConf); + } + // documento... + foreach (var item in TSVC_Data) + { + lgInfo($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}"); + // salvo i valori PREC... + LastTSVC.Add(item.Key, 0); + } + } + // infine se obj memoria valido salvo in MP-IO x sue applicazioni + if (memMap != null) + { + // invio su cloud conf memoria... + string rawData = JsonConvert.SerializeObject(memMap); + utils.callUrlNow($"{urlSaveMemMap}", rawData); + // salvo ANCHE come parametri i valori... + objItem currItem = new objItem(); + List allParam = new List(); + // valori WRITE + foreach (var item in memMap.mMapWrite) + { + currItem = new objItem() + { + uid = item.Value.name, + name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, + writable = true + }; + allParam.Add(currItem); + } + // valori READ + foreach (var item in memMap.mMapRead) + { + currItem = new objItem() + { + uid = item.Value.name, + name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, + writable = false + }; + allParam.Add(currItem); + } + // invio su cloud parametri! + rawData = JsonConvert.SerializeObject(allParam); + utils.callUrl($"{urlSaveAllParams}", rawData); + } + } + catch (Exception exc) + { + lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}"); + } + } + else + { + lgError("Errore in loadMemConf: file json vuoto!"); + } + reader.Dispose(); + } + else + { + lgInfo("loadMemConf: non trovata opzione PARAM_CONF in file INI"); + } + // loggo + lgInfo("DONE loadMemConf"); + } + + /// + /// Metodo da overridare x scrivere DAVVERO i aprametri sul PLC + /// + /// + protected virtual void plcWriteParams(List updatedPar) + { + // non faccio nulla di base... + } + + /// + /// Processa le richieste di scrittura memoria + /// + /// + protected string processMemWriteRequests() + { + string answ = ""; + // li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC) + List writeList = new List(); + List updatedPar = new List(); + // recupero elenco delle cose da fare + string resp = getParams2write(); + if (!string.IsNullOrEmpty(resp)) + { + try + { + writeList = JsonConvert.DeserializeObject>(resp); + // se ho da fare chiamo esecuzione.. + if (writeList.Count > 0) + { + foreach (var item in writeList) + { + // scrivo in memoria + if (memMap.mMapWrite.ContainsKey(item.uid)) + { + memMap.mMapWrite[item.uid].value = item.reqValue; + // accodo in stringa taskVal... + answ += $" | Parameter {item.uid} --> {item.reqValue}"; + // sistemo valori + item.value = item.reqValue; + lgInfo($"Effettuato update parametro: actVal = {item.value} | reqVal = {item.reqValue}"); + item.reqValue = ""; + // salvo in lista da ritrasmettere + updatedPar.Add(item); + } + else + { + answ += $" | Error: parameter {item.uid} not found"; + } + } + // richiamo scrittura parametri su PLC + plcWriteParams(updatedPar); + // invio su cloud parametri! + string rawData = JsonConvert.SerializeObject(updatedPar); + utils.callUrl($"{urlUpdateWriteParams}", rawData); + } + } + catch (Exception exc) + { + lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); + } + } + else + { + lgError("Non è stata ricevuta risposta x task da eseguire"); + } + return answ; + } + + protected void raiseRefresh(newDisplayData currDispData) + { + if (currDispData != null) + { + if (currDispData.hasData) + { + // segnalo refresh! + if (eh_refreshed != null) + { + eh_refreshed(this, new iobRefreshedEventArgs(currDispData)); + } + } + } + } + + /// + /// Cancella dal server i task eseguiti + /// + /// + /// + /// + protected string remTask2exe(string taskName, string esitoTask) + { + string answ = ""; + if (checkServerAlive) + { + string url2call = $"{urlRemTask2Exe}{taskName}"; + lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}"); + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Invia informazioni associazione IOB 2 machine + /// + protected void sendM2IOB() + { + if (checkServerAlive) + { + lgInfo("chiamata URL " + urlSetM2IOB); + utils.callUrlNow(urlSetM2IOB); + } + } + + /// + /// Invia al server IO i valori dei parametri opzionali (es counters) + /// + /// Nome parametro + /// Valore parametro + protected void sendOptVal(string paramName, string paramValue) + { + if (checkServerAlive) + { + string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}"; + lgInfo("chiamata URL " + url2call); + utils.callUrlNow(url2call); + } + } + + /// + /// Invia al server IO i valori dei parametri opzionali (es counters) + /// + /// Nome parametro + /// Valore parametro INT + protected void sendOptVal(string paramName, int paramValueInt) + { + // override! + sendOptVal(paramName, paramValueInt.ToString()); + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + protected void sendToLogWatch(string messType, string message) + { + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; + parentForm.updateFormDisplay(currDispData); + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + /// + protected void sendToLogWatch(string messType, string message, params object[] args) + { + try + { + string expString = string.Format(message, args); + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}"; + parentForm.updateFormDisplay(currDispData); + } + catch + { } + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + /// + /// + protected void sendToLogWatch(string messType, string message, Exception exception, params object[] args) + { + try + { + string expString = string.Format(message, args); + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}{Environment.NewLine}{exception}"; + parentForm.updateFormDisplay(currDispData); + } + catch + { } + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + protected void sendToTaskWatch(string messType, string message) + { + parentForm.taskWatcher = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; + } + + /// + /// Impostazioni parametri PLC + /// + protected virtual void setParamPlc() + { + loadMemConf(); + fixDefaultPar(); + } + + #endregion Protected Methods + + #region Public Methods + /// /// Effettua chiamata URL e restituisce risultato /// @@ -3049,6 +2708,13 @@ namespace IOB_WIN return answ; } + /// + /// processa dataLayer e se necessario salva/mostra + /// + public static void checkSavePersDataLayer() + { + } + public static string GetMACAddress() { NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); @@ -3065,6 +2731,10 @@ namespace IOB_WIN return sMacAddress; } + public static void resetDebugConsole() + { + } + /// /// Reset dei webclients /// @@ -3073,6 +2743,257 @@ namespace IOB_WIN utils.resetWebClients(); } + /// + /// Accumula in coda i valori ALARM e logga... + /// + /// VALORE RAW (x display) + /// VALORE già processato con qEncodeFLog(...) + public void accodaAlarmLog(string val, string encodedVal) + { + // mostro dati variati letti... + displayOtherData(val); + // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! + QueueFLog.Enqueue(encodedVal); + // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati... + + // loggo! + lgInfo(string.Format("[QUEUE-ALARM-LOG] {0}", encodedVal)); + counterFLog++; + if (counterFLog > 9999) + { + counterFLog = 0; + } + } + + /// + /// Accumula in coda i valori Flux Log e logga... + /// + /// VALORE RAW (x display) + /// VALORE già processato con qEncodeFLog(...) + public void accodaFLog(string val, string encodedVal) + { + // mostro dati variati letti... + displayOtherData(val); + // --> accodo (valore già formattato)! + QueueFLog.Enqueue(encodedVal); + // se abilitato controllo coda FLog (superiore a 0...) + if (maxQueueFLog > 0) + { + // se ho una coda superiore a max ammesso + if (QueueFLog.Count > maxQueueFLog) + { + // elimino valori iniziali fino a tornare al max ammesso... + while (QueueFLog.Count > maxQueueFLog) + { + string currVal = ""; + QueueFLog.TryDequeue(out currVal); + lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}"); + } + } + } + // loggo! + lgInfo(string.Format("[QUEUE-FLOG] {0}", encodedVal)); + counterFLog++; + if (counterFLog > 9999) + { + counterFLog = 0; + } + } + + /// + /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA + /// + /// + public void accodaOtherData(string newLine) + { + // inserisco in cima allo stack, trimmo e aggiorno display + string strOtherData = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3)); + //parentForm.dataMonitor_3 = strOtherData; + parentForm.WriteTextSafe(strOtherData); + } + + /// + /// Accumula in coda i valori Signal IN e logga... + /// Parametri da aggiornare x display in form + /// + public void accodaSigIN(ref newDisplayData currDispData) + { + // mostro dati variati letti... + displayInData(ref currDispData); + // --> accodo (valore già formattato)! + QueueIN.Enqueue(qEncodeIN); + // loggo! + lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN)); + // aggiorno counters ed eventuale reset + nReadFilt++; + if (nReadFilt > int.MaxValue - 1) + { + nReadFilt = 0; // per evitare buffer overflow... + } + + counterSigIN++; + if (counterSigIN > 9999) + { + counterSigIN = 0; + } + } + + /// + /// Update visualizzaizone BIT in ingresso + /// Parametri da aggiornare x display in form + /// + public void displayInData(ref newDisplayData currDispData) + { + if (currDispData != null) + { + // mostro update... + string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr(B_output, 8)); + currDispData.newSignalData = newString; + } + } + + /// + /// Mostra cosa ha/avrebbe inviato + /// + /// + public void displayOtherData(string newData) + { + // mostro update... + accodaOtherData(newData); + } + + /// + /// Esecuzione dei task richiesti e pulizia coda richieste eseguite + /// + /// + public virtual Dictionary executeTasks(Dictionary task2exe) + { + // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti... + Dictionary taskDone = new Dictionary(); + if (task2exe != null) + { + bool taskOk = false; + string taskVal = ""; + // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4 + foreach (var item in task2exe) + { + taskOk = false; + taskVal = ""; + // converto richiesta in enum... + taskType tName = taskType.nihil; + Enum.TryParse(item.Key, out tName); + // controllo sulla KEY... + switch (tName) + { + case taskType.nihil: + case taskType.fixStopSetup: + case taskType.forceSetPzCount: + case taskType.sendWatchDogMes2Plc: + taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; + lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.setArt: + case taskType.setComm: + case taskType.setProg: + case taskType.setPzComm: + // recupero dati da memMap... + if (memMap.mMapWrite.ContainsKey(item.Key)) + { + dataConf currMem = memMap.mMapWrite[item.Key]; + string addr = currMem.memAddr; + taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte"; + // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà + memMap.mMapWrite[item.Key].value = item.Value; + } + else + { + taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}"; + } + break; + + case taskType.forceResetPzCount: + // reset contapezzi inizio setup + taskOk = resetcontapezziPLC(); + taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.startSetup: + // reset contapezzi inizio setup + taskOk = resetcontapezziPLC(); + taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.stopSetup: + // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO + if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE") + { + taskOk = resetcontapezziPLC(); + } + taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.setParameter: + // richiedo da URL i parametri WRITE da popolare + lgInfo("Chiamata processMemWriteRequests"); + taskVal = processMemWriteRequests(); + // se restituiscce "" faccio altra prova... + if (string.IsNullOrEmpty(taskVal)) + { + // i parametri me li aspetto come stringa composta paramName|paramvalue + if (item.Value.Contains("|")) + { + string[] paramsJob = item.Value.Split('|'); + taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; + } + else + { + taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; + } + } + break; + + default: + taskVal = "SKIPPED | NO EXEC"; + lgInfo($"Chiamata default senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + } + // aggiungo task! + taskDone.Add(item.Key, taskVal); + } + } + return taskDone; + } + + /// + /// Cerca parametri opzionali in modalità "like" del nome + /// + /// + /// + public Dictionary findOptPar(string keyStartSearch = "") + { + Dictionary answ = new Dictionary(); + // controllo SE keySearch !="" + if (!string.IsNullOrWhiteSpace(keyStartSearch)) + { + if (cIobConf.optPar.Count > 0) + { + // ciclo su tutti e cerco occorrenze che INIZINO... + foreach (var item in cIobConf.optPar) + { + if (item.Key.StartsWith(keyStartSearch)) + { + answ.Add(item.Key, item.Value); + } + } + } + } + return answ; + } + /// /// Effettua chaimata x split ODL /// @@ -3165,6 +3086,157 @@ namespace IOB_WIN return fatto; } + /// + /// effettua recupero dati ed invio valori modificati... + /// + /// + public void getAndSend(gatherCycle ciclo) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!! + try + { + trySendValues(); + } + catch (Exception exc) + { + lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria"); + currDispData.semOut = Semaforo.SR; + } + // controllo connessione/connettività + if (connectionOk) + { + // controllo non sia già in esecuzione... + if (!adpCommAct) + { + // provo ad avviare + try + { + // imposto flag adapter running.. + adpCommAct = true; + adpStartRun = DateTime.Now; + } + catch (Exception exc) + { + string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}"; + adpCommAct = false; + adpStartRun = DateTime.Now; + currDispData.newLiveLogData = errore; + } + if (adpCommAct) + { + // try / catch generale altrimenti segno che è disconnesso... + try + { + bool showDebugData = false; + if (ciclo == gatherCycle.VHF) + { + processVHF(); + } + // processing dati memoria (lettura, filtraggio, enqueque) + else if (ciclo == gatherCycle.HF) + { + processWhatchDog(); + processAllMemory(); + processMode(); + } + else if (ciclo == gatherCycle.MF) + { + processServerRequests(); + processOverride(); + processContapezzi(); + processCncAlarms(); + processDynData(); + } + else if (ciclo == gatherCycle.LF) + { + processOtherCounters(); + processProgram(); + // verifico se devo gestire cambio ODL in modo automatico + processAutoOdl(); + } + else if (ciclo == gatherCycle.VLF) + { + if (utils.CRB("enableContapezzi")) + { + // rilettura contapezzi da server... + lgInfo("Ciclo VLF: pzCntReload(true)"); + if (!isMulti) + { + pzCntReload(true); + } + // refresh associazione Macchina - IOB + sendM2IOB(); + } + // recupero dati SETUP (sysinfo) e li invio/mostro se variati... + processSysInfo(); + // checkLogDir x shrink! + checkShrinkDir(); + // eventuale log! + if (utils.CRB("recTime")) + { + logTimeResults(); + } + } + // mostra eventuali altri dati di processo... + reportDataProc(); + if (showDebugData) + { + // verifica se debba salvare e mostrare dati + checkSavePersDataLayer(); + } + } + catch (Exception exc) + { + // segnalo eccezione e indico disconnesso... + lgError(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc)); + parentForm.fermaAdapter(true, false, true); + } + // tolgo flag running + adpCommAct = false; + } + else + { + if (periodicLog) + { + lgInfo("ADP not running..."); + } + } + } + else + { + // log ADP running + lgError("Non eseguo chiamata: ADP ancora in running"); + // se è bloccato da oltre maxSec lo sblocco... + if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) + { + // tolgo flag running + adpCommAct = false; + adpStartRun = DateTime.Now; + } + } + } + else + { + // provo a riconnettere SE abilitato tryRestart... + if (adpTryRestart && !connectionOk) + { + // controllo se sia scaduto periodi di veto al tryConnect... + int waitRecMSec = utils.CRI("waitRecMSec") * 2; + DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); + if (DateTime.Now > dtVeto) + { + lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect"); + lastConnectTry = DateTime.Now; + tryConnect(); + } + } + currDispData.semIn = Semaforo.SR; + } + raiseRefresh(currDispData); + } + /// /// Recupera eventuali allarmi CNC... /// @@ -3184,6 +3256,25 @@ namespace IOB_WIN return outVal; } + /// + /// Cerca se esiste il parametro opzionale e lo restituisce + /// + /// + /// + public string getOptPar(string key) + { + string answ = ""; + if (cIobConf.optPar.Count > 0) + { + // controllo SE HO il parametro + if (cIobConf.optPar.ContainsKey(key)) + { + answ = cIobConf.optPar[key]; + } + } + return answ; + } + /// /// Restituisce info OVERRIDES /// @@ -3212,6 +3303,60 @@ namespace IOB_WIN return outVal; } + /// + /// Recupera la VC x TS, svuotando lista e resettando periodo partenza + /// + /// Nome della VC + /// Reimposta e resetta array VC + /// + public double getVal_TSVC(string VCName, bool doReset) + { + double answ = 0; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) + { + // !!!FARE!!! vero calcolo... x ora FIX a MAX... + foreach (var item in TSVC_Data[VCName].dataArray) + { + answ = item > answ ? item : answ; + } + // ora resetto... SE richiesto... + if (doReset) + { + TSVC_Data[VCName].dataArray = new List(); + TSVC_Data[VCName].DTStart = DateTime.Now; + } + } + return answ; + } + + /// + /// Recupera la VC x TS, svuotando lista e resettando periodo partenza + /// + /// Nome della VC + /// Reimposta e resetta array VC + /// + public int getVal_TSVC_int(string VCName, bool doReset) + { + int answ = 0; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) + { + // !!!FARE!!! vero calcolo... x ora FIX a MAX... + foreach (var item in TSVC_Data[VCName].dataArray) + { + answ = (int)item > answ ? (int)item : answ; + } + // ora resetto... SE richiesto.. + if (doReset) + { + TSVC_Data[VCName].dataArray = new List(); + TSVC_Data[VCName].DTStart = DateTime.Now; + } + } + return answ; + } + /// /// Restituisce un payload in formato json della lista di valori ricevuta /// @@ -3298,6 +3443,87 @@ namespace IOB_WIN return answ; } + /// + /// Effettua un trim della stringa al numero max di linee da mostrare a video + /// + /// + /// + public string limitLine2show(string newString) + { + // se num righe superiore a limite trimmo... + if (newString.Split('\n').Length > parentForm.nLine2show) + { + //int idx = newString.LastIndexOf('\r'); + int idx = newString.LastIndexOf(Environment.NewLine); + newString = newString.Substring(0, idx); + } + return newString; + } + + /// + /// riporta il log di tutti i dati di results temporali registrati + /// + public void logTimeResults() + { + if (TimingData.results.Count > 0) + { + lgInfo("{0}--------------- START TIMING DATA ---------------", Environment.NewLine); + int globNumCall = 0; + TimeSpan globAvgMsec = new TimeSpan(0); + foreach (TimeRec item in TimingData.results) + { + // loggo SOLO se del mio IOB corrente... + if (item.classCall == cIobConf.codIOB) + { + lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + globNumCall += item.numCall; + globAvgMsec += item.totMsec; + } + } + // riporto conteggio medio al secondo... + lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine); + // mostro in form statistiche globali! + parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds)); + } + } + + /// + /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza) + /// + /// + /// + /// + public bool monItem2Send(string newVal, DynDataItem item) + { + bool answ = false; + if (item != null) + { + // controllo in base al tipo di function... + switch (item.func) + { + case "SAMPLE": + // controllo se scaduto sample period... + if (item.DTScad < DateTime.Now) + { + answ = true; + } + break; + + case "CHANGE": + default: + // controllo se scaduto o se variato... + if (newVal != item.actVal || item.DTScad < DateTime.Now) + { + // aggiorno scadenza e che vada inviato + answ = true; + } + break; + } + } + return answ; + } + /// /// Verifica e processing x gestione ODL automatica /// @@ -3622,6 +3848,55 @@ namespace IOB_WIN } } + /// + /// Effettua ciclo controllo richieste server + /// + public void processServerRequests() + { + Dictionary task2exe = new Dictionary(); + Dictionary taskDone = new Dictionary(); + // recupero elenco delle cose da fare + string resp = getTask2exe(); + if (!string.IsNullOrEmpty(resp)) + { + try + { + task2exe = JsonConvert.DeserializeObject>(resp); + // se ho da fare chiamo esecuzione.. + if (task2exe.Count > 0) + { + taskDone = processTask(task2exe); + } + } + catch (Exception exc) + { + lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); + } + } + } + + public Dictionary processTask(Dictionary task2exe) + { + Dictionary taskDone = new Dictionary(); + if (task2exe != null) + { + lgInfo($"Task2Exe: trovati {task2exe.Count} task da eseguire, procedo"); + // chiamo procedura esecutiva (diversa x ogni IOB) + taskDone = executeTasks(task2exe); + // loggo tutti i task done... + foreach (var item in taskDone) + { + sendToTaskWatch(item.Key, item.Value); + } + // ora chiamo la cancellazione dei task eseguiti... + foreach (var item in taskDone) + { + remTask2exe(item.Key, item.Value); + } + } + return taskDone; + } + /// /// Classe fittizia in caso di processing task in VHF /// @@ -3636,6 +3911,118 @@ namespace IOB_WIN { } + /// + /// Effettua rilettura del contapezzi dal server MP/IO + /// + /// Forza rilettura da DB tempi ciclo rilevati + public void pzCntReload(bool forceCountRec) + { + // legge da IO server ULTIMO valore CONTPEZZI al riavvio... + string currServerCount = ""; + string lastIdxODL = ""; + if (checkServerAlive) + { + // leggo PRIMA ODL .... + lastIdxODL = utils.callUrl(urlGetCurrODL); + lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL); + // se ho valori in coda da trasmettere uso dati REDIS + if (forceCountRec) + { + // uso dati da TCiclo registrati... + currServerCount = utils.callUrl(urlGetPzCountRec); + lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount); + } + else + { + // uso il contapezzi dichiarato dall'IOB stesso + currServerCount = utils.callUrl(urlGetPzCount); + lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount); + } + // controllo: SE NON HO ODL... + if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0") + { + // NON AGGIORNO + contapezziIOB = contapezziPLC; + lgInfo($"Errore lettura ODL (vuoto) resta tutto invariato contapezzi: {contapezziIOB} | contapezziPLC {contapezziPLC}"); + } + else + { + if (!string.IsNullOrEmpty(currServerCount)) + { + // se "-1" resto a ultimo... + if (currServerCount != "-1") + { + int newVal = -1; + Int32.TryParse(currServerCount, out newVal); + contapezziIOB = newVal > -1 ? newVal : contapezziIOB; + lgInfo("Ricevuta conferma da server di {0} pezzi registrati per ODL", currServerCount); + } + else + { + // NON AGGIORNO + contapezziIOB = contapezziPLC; + lgInfo($"Errore lettura contapezzi (-1) - uso contapezziPLC --> {contapezziPLC}"); + } + } + else + { + // registro che ho UN NUOVO ODL + lgInfo($"Lettura ODL in pzCntReload, currIdxODL {currIdxODL} --> lastIdxODL {lastIdxODL}"); + // se ODL differente e NUOVO è zero --> resetto! + if (currIdxODL.ToString() != lastIdxODL && lastIdxODL == "0") + { + // cambiato ODL quindi reset... + contapezziIOB = 0; + lgInfo("Nuovo ODL==0, RESET contapezzi (-->ZERO)"); + } + // provo a salvare nuovo ODL + int.TryParse(lastIdxODL, out currIdxODL); + } + } + } + else + { + // se server NON pronto... + contapezziIOB = contapezziPLC; + lgError("Errore server NON pronto in pzCntReload"); + } + } + + /// + /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont + /// Flusso dati + /// Valore da salvare + /// + public string qEncodeFLog(string flusso, string valore) + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", DateTime.Now, flusso, valore, counterFLog); + } + catch + { } + return answ; + } + + /// + /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont + /// DataOra evento registrato + /// Flusso dati + /// Valore da salvare + /// + public string qEncodeFLog(DateTime eventDT, string flusso, string valore) + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", eventDT, flusso, valore, counterFLog); + } + catch + { } + return answ; + } + /// /// Effettua lettura dati /// Parametri da aggiornare x display in form @@ -3683,6 +4070,45 @@ namespace IOB_WIN lastReadPLC = DateTime.Now; } + /// + /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati + /// + public virtual void reportRawInput(ref newDisplayData currDispData) + { + // processo eventualmente aggiungendo ad elementi esistenti... + if (currDispData == null) + { + currDispData = new newDisplayData(); + } + try + { + StringBuilder sb = new StringBuilder(); + sb.Append($"B_input --> {(short)B_input}{Environment.NewLine}"); + sb.Append($"{baseUtils.binaryForm(B_input)}{Environment.NewLine}"); + sb.Append($"{Environment.NewLine}"); + sb.Append($"----------- RAW Data Memory -----------{Environment.NewLine}"); + int i = 0; + foreach (var item in RawInput) + { + sb.Append($"B{i:00} --> {baseUtils.binaryForm(item)} = {(short)item}{Environment.NewLine}"); + i++; + } + sb.Append("-------------------------------"); + currDispData.currBitmap = sb.ToString(); + } + catch + { } + } + + /// + /// Metodo generico di reset contapezzi... + /// + /// + public virtual bool resetcontapezziPLC() + { + return false; + } + /// /// metodo dummy x salvataggio aree memoria conf x CN /// @@ -3692,528 +4118,23 @@ namespace IOB_WIN } /// - /// Processo la coda SignalIN... + /// Salva valori indicati in prod data /// - public void svuotaCodaSignIN() + /// Item KVP di cui salvare i dati in currProdData come chiave/valore + /// + public void saveProdData(KeyValuePair item) { - // verifico SE la coda abbia dei valori... - if (QueueIN.Count > 0) + // imposto i valori... + if (currProdData.ContainsKey(item.Key)) { - // invio pacchetto di dati (max da conf) - for (int i = 0; i < nMaxSend; i++) - { - if (QueueIN.Count > 0) - { - string currVal = ""; - // se online provo - if (MPOnline) - { - if (IobOnline) - { - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueIN.Count > 1) - { - List listaValori = new List(); - // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueIN.Count > maxJsonDataEv) - { - // prendoi primi maxJsonDataValori - for (int j = 0; j < maxJsonDataEv; j++) - { - QueueIN.TryDequeue(out currVal); - listaValori.Add(currVal); - } - sendDataBlock(urlType.SignIN, listaValori); - } - else - { - // invio in blocco - listaValori = QueueIN.ToList(); - // invio - sendDataBlock(urlType.SignIN, listaValori); - // svuoto! - QueueIN = new ConcurrentQueue(); - } - } - else - { - // INVIO SINGOLO...!!! - QueueIN.TryDequeue(out currVal); - sendToMoonPro(urlType.SignIN, currVal); - } - // salvo come last signal in il currVal ultimo... SE !="" - if (!string.IsNullOrEmpty(currVal)) - { - lastSignInVal = currVal; - } - } - else - { - break; - } - } - else - { - break; - } - } - else - { - break; - } - } - } - } - - /// - /// Effettua verifica se abilitato invio pezzi in blocco e nel caso - /// - invio in blocco pezzi - /// - aggiornamento del contapezzi (passato come ref) x nuovo valore post invio - /// - public void trySendPzCountBlock() - { - // in primis HA SENSO procedere SOLO SE server MP è Online... - if (MPOnline) - { - // SOLO SE online la macchina... - if (IobOnline) - { - int numIncr = 0; - int qtyAdded = 0; - // verifico se la funzione SIA abilitata - if (enableSendPzCountBlock) - { - int delta = contapezziPLC - contapezziIOB; - // se è abilitata verifico differenza: se ho DELTA > minSendPzCountBlock --> invio un blocco <= maxSendPzCountBlock - if (delta > minSendPzCountBlock) - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // resta indietro di ALMENO minSendPzCountBlock pezzi x recuperare 1:1... - numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock; - // invio il num max di pezzi ammesso in blocco! - lastUrl = $"{urlAddPzCount}{numIncr}"; - string resp = utils.callUrlNow(lastUrl); - if (!string.IsNullOrEmpty(resp)) - { - // dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti) - int.TryParse(resp, out qtyAdded); - if (qtyAdded > 0) - { - // aggiorno IL MIO contapezzi... - contapezziIOB += qtyAdded; - lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}"); - // invio conferma contapezzi.. - string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); - // verifica se tutto OK - if (retVal != contapezziIOB.ToString()) - { - // errore salvataggio contapezzi - lgInfo($"trySendPzCountBlock: errore salvataggio contapezzi: contapezziIOB {contapezziIOB} | risposta: {retVal}"); - } - } - else - { - lgError($"Richiesto incremento {numIncr} ma NON registrato su server MP-IO"); - } - } - currDispData.newUrlCallData = lastUrl; - currDispData.counter = contapezziIOB; - currDispData.semOut = Semaforo.SV; - raiseRefresh(currDispData); - } - } - } - else - { - lgInfo("Impossibile trySendPzCountBlock: IobOnline è false"); - } + currProdData[item.Key] = item.Value; } else { - lgInfo("Impossibile trySendPzCountBlock: MPOnline è false"); + currProdData.Add(item.Key, item.Value); } } - /// - /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi - /// - /// - /// - public string urlDataBlock(urlType tipoUrl) - { - // verifico la parte di link "tipoComando" - string tipoComando = tipoUrl == urlType.FLog ? cIobConf.serverData.CMDFLOG_JSON : cIobConf.serverData.CMDBASE_JSON; - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, tipoComando, cIobConf.codIOB); - return answ; - } - - /// - /// Fornisce URL di tipo FluxLog - /// - /// valore salvato in coda nel formato dtEve#flux#valore#counter - /// - public string urlFLog(string queueVal) - { - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDFLOG); - // decodifica valore! - string[] valori = qDecodeIN(queueVal); - // aggiungo macchina e valore... - answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]); - // aggiondo dataOra evento e corrente + contatore... - answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]); - return answ; - } - - /// - /// Fornisce URL INPUT per i parametri richiesti - /// - /// valore salvato in coda formato dtEve#valore#counter - /// - public string urlInput(string queueVal) - { - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDBASE); - // decodifica valore! - string[] valori = qDecodeIN(queueVal); - // aggiungo macchina e valore... - answ += string.Format(@"{0}?valore={1}", cIobConf.codIOB, valori[1]); - // aggiondo dataOra evento e corrente + contatore... - answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[2]); - return answ; - } - - /// - /// Esegue filtraggio dati x bit blinking!!! - /// - private void filterData() - { - // effettuo filtraggio dei valori letti... inizializzo OUT! - B_output = 0; - // in primis verifico SE ci siano bit blinkng... se non ci sono OUT=IN... - if (cIobConf.BLINK_FILT == 0) - { - B_output = B_input; - } - else - { - // incomincio con i valori NON blinking: questi "passano invariati", inizio a sommare nel valore OUT... - B_output = B_input & ~cIobConf.BLINK_FILT; - // calcolo il valore dei BIT che "passano la maschera" - int iBlink = B_input & cIobConf.BLINK_FILT; - // ...aggiungo i "bit che passano" - B_output += iBlink; - - // calcolo QUALI valori (tra quelli blink) siano PASSATI da 0 a 1 --> init counters... - BitArray bBlinkStart = new BitArray(new byte[] { Convert.ToByte(iBlink) }); - int[] bitsUp = bBlinkStart.Cast().Select(bit => bit ? 1 : 0).ToArray(); - for (int i = 0; i < bitsUp.Length; i++) - { - // SE 1... impostiamo contatori al MAX - if (bitsUp[i] == 1) - { - // se era zero indico START blink... - if (i_counters[i] == 0) - { - lgInfo("START BLINK: B{0}", i); - } - // imposto comunque contatore al cambio fronte... - i_counters[i] = cIobConf.MAX_COUNTER_BLINK; - } - } - - // quelli che sono zero... LI RECUPERO E LI PROCESSO... - int iZero = ~B_input & cIobConf.BLINK_FILT; - BitArray bBlinkEnd = new BitArray(new byte[] { Convert.ToByte(iZero) }); - int[] bitsDown = bBlinkEnd.Cast().Select(bit => bit ? 1 : 0).ToArray(); - for (int i = 0; i < bitsDown.Length; i++) - { - // se era a zero (invertito...) - if (bitsDown[i] == 1) - { - // SE è in corso il conteggio... - if (i_counters[i] > 0) - { - // decremento! - i_counters[i] -= 1; - // se è zero NON faccio nulla, altrimenti SOMMO... - if (i_counters[i] > 0) - { - B_output += 1 << i; - } - else - { - lgInfo("END BLINK: B{0}", i); - } - } - } - } - } - } - - /// - /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... - /// - private void processAllMemory() - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // in primis SALVO valori previous/precedenti - B_previous = B_output; - // poi faccio lettura NUOVI valori - readAllData(ref currDispData); - // eseguo il filtering dei valori (per i bit "blinking") - filterData(); - // effettuo confronto valori vecchi/nuovi... SE trovo variazione OPPURE se è passato + di un timeout di controllo... - if (B_output != B_previous) - { - accodaSigIN(ref currDispData); - } - raiseRefresh(currDispData); - } - - /// - /// Effettua gestioen programma: legge e mostra su display... - /// - private void processProgram() - { - string currPrgName = ""; - // se abilitata lettura prgName - if (enablePrgName) - { - if (connectionOk) - { - currPrgName = getPrgName(); - } - else - { - lgError("Errore connessione mancante x getPrgName"); - } - } - else - { - currPrgName = lastPrgName; - } - // verifico SE sia cambiato il programma... - if (lastPrgName != currPrgName) - { - // salvo! - lastPrgName = currPrgName; - string sVal = string.Format("[PROG]{0}", currPrgName); - - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog("PROG", currPrgName)); - } - } - - /// - /// Processo lettura dati sysinfo - /// - private void processSysInfo() - { - if (utils.CRB("enableSysInfo")) - { - Dictionary currSysInfo = new Dictionary(); - - if (connectionOk) - { - currSysInfo = getSysInfo(); - } - else - { - lgError("Errore connessione mancante x getSysInfo"); - } - // verifico SE sia cambiato il programma... - if (lastSysInfo != currSysInfo["SYSINFO"]) - { - // salvo! - lastSysInfo = currSysInfo["SYSINFO"]; - // per ogni valore del dizionario mostro ed accodo! - string sVal = ""; - foreach (var item in currSysInfo) - { - sVal = string.Format("[SYSINFO]{0}|{1}", item.Key, item.Value); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); - } - } - } - } - - /// - /// Processo la coda FLog... - /// - private void svuotaCodaFLog() - { - //controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! - if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) - { - string wdStatus = "elapsed"; - string sVal = string.Format("[WDST]{0}", wdStatus); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); - lastWatchDog = DateTime.Now; - } - // verifico SE la coda abbia dei valori... - if (QueueFLog.Count > 0) - { - // invio pacchetto di dati (max da conf) - for (int i = 0; i < nMaxSend; i++) - { - // SE ho qualcosa in coda... - if (QueueFLog.Count > 0) - { - string currVal = ""; - if (MPOnline) - { - if (IobOnline) - { - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > 1) - { - List listaValori = new List(); - // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) - { - // prendoi primi maxJsonDataValori - for (int j = 0; j < maxJsonData; j++) - { - QueueFLog.TryDequeue(out currVal); - listaValori.Add(currVal); - } - sendDataBlock(urlType.FLog, listaValori); - } - else - { - // invio in blocco - listaValori = QueueFLog.ToList(); - // invio - sendDataBlock(urlType.FLog, listaValori); - // svuoto! - QueueFLog = new ConcurrentQueue(); - } - } - else - { - // INVIO SINGOLO...!!! - QueueFLog.TryDequeue(out currVal); - sendToMoonPro(urlType.FLog, currVal); - } - } - else - { - break; - } - } - else - { - break; - } - } - else - { - break; - } - } - } - } - - #endregion IOB METHODS - - #region gestione VC e postprocessing - - /// - /// Recupera la VC x TS, svuotando lista e resettando periodo partenza - /// - /// Nome della VC - /// Reimposta e resetta array VC - /// - public double getVal_TSVC(string VCName, bool doReset) - { - double answ = 0; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - // !!!FARE!!! vero calcolo... x ora FIX a MAX... - foreach (var item in TSVC_Data[VCName].dataArray) - { - answ = item > answ ? item : answ; - } - // ora resetto... SE richiesto... - if (doReset) - { - TSVC_Data[VCName].dataArray = new List(); - TSVC_Data[VCName].DTStart = DateTime.Now; - } - } - return answ; - } - - /// - /// Recupera la VC x TS, svuotando lista e resettando periodo partenza - /// - /// Nome della VC - /// Reimposta e resetta array VC - /// - public int getVal_TSVC_int(string VCName, bool doReset) - { - int answ = 0; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - // !!!FARE!!! vero calcolo... x ora FIX a MAX... - foreach (var item in TSVC_Data[VCName].dataArray) - { - answ = (int)item > answ ? (int)item : answ; - } - // ora resetto... SE richiesto.. - if (doReset) - { - TSVC_Data[VCName].dataArray = new List(); - TSVC_Data[VCName].DTStart = DateTime.Now; - } - } - return answ; - } - - /// - /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza) - /// - /// - /// - /// - public bool monItem2Send(string newVal, DynDataItem item) - { - bool answ = false; - if (item != null) - { - // controllo in base al tipo di function... - switch (item.func) - { - case "SAMPLE": - // controllo se scaduto sample period... - if (item.DTScad < DateTime.Now) - { - answ = true; - } - break; - - case "CHANGE": - default: - // controllo se scaduto o se variato... - if (newVal != item.actVal || item.DTScad < DateTime.Now) - { - // aggiorno scadenza e che vada inviato - answ = true; - } - break; - } - } - return answ; - } - /// /// /// @@ -4238,168 +4159,6 @@ namespace IOB_WIN LastTSVC[chiave] = valore; } - /// - /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e restituisce bool val se SCADUTO periodo controllo - /// - /// Nome della VC - /// Valore (nuovo) delal VC - /// - public bool stackVal_TSVC(string VCName, double VCVal) - { - bool answ = false; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - TSVC_Data[VCName].dataArray.Add(VCVal); - // ora verifico scadenza... - if (TSVC_Data[VCName].DTStart.AddSeconds(TSVC_Data[VCName].Period) < DateTime.Now) - { - answ = true; - } - } - return answ; - } - - #endregion gestione VC e postprocessing - - #region gestione code (accumulo, invio) - - /// - /// Fornisce il valore letto da BITMAP in formato valido x messa in coda nel formato dtEve#value#cont - /// - protected string qEncodeIN - { - get - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN); - } - catch - { } - return answ; - } - } - - /// - /// Accumula in coda i valori ALARM e logga... - /// - /// VALORE RAW (x display) - /// VALORE già processato con qEncodeFLog(...) - public void accodaAlarmLog(string val, string encodedVal) - { - // mostro dati variati letti... - displayOtherData(val); - // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); - // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati... - - // loggo! - lgInfo(string.Format("[QUEUE-ALARM-LOG] {0}", encodedVal)); - counterFLog++; - if (counterFLog > 9999) - { - counterFLog = 0; - } - } - - /// - /// Accumula in coda i valori Flux Log e logga... - /// - /// VALORE RAW (x display) - /// VALORE già processato con qEncodeFLog(...) - public void accodaFLog(string val, string encodedVal) - { - // mostro dati variati letti... - displayOtherData(val); - // --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); - // se abilitato controllo coda FLog (superiore a 0...) - if (maxQueueFLog > 0) - { - // se ho una coda superiore a max ammesso - if (QueueFLog.Count > maxQueueFLog) - { - // elimino valori iniziali fino a tornare al max ammesso... - while (QueueFLog.Count > maxQueueFLog) - { - string currVal = ""; - QueueFLog.TryDequeue(out currVal); - lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}"); - } - } - } - // loggo! - lgInfo(string.Format("[QUEUE-FLOG] {0}", encodedVal)); - counterFLog++; - if (counterFLog > 9999) - { - counterFLog = 0; - } - } - - /// - /// Accumula in coda i valori Signal IN e logga... - /// Parametri da aggiornare x display in form - /// - public void accodaSigIN(ref newDisplayData currDispData) - { - // mostro dati variati letti... - displayInData(ref currDispData); - // --> accodo (valore già formattato)! - QueueIN.Enqueue(qEncodeIN); - // loggo! - lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN)); - // aggiorno counters ed eventuale reset - nReadFilt++; - if (nReadFilt > int.MaxValue - 1) - { - nReadFilt = 0; // per evitare buffer overflow... - } - - counterSigIN++; - if (counterSigIN > 9999) - { - counterSigIN = 0; - } - } - - /// - /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont - /// Flusso dati - /// Valore da salvare - /// - public string qEncodeFLog(string flusso, string valore) - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", DateTime.Now, flusso, valore, counterFLog); - } - catch - { } - return answ; - } - - /// - /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont - /// DataOra evento registrato - /// Flusso dati - /// Valore da salvare - /// - public string qEncodeFLog(DateTime eventDT, string flusso, string valore) - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", eventDT, flusso, valore, counterFLog); - } - catch - { } - return answ; - } - /// /// Invia una LISTA di valori /// @@ -4560,101 +4319,345 @@ namespace IOB_WIN } /// - /// Decodifica valore della coda IN nel formato - /// answ[0]=dtEve - /// answ[1]=valore - /// answ[2]=counter + /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi... /// - /// dtEve + '#' + value + '#' + cont + /// Pezzi richiesti /// - protected static string[] qDecodeIN(string queueVal) + public virtual bool setcontapezziPLC(int newPzCount) { - string[] answ = null; - if (!string.IsNullOrEmpty(queueVal)) + return false; + } + + /// + /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e restituisce bool val se SCADUTO periodo controllo + /// + /// Nome della VC + /// Valore (nuovo) delal VC + /// + public bool stackVal_TSVC(string VCName, double VCVal) + { + bool answ = false; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) { - try + TSVC_Data[VCName].dataArray.Add(VCVal); + // ora verifico scadenza... + if (TSVC_Data[VCName].DTStart.AddSeconds(TSVC_Data[VCName].Period) < DateTime.Now) { - answ = queueVal.Split('#'); + answ = true; } - catch - { } } return answ; } - #endregion gestione code (accumulo, invio) - - #region gestione dataMonitor (update visualizzazione valori) - /// - /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA + /// Avvia l'adapter sulla porta richiesta /// - /// - public void accodaOtherData(string newLine) + /// indica se sia richeisto di SVUOTARE le code delel info + public virtual void startAdapter(bool resetQueue) { - // inserisco in cima allo stack, trimmo e aggiorno display - string strOtherData = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3)); - //parentForm.dataMonitor_3 = strOtherData; - parentForm.WriteTextSafe(strOtherData); + lgInfo("Starting adapter..."); + maxJsonData = utils.CRI("maxJsonData"); + maxJsonDataEv = utils.CRI("maxJsonDataEv"); + parentForm.commPlcActive = false; + adpRunning = true; + dtAvvioAdp = DateTime.Now; + lastWatchDog = dtAvvioAdp; + lastPING = dtAvvioAdp; + lastReadPLC = dtAvvioAdp.AddMinutes(-1); + lastDisconnCheck = dtAvvioAdp; + TimingData.resetData(); + // aggiungo altri defaults + setDefaults(resetQueue); + adpTryRestart = true; + parentForm.displayTaskAndLog("Adapter Started!"); } /// - /// Update visualizzaizone BIT in ingresso - /// Parametri da aggiornare x display in form + /// ferma l'adapter... /// - public void displayInData(ref newDisplayData currDispData) + /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato in automatico) + /// indica se sia richeisto di SVUOTARE le code delel info + public virtual void stopAdapter(bool tryRestart, bool forceDequeue) { - if (currDispData != null) + if (forceDequeue) { - // mostro update... - string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr(B_output, 8)); - currDispData.newSignalData = newString; + // svuoto le code dei valori letti e non ancora trasmessi... + parentForm.displayTaskAndLog("Svuotamento FORZATO coda segnali..."); + while (QueueIN.Count > 0) + { + // INVIO COMUNQUE...!!! + string valore = ""; + QueueIN.TryDequeue(out valore); + sendToMoonPro(urlType.SignIN, valore); + } + parentForm.displayTaskAndLog("Svuotamento FORZATO coda FluxLOG..."); + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueFLog.Count > minJsonData) + { + while (QueueFLog.Count > 0) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueFLog.Count > maxJsonData) + { + string currVal = ""; + // prendoi primi maxJsonDataValori + for (int i = 0; i < maxJsonData; i++) + { + QueueFLog.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.FLog, listaValori); + } + else + { + // invio in blocco + listaValori = QueueFLog.ToList(); + // invio + sendDataBlock(urlType.FLog, listaValori); + // svuoto! + QueueFLog = new ConcurrentQueue(); + } + } + // HO FINITO invio di FLog... + } + else + { + string currVal = ""; + while (QueueFLog.Count > 0) + { + // INVIO COMUNQUE...!!! + QueueFLog.TryDequeue(out currVal); + sendToMoonPro(urlType.FLog, currVal); + } + } + } + parentForm.displayTaskAndLog("Stopping adapter..."); + adpTryRestart = false; + + parentForm.displayTaskAndLog("Stopping adapter - last periodic data read..."); + + // chiudo la connessione all'adapter... + tryDisconnect(); + dtStopAdp = DateTime.Now; + adpTryRestart = tryRestart; + adpRunning = false; + // chiudo! + parentForm.displayTaskAndLog("Adapter Stopped."); + parentForm.commPlcActive = false; + } + + /// + /// Processo la coda SignalIN... + /// + public void svuotaCodaSignIN() + { + // verifico SE la coda abbia dei valori... + if (QueueIN.Count > 0) + { + // invio pacchetto di dati (max da conf) + for (int i = 0; i < nMaxSend; i++) + { + if (QueueIN.Count > 0) + { + string currVal = ""; + // se online provo + if (MPOnline) + { + if (IobOnline) + { + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueIN.Count > 1) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueIN.Count > maxJsonDataEv) + { + // prendoi primi maxJsonDataValori + for (int j = 0; j < maxJsonDataEv; j++) + { + QueueIN.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.SignIN, listaValori); + } + else + { + // invio in blocco + listaValori = QueueIN.ToList(); + // invio + sendDataBlock(urlType.SignIN, listaValori); + // svuoto! + QueueIN = new ConcurrentQueue(); + } + } + else + { + // INVIO SINGOLO...!!! + QueueIN.TryDequeue(out currVal); + sendToMoonPro(urlType.SignIN, currVal); + } + // salvo come last signal in il currVal ultimo... SE !="" + if (!string.IsNullOrEmpty(currVal)) + { + lastSignInVal = currVal; + } + } + else + { + break; + } + } + else + { + break; + } + } + else + { + break; + } + } } } /// - /// Mostra cosa ha/avrebbe inviato + /// Metodo base connessione... /// - /// - public void displayOtherData(string newData) + public virtual void tryConnect() { - // mostro update... - accodaOtherData(newData); + dtAvvioAdp = DateTime.Now; } /// - /// Effettua un trim della stringa al numero max di linee da mostrare a video + /// Metodo base disconnessione... /// - /// + public virtual void tryDisconnect() + { + } + + /// + /// Effettua verifica se abilitato invio pezzi in blocco e nel caso + /// - invio in blocco pezzi + /// - aggiornamento del contapezzi (passato come ref) x nuovo valore post invio + /// + public void trySendPzCountBlock() + { + // in primis HA SENSO procedere SOLO SE server MP è Online... + if (MPOnline) + { + // SOLO SE online la macchina... + if (IobOnline) + { + int numIncr = 0; + int qtyAdded = 0; + // verifico se la funzione SIA abilitata + if (enableSendPzCountBlock) + { + int delta = contapezziPLC - contapezziIOB; + // se è abilitata verifico differenza: se ho DELTA > minSendPzCountBlock --> invio un blocco <= maxSendPzCountBlock + if (delta > minSendPzCountBlock) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // resta indietro di ALMENO minSendPzCountBlock pezzi x recuperare 1:1... + numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock; + // invio il num max di pezzi ammesso in blocco! + lastUrl = $"{urlAddPzCount}{numIncr}"; + string resp = utils.callUrlNow(lastUrl); + if (!string.IsNullOrEmpty(resp)) + { + // dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti) + int.TryParse(resp, out qtyAdded); + if (qtyAdded > 0) + { + // aggiorno IL MIO contapezzi... + contapezziIOB += qtyAdded; + lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}"); + // invio conferma contapezzi.. + string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); + // verifica se tutto OK + if (retVal != contapezziIOB.ToString()) + { + // errore salvataggio contapezzi + lgInfo($"trySendPzCountBlock: errore salvataggio contapezzi: contapezziIOB {contapezziIOB} | risposta: {retVal}"); + } + } + else + { + lgError($"Richiesto incremento {numIncr} ma NON registrato su server MP-IO"); + } + } + currDispData.newUrlCallData = lastUrl; + currDispData.counter = contapezziIOB; + currDispData.semOut = Semaforo.SV; + raiseRefresh(currDispData); + } + } + } + else + { + lgInfo("Impossibile trySendPzCountBlock: IobOnline è false"); + } + } + else + { + lgInfo("Impossibile trySendPzCountBlock: MPOnline è false"); + } + } + + /// + /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi + /// + /// /// - public string limitLine2show(string newString) + public string urlDataBlock(urlType tipoUrl) { - // se num righe superiore a limite trimmo... - if (newString.Split('\n').Length > parentForm.nLine2show) - { - //int idx = newString.LastIndexOf('\r'); - int idx = newString.LastIndexOf(Environment.NewLine); - newString = newString.Substring(0, idx); - } - return newString; + // verifico la parte di link "tipoComando" + string tipoComando = tipoUrl == urlType.FLog ? cIobConf.serverData.CMDFLOG_JSON : cIobConf.serverData.CMDBASE_JSON; + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, tipoComando, cIobConf.codIOB); + return answ; } /// - /// Mostra i dati grezzi letti in esadecimale - /// Parametri da aggiornare x display in form + /// Fornisce URL di tipo FluxLog /// - private void displayRawData(ref newDisplayData currDispData) + /// valore salvato in coda nel formato dtEve#flux#valore#counter + /// + public string urlFLog(string queueVal) { - // mostro update... - string newString = string.Format("{0:X}", B_input); - currDispData.newInData = newString; -#if false - // salvo coda debug... - QueueDebug.Enqueue(B_input); -#endif + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDFLOG); + // decodifica valore! + string[] valori = qDecodeIN(queueVal); + // aggiungo macchina e valore... + answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]); + // aggiondo dataOra evento e corrente + contatore... + answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]); + return answ; } - #endregion gestione dataMonitor (update visualizzazione valori) + /// + /// Fornisce URL INPUT per i parametri richiesti + /// + /// valore salvato in coda formato dtEve#valore#counter + /// + public string urlInput(string queueVal) + { + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDBASE); + // decodifica valore! + string[] valori = qDecodeIN(queueVal); + // aggiungo macchina e valore... + answ += string.Format(@"{0}?valore={1}", cIobConf.codIOB, valori[1]); + // aggiondo dataOra evento e corrente + contatore... + answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[2]); + return answ; + } + + #endregion Public Methods } /// diff --git a/Jenkinsfile b/Jenkinsfile index b519a38b..2254ae71 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { steps { /* calcolo numero versione... diverso x branch MASTER/DEVELOP */ script { - withEnv(['NEXT_BUILD_NUMBER=723']) { + withEnv(['NEXT_BUILD_NUMBER=724']) { // env.versionNumber = VersionNumber(versionNumberString : '3.3.${BUILD_DATE_FORMATTED, "yyMM"}.${BUILDS_ALL_TIME}', projectStartDate : '2006-01-01', skipFailedBuilds: true) env.versionNumber = VersionNumber(versionNumberString : '3.3.${BUILD_DATE_FORMATTED, "yyMM"}.${BUILDS_ALL_TIME}', projectStartDate : '2006-01-01', skipFailedBuilds: true, overrideBuildsAllTime: '${NEXT_BUILD_NUMBER}') env.APP_NAME = 'MAPO-IOB-WIN' diff --git a/gitSpread.bat b/gitSpread.bat index 15fb39a3..11d43636 100644 --- a/gitSpread.bat +++ b/gitSpread.bat @@ -23,7 +23,7 @@ if %pushRemote% GTR 1 ( git push gitlab.steamware %baseBranch%:develop ) if %pushRemote% GTR 2 ( git push gitlab.steamware %baseBranch%:IOB-NET ) if %pushRemote% GTR 2 ( git push gitlab.steamware %baseBranch%:IOB/Euromap63 ) if %pushRemote% GTR 2 ( git push gitlab.steamware %baseBranch%:IOB/MTC ) -if %pushRemote% GTR 2 ( git push gitlab.steamware %baseBranch%:IobMan ) +if %pushRemote% GTR 1 ( git push gitlab.steamware %baseBranch%:IobMan ) if %pushRemote% GTR 1 ( git push gitlab.steamware %baseBranch%:master ) ECHO on \ No newline at end of file