using IOB_UT; using NLog; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace IOB_WIN { public class IobGeneric { #region variabili ed oggetti base /// /// Evento Iob ha subito un refresh /// public event EventHandler eh_refreshed; /// /// Determina se sia encessario convertire valori little/big endian (SIEMENS=true, OSAI=FALSE) /// public bool hasBigEndian = false; /// /// alias booleano false = R /// public bool R = false; /// /// alias booleano true = W /// public bool W = true; /// /// wrapper di log /// public static Logger lg; /// /// dataOra ultimo log periodico... /// public DateTime lastPeriodicLog; /// /// Oggetto della coda degli elementi letti (e non ancora trasmessi) /// public Queue QueueIN = new Queue(); /// /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) /// public Queue QueueFLog = new Queue(); /// /// Coda valori simulazione deterministica... /// public Queue QueueSIM = new Queue(); /// /// Coda valori letti x DEBUG... /// public Queue QueueDebug = new Queue(); /// /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC /// protected bool adpCommAct; /// /// valore booleano di check se sia stato AVVIATO l'adapter (Running) /// public bool adpRunning = false; /// /// valore booleano di check se l'adapter STIA SALVANDO /// public bool adpSaving = false; /// /// valore booleano (richiesta di riavvio automatico) /// public bool adpTryRestart; /// /// Ultimo valore watchdog rilevato /// public bool lastWatchDog = false; /// /// Verifica se sia in modalità DEMO x dati INPUT /// public bool DemoIn { get { return IOB_UT.baseUtils.CRB("DemoIn"); } } /// /// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...) /// public bool DemoInSample { get { return IOB_UT.baseUtils.CRB("DemoInSample"); } } /// /// Numero simulazioni ammesse... /// protected int numSim { get; set; } /// /// Numero letture IN da avvio /// protected int nReadIN { get; set; } /// /// Numero letture IN da avvio /// protected int nReadFilt { get; set; } /// /// Numero invii OUT (svuotamento coda) /// protected int nSendOut { get; set; } /// /// Verifica se sia in modalità DEMO x dati OUTPUT /// public bool DemoOut { get { return utils.CRB("DemoOut"); } } /// /// Contatore x invio dati SignalIN /// public int counterSigIN { get; set; } /// /// Contatore x invio dati FluxLog /// public int counterFLog { get; set; } /// /// Ultimo URL /// public string lastUrl { get; set; } /// /// Ultimo programma letto /// public string lastPrgName { get; set; } /// /// Ultimo sysinfo letto /// public string lastSysInfo { get; set; } /// /// Array dei contatori x segnali blinking /// protected int[] i_counters; /// /// Vettore 16 BIT valori precedenti /// protected int B_previous; /// /// Vettore 16 BIT valori in ingresso al filtro /// protected int B_input; /// /// Vettore 16 BIT valori in uscita dal filtro /// protected int B_output; /// /// 16 byte di strobe (4 word da 32 bit di flags...) /// public byte[] RawInput = new byte[4]; /// /// 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; } } /// /// 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; } } /// /// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing "monoblocco" semplificato"= /// public bool procIotMem = false; /// /// porta x adapter (x restart) /// protected int adpPortNum; /// /// DataOra ultimo avvio adapter x watchdog /// protected DateTime adpStartRun; /// /// Data/ora ultimo avvio adapter /// public DateTime dtAvvioAdp = DateTime.Now; /// /// Data/ora ultimo spegnimento adapter /// public DateTime dtStopAdp = DateTime.Now; /// /// vettore gestione cronometraggi /// public DateTime inizio; /// /// Conteggio ATTUALE ore macchina ON /// public double contOreMaccOn; /// /// Conteggio ATTUALE ore macchina IN LAVORO /// public double contOreMaccLav; /// /// stato Online/Offline della IOB /// public bool IobOnline; /// /// stato Online/Offline della IOB /// public bool MPOnline; #endregion /// /// Form chiamante /// protected MainForm parentForm; /// /// Conf adapter corrente /// public IobConfiguration currIobConf; /// /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto /// /// /// public IobGeneric(MainForm caller, IobConfiguration IOBConf) { lg = LogManager.GetCurrentClassLogger(); lg.Info("Avvio preliminare AdapterGeneric"); // configurazione... currIobConf = IOBConf; // aggiungo altri defaults setDefaults(); // salvo il form chiamante parentForm = caller; // concluso! lg.Info("Istanziata classe preliminare IOBGeneric"); } /// /// Imposto alcuni valori di default /// private void setDefaults() { numSim = utils.CRI("numSim"); lastPrgName = ""; nReadIN = 0; nReadFilt = 0; nSendOut = 0; // svuoto code... SE NON SIM... if (!DemoIn) { QueueIN.Clear(); QueueFLog.Clear(); QueueSIM.Clear(); } // imposto contatori blink a zero... i_counters = new int[32]; lastPeriodicLog = DateTime.Now; } #region metodi adapter /// /// lettura file di persistenza /// public void loadPersData() { } /// /// Avvia l'adapter sulla porta richiesta /// /// public virtual void startAdapter(int port) { lg.Info("Starting adapter..."); parentForm.commPlcActive = false; adpRunning = true; dtAvvioAdp = DateTime.Now; TimingData.resetData(); // aggiungo altri defaults setDefaults(); 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) public void stopAdapter(bool tryRestart) { // svuoto le code dei valori letti e non ancora trasmessi... parentForm.displayTaskAndLog("Svuotamento FORZATO coda segnali..."); while (QueueIN.Count > 0) { // INVIO SE PERMESSO...!!! sendToMoonPro(urlType.SignIN, QueueIN.Dequeue()); } parentForm.displayTaskAndLog("Svuotamento FORZATO coda FluxLOG..."); while (QueueFLog.Count > 0) { // INVIO SE PERMESSO...!!! sendToMoonPro(urlType.FLog, QueueFLog.Dequeue()); } //reportDataProc(); 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.resetProgBar(); parentForm.displayTaskAndLog("Adapter Stopped."); parentForm.commPlcActive = false; } /// /// effettua recupero dati ed invio valori modificati... /// /// public void getAndSend(gatherCycle ciclo) { // 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) { parentForm.displayTaskAndLog(string.Format("Adapter NOT STARTED!!!{0}{1}", Environment.NewLine, exc)); adpCommAct = false; adpStartRun = DateTime.Now; } if (adpCommAct) { // try / catch generale altrimenti segno che è disconnesso... try { bool showDebugData = false; // processing dati memoria (invio, lettura, filtraggio, enqueque) if (ciclo == gatherCycle.VHF) { // gestione queue SignalIN (invio, display) svuotaCodaSignIN(); // gestione queue FluxLog (invio, display) svuotaCodaFLog(); } else if (ciclo == gatherCycle.HF) { processAllMemory(); } else if (ciclo == gatherCycle.MF) { processProgram(); } else if (ciclo == gatherCycle.LF) { } else if (ciclo == gatherCycle.VLF) { // recupero dati SETUP (sysinfo) e li invio/mostro se variati... processSysInfo(); // 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... lg.Error(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc)); parentForm.fermaAdapter(true); } // tolgo flag running adpCommAct = false; } else { if (periodicLog) lg.Info("ADP not running..."); } } else { // log ADP running lg.Error("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 { // log connessione KO lg.Error("getAndSend - Connessione non disponibile, provo a riconnettere"); // provo a riconnettere SE abilitato tryRestart... if (adpTryRestart && !connectionOk) { lg.Info("ConnKO - tryConnect"); tryConnect(); } } if (eh_refreshed != null) { eh_refreshed(this, new EventArgs()); } } private void reportDataProc() { // update valori visualizzazione... parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut); } /// /// riporta il log di tutti i dati di results temporali registrati /// public void logTimeResults() { if (TimingData.results.Count > 0) { lg.Info("{0}--------------- START TIMING DATA ---------------", Environment.NewLine); int globNumCall = 0; TimeSpan globAvgMsec = new TimeSpan(0); foreach (TimeRec item in TimingData.results) { lg.Info("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); globNumCall += item.numCall; globAvgMsec += item.totMsec; } // riporto conteggio medio al secondo... lg.Info("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); lg.Info("{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 dataLayer e se necessario salva/mostra /// public void checkSavePersDataLayer() { } public void resetDebugConsole() { } /// /// Metodo base connessione... /// public virtual void tryConnect() { } /// /// Metodo base disconnessione... /// public virtual void tryDisconnect() { } protected bool _connOk = false; /// /// Salva verifica stato connessione OK /// /// public virtual bool connectionOk { get { return _connOk || DemoIn; } set { _connOk = value; } } #endregion #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) { lg.Error(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc)); } return value; } /// /// 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) { lg.Error(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; } /// /// 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 double /// /// /// private double getStoredValDouble(string keyVal) { double answ = 0; try { answ = Convert.ToDouble(getStoredVal(keyVal)); } catch (Exception exc) { lg.Error(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc)); } answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0; return answ; } /// /// 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; } /// /// 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 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; } #endregion #region area lettura configurazioni /// /// 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 (utils.CRB("verbose")) lg.Info(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile)); } /// /// Decodifica file MAP generico /// /// /// /// /// /// /// protected otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) { 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()); } /// /// 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 otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) { 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()); } #if false /// /// Decodifica file MAP (caso LISTA DI BIT...) /// /// /// /// tipo memoria (R/D/...) /// indirizzo Byte: indirizzo di partenza memoria /// dimensione singolo slot in byte /// indirizzo bit: numero riga x calcolo indice bit /// protected otherData decodeBitData(string linea, char separator, string memPre, int baseAddr, int memSize, int numRiga) { string[] valori = linea.Split(separator); int shift = 0; try { shift = (Convert.ToInt32(valori[0]) - 1) / (8 * memSize); } catch { } int resto = 0; Math.DivRem(numRiga, 8 * memSize, out resto); string memAddr = string.Format("{0}{1}.{2}", memPre, baseAddr + shift, resto); return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); } #endif #endregion /// /// effettua ogni log period una rilettura di TUTTI gli allarmi... /// public virtual void forceAlarmCheck() { } #region area simulazione /// /// lettura memoria in SIMULAZIONE /// public void simReadMem() { if (lastSim == null) { lastSim = DateTime.Now; } // simulazione complessa (basata su samples dei parametri effettivi...) if (utils.CRB("DemoInSample")) { if (numSim >= 0) { // verifico se la coda di simulazione sia piena altrimenti la RICARICO... if (QueueSIM.Count == 0) { leggiSimFile(); numSim--; } // processo la coda di simulazione (stacco ed accodo 1 valore...) B_input = QueueSIM.Dequeue(); countSim++; // ogni 5 loggo... if (countSim % 5 == 0) { lg.Info("Read {0} sim data", countSim); } } else { // fermo tutto! parentForm.sIN = Semaforo.SR; parentForm.fermaAdapter(false); } } // simulazione semplice (counter crescente) else { // se passato 1 sec genero NUOV num casuale e lo metto nel bit strobe... ogni 2 sec... if (DateTime.Now.Subtract(lastSim).TotalMilliseconds > 100) { countSim++; if (countSim > 255) countSim = 0; B_input = countSim; } } lastSim = DateTime.Now; } /// /// legge ed accoda file Sim in QueueSIM /// private void leggiSimFile() { int totRighe = 0; string fileName = utils.simDataFile; string linea; totRighe = File.ReadLines(fileName).Count(); lg.Info("File SIM: TROVATE {0} righe", totRighe); // ora conto righe effettive... totRighe = 0; // carica da file... StreamReader file = new StreamReader(fileName); // leggo 1 linea alla volta... int valore = 0; while ((linea = file.ReadLine()) != null) { // SE non è un commento... if (linea.Substring(0, 1) != ";") { //elencoAllarmi[rumRiga] = decodeAlarmLine(linea, ':'); Int32.TryParse(linea, out valore); QueueSIM.Enqueue(valore); totRighe++; } } // chiudo file file.Close(); lg.Info("File SIM: LETTE {0} righe", totRighe); } #endregion #region IOB METHODS /// /// Valore del num max invii consecutivi da coda... /// protected int nMaxSend = 5; /// /// DateTime Ultimo valore simulazione generato /// public DateTime lastSim; /// /// contatore x simulazione valori input /// public int countSim = 0; /// /// URL per check alive... /// public string urlAlive { get { return string.Format(@"{0}{1}{2}", currIobConf.serverData.MPIP, currIobConf.serverData.MPURL, currIobConf.serverData.CMDALIVE); } } /// /// URL per check alive... /// public string urlIobEnabled { get { return string.Format(@"{0}{1}{2}{3}", currIobConf.serverData.MPIP, currIobConf.serverData.MPURL, currIobConf.serverData.CMDENABLED, currIobConf.codIOB); } } /// /// 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(@"{0}{1}{2}", currIobConf.serverData.MPIP, currIobConf.serverData.MPURL, currIobConf.serverData.CMDBASE); // decodifica valore! string[] valori = qDecodeIN(queueVal); // aggiungo macchina e valore... answ += string.Format(@"{0}?valore={1}", currIobConf.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; } /// /// 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(@"{0}{1}{2}", currIobConf.serverData.MPIP, currIobConf.serverData.MPURL, currIobConf.serverData.CMDFLOG); // decodifica valore! string[] valori = qDecodeIN(queueVal); // aggiungo macchina e valore... answ += string.Format(@"{0}?flux={1}&&valore={2}", currIobConf.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; } /// /// Effettua chaimata URL e restituisce risultato /// /// /// public string callUrl(string URL) { string answ = ""; var client = new WebClient(); answ = client.DownloadString(URL); // restituisco valore! return answ; } /// /// Verifica se il server sia ALIVE /// private bool checkServerAlive { get { bool answ = false; if (DemoOut) { answ = true; } else { try { // chiamo URL, se restituisce "OK" è alive! answ = (callUrl(urlAlive) == "OK"); } catch { } // verifico SE è variato stato online/offline... if (MPOnline != answ) { // se ORA sono online riporto... if (answ) { lg.Info("SERVER ONLINE"); } else { lg.Info("SERVER OFFLINE"); } // salvo nuovo status... MPOnline = answ; } } return answ; } } /// /// Verifica se la IOB sia ENABLED (da server o Demo) /// private bool checkIobEnabled { get { bool answ = false; if (DemoOut) { answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); } else { try { // chiamo URL, se restituisce "OK" è enabled! answ = (callUrl(urlIobEnabled) == "OK"); } catch { } } // verifico SE è variato stato online/offline... if (IobOnline != answ) { // se ORA sono online riporto... if (answ) { lg.Info("IOB ONLINE"); } else { lg.Info("IOB OFFLINE"); } // salvo nuovo status... IobOnline = answ; } return answ; } } /// /// Processo la coda SignalIN... /// private void svuotaCodaSignIN() { // verifico SE la coda abbia dei valori... if (QueueIN.Count > 0) { // verifico se risponde il server... if (checkServerAlive) { // verifico SE posso inviare dati if (checkIobEnabled) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... if (QueueIN.Count > 0) { // INVIO!!! sendToMoonPro(urlType.SignIN, QueueIN.Dequeue()); } } } else { // mostro VETO-SEND x invio... GIALLO parentForm.sOUT = Semaforo.SG; } } else { // mostro SERVER KO x invio... ROSSO parentForm.sOUT = Semaforo.SR; } } } /// /// Processo la coda FLog... /// private void svuotaCodaFLog() { // verifico SE la coda abbia dei valori... if (QueueFLog.Count > 0) { // verifico se risponde il server... if (checkServerAlive) { // verifico SE posso inviare dati if (checkIobEnabled) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... if (QueueFLog.Count > 0) { // INVIO!!! sendToMoonPro(urlType.FLog, QueueFLog.Dequeue()); } } } else { // mostro VETO-SEND x invio... GIALLO parentForm.sOUT = Semaforo.SG; } } else { // mostro SERVER KO x invio... ROSSO parentForm.sOUT = Semaforo.SR; } } } /// /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... /// private void processAllMemory() { // in primis SALVO valori previous/precedenti B_previous = B_output; // poi faccio lettura NUOVI valori readAllData(); // eseguo il filtering dei valori (per i bit "blinking") filterData(); // effettuo confronto valori vecchi/nuovi... SE trovo variazione if (B_output != B_previous) { // mostro dati variati letti... displayInData(); // --> accodo (valore già formattato)! QueueIN.Enqueue(qEncodeIN); nReadFilt++; // Gestione counter SignIn! counterSigIN++; // se supera 10k resetto... if (counterSigIN > 9999) counterSigIN = 0; } } /// /// 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 (currIobConf.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 & ~currIobConf.BLINK_FILT; // calcolo il valore dei BIT che "passano la maschera" int iBlink = B_input & currIobConf.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) { lg.Info("START BLINK: B{0}", i); } // imposto comunque contatore al cambio fronte... i_counters[i] = currIobConf.MAX_COUNTER_BLINK; } } // quelli che sono zero... LI RECUPERO E LI PROCESSO... int iZero = ~B_input & currIobConf.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 { lg.Info("END BLINK: B{0}", i); } } } } } } /// /// 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, (int)B_output, counterSigIN); } catch { } return answ; } } /// /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont /// protected 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; } /// /// Decodifica valore della coda IN nel formato /// answ[0]=dtEve /// answ[1]=valore /// answ[2]=counter /// /// dtEve + '#' + value + '#' + cont /// protected string[] qDecodeIN(string queueVal) { return queueVal.Split('#'); } /// /// Effettua lettura dati /// public virtual void readAllData() { if (!DemoIn) { if (connectionOk) { readSemafori(); } else { lg.Error("Errore connessione mancante x readSemafori"); } } else { // segnalo ceh sono in Demo parentForm.sIN = Semaforo.SS; // simulo letura simReadMem(); } nReadIN++; // aggiorno valore mostrato... displayRawData(); } /// /// Effettua gestioen programma: legge e mostra su display... /// private void processProgram() { string currPrgName = ""; if (!DemoIn) { currPrgName = getPrgName(); } else { currPrgName = string.Format("{0:HHmmss}|O{0:HHmmss}", DateTime.Now); } // verifico SE sia cambiato il programma... if (lastPrgName != currPrgName) { // salvo! lastPrgName = currPrgName; string sVal = string.Format("[PROG]{0}", currPrgName); displayOtherData(sVal); // --> accodo (valore già formattato)! QueueFLog.Enqueue(qEncodeFLog("PROG", currPrgName)); // Gestione counter FLog! counterFLog++; // se supera 10k resetto... if (counterFLog > 9999) counterFLog = 0; } } /// /// Processo lettura dati sysinfo /// private void processSysInfo() { Dictionary currSysInfo = new Dictionary(); if (!DemoIn) { currSysInfo = getSysInfo(); } else { currSysInfo.Add("SYS-DEMO", string.Format("S-{0:HHmm}", DateTime.Now)); } // 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); displayOtherData(sVal); // --> accodo (valore già formattato)! QueueFLog.Enqueue(qEncodeFLog(item.Key, item.Value)); // Gestione counter FLog! counterFLog++; // se supera 10k resetto... if (counterFLog > 9999) counterFLog = 0; } } } /// /// Restituisce info sistema /// /// public virtual Dictionary getSysInfo() { Dictionary outVal = new Dictionary(); return outVal; } /// /// Restituisce programma in esecuzione /// public virtual string getPrgName() { return ""; } /// /// Effettua lettura semafori principale /// public virtual void readSemafori() { } /// /// Effettua invio a MoonPro del valore richiesto /// /// /// Valore da trasmettere: es /// INPUT: lo status rilevato in HEX /// FLog: il valore da trasmettere per il flusso indicato public void sendToMoonPro(urlType tipoUrl, string queueVal) { // recupero e formatto URL dati da coda... switch (tipoUrl) { case urlType.FLog: lastUrl = urlFLog(queueVal); break; case urlType.SignIN: lastUrl = urlInput(queueVal); break; default: lastUrl = ""; break; } // se NON sono in demo effettuo invio! if (!DemoOut) { // chiamo URL! string answ = callUrl(lastUrl); // loggo! lg.Info(string.Format("{0} -> {1}", queueVal, answ)); // se "OK" verde, altrimenti errore --> ROSSO if (answ == "OK") { parentForm.sOUT = Semaforo.SV; } else { parentForm.sOUT = Semaforo.SR; } } else { parentForm.sOUT = Semaforo.SV; // loggo! lg.Info(string.Format("{0} -> [SIM]", queueVal)); } nSendOut++; // riporto cosa inviato displayOutData(); } #endregion #region gestione dataMonitor (accodamento valori) /// /// Mostra i dati grezzi letti in esadecimale /// private void displayRawData() { // mostro update... string newString = string.Format("{0:X}", (int)B_input); accodaRawData(newString); // salvo coda debug... QueueDebug.Enqueue(B_input); } /// /// Update visualizzaizone BIT in ingresso /// public void displayInData() { // mostro update... string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr((int)B_output, 8)); accodaSignaInlData(newString); } /// /// Mostra cosa ha/avrebbe inviato /// public void displayOutData() { accodaUrlData(lastUrl); } /// /// Mostra cosa ha/avrebbe inviato /// /// public void displayOtherData(string newData) { // mostro update... accodaOtherData(newData); } /// /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA /// /// public void accodaRawData(string newLine) { // inserisco in cima allo stack, trimmo e aggiorno display parentForm.dataMonitor_0 = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_0)); } /// /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA /// /// public void accodaSignaInlData(string newLine) { // inserisco in cima allo stack, trimmo e aggiorno display parentForm.dataMonitor_1 = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_1)); } /// /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA /// /// public void accodaUrlData(string newLine) { // inserisco in cima allo stack, trimmo e aggiorno display parentForm.dataMonitor_2 = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_2)); } /// /// 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 parentForm.dataMonitor_3 = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3)); } /// /// Effettua un trim della stringa al numero max di linee da mostrare a video /// /// /// private 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; } #endregion } }