using IOB_UT_NEXT; using MapoSDK; using MathNet.Numerics.Statistics; using Newtonsoft.Json; using NLog; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace IOB_WIN_NEXT { public class IobGeneric { #region Protected Fields /// /// wrapper di log /// protected static Logger lg; protected bool _connOk = false; /// /// 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; /// /// NUm errori check alive /// protected int currAliveErrors = 0; /// /// Dizionario valori impostati x produzione /// protected Dictionary currProdData = new Dictionary(); /// /// num corrente errori read PLC /// protected int currReadErrors = 0; /// /// num errori send /// protected int currSendErrors = 0; /// /// 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; /// /// Dizionario ULTIMI valori impostati x produzione /// protected Dictionary lastProdData = new Dictionary(); /// /// 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; /// /// Num massimo di errori in funzionalità check alive /// protected int maxAliveErrors = utils.CRI("maxAliveErrors"); /// /// Num massimo di errori di rete read (dal PLC) /// protected int maxReadErrors = utils.CRI("maxReadErrors"); /// /// Num massimodi errori di rete send al server /// protected int maxSendErrors = utils.CRI("maxSendErrors"); /// /// 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) /// 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; /// /// Struttura allarmi mappati /// public List alarmMaps = new List(); /// /// Conf adapter corrente /// public IobConfiguration cIobConf; /// /// Conteggio ATTUALE ore macchina IN LAVORO /// public double contOreMaccLav; /// /// Conteggio ATTUALE ore macchina ON /// public double contOreMaccOn; /// /// contatore x simulazione valori input /// public int countSim = 0; /// /// ODL attualmente sulla macchina /// public Int32 currIdxODL = 0; /// /// Modo corrente (da classe ENUM) /// public CNC_MODE currMode; /// /// ODL corrente caricato sulla macchina (stringa, da chiamata MP/IO) /// public string currODL = ""; /// /// Indica se sia richiesto campionamento memoria PERIODICO /// public bool doSampleMemory; /// /// Indica se si debba leggere e fare DUMP delle aree di memoria (1 volta solo all'avvio x debug...) /// public bool doStartMemDump; /// /// Data/ora ultimo avvio adapter /// public DateTime dtAvvioAdp = DateTime.Now; /// /// Data/ora ultimo spegnimento adapter /// public DateTime dtStopAdp = DateTime.Now; /// /// Indicazione VETO check status IOB x evitare loop troppo stretti... /// public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1); /// /// Abilitazione lettura PrgName /// public bool enablePrgName = true; /// /// Abilitazione invio pezzi "in blocco" per recupero contapezzi /// public bool enableSendPzCountBlock = false; /// /// Determina se sia encessario convertire valori little/big endian (SIEMENS=true, OSAI=FALSE) /// public bool hasBigEndian = false; /// /// dataOra ultima verifica CNC disconnesso... /// public DateTime lastDisconnCheck; /// /// Data/ora ultima volta che IOB è stato dichairato online /// public DateTime lastIobOnline = DateTime.Now.AddHours(-1); /// /// dataOra ultimo log periodico... /// public DateTime lastPeriodicLog; /// /// dataOra ultimo PING inviato verso il PLC... /// public DateTime lastPING; /// /// DataOra ultima lettura da PLC /// public DateTime lastReadPLC; /// /// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...) /// public string lastSignInVal = ""; /// /// DateTime Ultimo valore simulazione generato /// public DateTime lastSim; /// /// dataOra ultimo segnale inviato al SERVER... /// public DateTime lastWatchDog; /// /// dataOra ultimo segnale inviato a macchina/PLC... /// public DateTime lastWatchDogPLC = DateTime.Now; /// /// Massimo numero di px da inviare in blocco /// public int maxSendPzCountBlock = 10; /// /// Struttura memoria PLC x lettura/scrittura da JSON file /// public plcMemMap memMap; /// /// Minimo numero di px da inviare in blocco /// public int minSendPzCountBlock = 5; /// /// Variabile booleana che indica se sia necessario fare refresh del contapezzi /// 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"= /// public bool procIotMem = false; /// /// Coda valori ALLARMI ove gestiti... /// public ConcurrentQueue QueueAlarm = new ConcurrentQueue(); /// /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) /// public ConcurrentQueue QueueFLog = new ConcurrentQueue(); /// /// Oggetto della coda degli elementi letti (e non ancora trasmessi) /// public ConcurrentQueue QueueIN = new ConcurrentQueue(); /// /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... /// public ConcurrentQueue QueueMessages = new ConcurrentQueue(); /// /// alias booleano false = R /// public bool R = false; /// /// 32 byte input base (es strobe, 8 word da 32 bit di flags...) /// public byte[] RawInput = new byte[32]; /// /// 32 byte output base (es ack, 8 word da 32 bit di flags...) /// public byte[] RawOutput = new byte[32]; /// /// Oggetto connessione REDIS /// public RedisIobCache redisMan; /// /// Oggetto cronometro x campionamento durate chiamate /// public Stopwatch stopwatch = new Stopwatch(); /// /// Oggetto gestione TempiCiclo e contapezzi /// public TCMan tcMan = new TCMan(0.5, 1.3, 5); /// /// Imposta veto chiamata split (durante chiamata, per 60 sec) /// public DateTime vetoSplit = DateTime.Now.AddMinutes(1); /// /// alias booleano true = W /// public bool W = true; #endregion Public Fields #region Public Constructors /// /// 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!"); } } #endregion Public Constructors #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); IobOnline = answ; } 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; // se disabilitato salto... if (pingDisabled) { answ = IPStatus.Success; } else { IPAddress address; PingReply reply; var rand = new Random(); int pingTOut = 500; using (Ping pingSender = new Ping()) { address = IPAddress.Loopback; int maxRetry = maxPingRetry + 1; int numRetry = 1; string ipAddr = cIobConf.serverData.MPIP.Replace($"{cIobConf.serverData.TRANSP}://", ""); // fix se fosse ip + porta... if (ipAddr.IndexOf(":") >= 0) { ipAddr = ipAddr.Substring(0, ipAddr.IndexOf(":")); } IPAddress.TryParse(ipAddr, out address); // se null --> provo DNS... if (address == null) { var rawAddresses = Dns.GetHostAddresses(ipAddr); if (rawAddresses.Length > 0) { address = rawAddresses[0]; } } try { // se != null --> uso address... if (address != null) { pingTOut = rand.Next(200, 400); reply = pingSender.Send(address, pingTOut); } else { pingTOut = rand.Next(300, 600); reply = pingSender.Send(cIobConf.cncIpAddr, pingTOut); } } catch (Exception exc) { pingTOut = rand.Next(100, 200); reply = pingSender.Send(IPAddress.Loopback, pingTOut); lgError($"ping to loopback addres per eccezione:{Environment.NewLine}{exc}"); } // se ho timeout riprovo... while (reply.Status != IPStatus.Success && numRetry < maxRetry) { Thread.Sleep(rand.Next(50, 200)); pingTOut = pingServerMsTimeout * numRetry / 2; reply = pingSender.Send(address, pingTOut); numRetry++; if (reply.Status == IPStatus.Success) { lgInfo("Server PING OK!"); break; } else { lgInfo($"Server Ping KO | reply: {reply.Status} --> retry | TimeOut: {pingTOut}"); } } } 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; } } /// /// Indica il counter della key richiesta attiva /// - gestito tramite Redis /// - a scadenza 25h /// - incrementato ogni invio di auto ODL /// protected int countKeyRichiesta { get { // calcolo key odierna int answ = redisMan.getKReqCount(dailyKey); return answ; } set { // calcolo key odierna redisMan.setKReqCount(dailyKey, value); } } protected string dailyKey { get { return DateTime.Today.ToString("yyMMdd"); } } /// /// Variabile di appoggio GLOBALE x indicare che si può forzare reset contapezzi con macchina in RUN /// protected bool forceResetInRun { get; set; } = false; /// /// Variabile di appoggio GLOBALE x indicare macchina RUN (es x gestione su reset pezzi) /// protected bool isRunning { get; set; } = false; /// /// 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"); protected string nextKeyRich { get { return $"{dailyKey}{countKeyRichiesta:00}"; } } /// /// 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; } /// /// Indica se sia stato resettato un contapezzi /// protected bool pzCountResetted { get; set; } = false; /// /// 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; } } /// /// Indica se vada inviata la key richiesta con lo split/autoODL /// protected bool sendKeyRichiesta { get; set; } = false; /// /// 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; int pingMsTimeout = cIobConf.pingMsTimeout; IPAddress.TryParse(cIobConf.cncIpAddr, out address); try { // se != null --> uso address... if (address != null) { reply = pingSender.Send(address, pingMsTimeout); } else { reply = pingSender.Send(cIobConf.cncIpAddr, pingMsTimeout); } } catch { reply = pingSender.Send(IPAddress.Loopback, 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...) /// public static bool DemoInSample { get { return baseUtils.CRB("DemoInSample"); } } /// /// Verifica se sia in modalità DEMO x dati OUTPUT /// public static bool DemoOut { get { return utils.CRB("DemoOut"); } } /// /// Indicazione VETO PING a server sino alla data-ora indicata /// public static DateTime dtVetoPing { get { return utils.dtVetoPing; } set { utils.dtVetoPing = value; } } /// /// Indicazione VETO invio a server sino alla data-ora indicata /// public static DateTime dtVetoSend { get { return utils.dtVetoSend; } set { utils.dtVetoSend = value; } } /// /// stato Online/Offline del server MP IO (su REDIS) /// public static bool MPOnline { get { return utils.MPIO_Online; } set { utils.MPIO_Online = value; } } /// /// Verifica se il server sia ALIVE (tramite PING) /// public bool checkServerAlive { get { bool answ = false; // controllo se ho un VETO all'invio... if (dtVetoPing < DateTime.Now) { if (DemoOut) { answ = true; } else { IPStatus pingStatus = testPingServer; // se passa il ping faccio il resto... if (pingStatus == IPStatus.Success) { string callResp = ""; try { // chiamo URL, se restituisce "OK" è alive! provo con chiamata http callResp = callUrl(urlAlive, false); answ = (callResp == "OK"); } catch (Exception exc) { lgError("Errore in checkServerAlive:{0}{1}", Environment.NewLine, exc); } // attesa casuale se necessario var rand = new Random(); // primi 3 test int maxTry = 3; while (maxTry > 0 && !answ) { try { Thread.Sleep(rand.Next(150, 500)); callResp = callUrl(urlAlive, false); answ = (callResp == "OK"); maxTry--; } catch { } } // se NON OK riprovo ANCORA 1 volta... if (!answ) { resetWebClients(); Thread.Sleep(rand.Next(500, 1000)); callResp = callUrl(urlAlive, false); answ = (callResp == "OK"); } // altri 3 maxTry = 3; while (maxTry > 0 && !answ) { try { Thread.Sleep(rand.Next(150, 500)); callResp = callUrl(urlAlive, false); answ = (callResp == "OK"); maxTry--; } catch { } } // verifico SE è variato stato online/offline... if (MPOnline != answ) { // se ORA sono online riporto... if (answ) { lgInfo("SERVER ONLINE in checkServerAlive"); parentForm.commSrvActive = 1; dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); } else { lgInfo("SERVER OFFLINE in checkServerAlive"); parentForm.commSrvActive = 0; } // salvo nuovo status... MPOnline = answ; } else { // allungo periodo controllo... dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20); } } else { lgInfo($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})"); MPOnline = false; // imposto veto a 10 volte reinvio dati standard... dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10); utils.dtVetoSend = dtVetoPing; } } } else { // altrimenti passo ultimo valore noto... answ = MPOnline; } #if false // se NON alive --> registro errore x possibile reboot if (!answ) { currAliveErrors++; lgError($"Server NON Alive | currAliveErrors: {currAliveErrors}"); // controllo reboot if (currAliveErrors > maxAliveErrors) { lgInfo("Superato limite errori Alive --> tryDisconnect"); currAliveErrors = 0; tryDisconnect(); } } else { currAliveErrors = 0; } #endif return answ; } } /// /// 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"); } } /// /// Indica lo stato Online/Offline della IOB /// public bool IobOnline { get { return utils.IOB_Online; } set { utils.IOB_Online = value; } } /// /// Verifica se sia macchina multi = DoppioPallet da CONF /// public bool isMulti { get { bool answ = false; if (cIobConf.optPar.Count > 0) { // cerco con chiave reale IS_MULTI string keyName = "IS_MULTI"; if (!cIobConf.optPar.ContainsKey(keyName)) { // legacy: accetto anche SIM_MULTI... keyName = "SIM_MULTI"; } // vera verifica su chiave... if (cIobConf.optPar.ContainsKey(keyName)) { string SIM_MULTI = getOptPar(keyName); answ = SIM_MULTI == "1"; } } return answ; } } /// /// Log verboso da configurazione (SOLO CHIAVE "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; } /// /// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per 100... 200% --> 200) /// public int maxPzDeltaPerc { get { int answ = 250; if (cIobConf.optPar.Count > 0) { // cerco con chiave MAX_PZ_INCR_PERC string keyName = "MAX_PZ_INCR_PERC"; // vera verifica su chiave... if (cIobConf.optPar.ContainsKey(keyName)) { string MAX_PZ_INCR_PERC = getOptPar(keyName); if (!int.TryParse(MAX_PZ_INCR_PERC, out answ)) { answ = 300; } } } 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; } } /// /// indica se ping disabilitato da optPar /// public bool pingDisabled { get { bool answ = false; bool.TryParse(getOptPar("NO_PING"), out answ); return answ; } } /// /// DataOra dell'ultima lettura variabile contapezzi da CNC in secondi /// public double plcAvgTc { get { double answ = tcMan.avgTC > 0 ? tcMan.avgTC : 1; return answ; } } /// /// DataOra dell'ultima lettura variabile contapezzi da CNC /// public DateTime plcLastPzRead { get { return tcMan.lastObservedData; } } /// /// Finestra dei byte da mostrare di default x il RawDataInput /// public int RawDataInputSize { get; set; } = 8; /// /// Indice di partenza della memoria RawData Input da mostrare /// public int RawDataInputStart { get; set; } = 0; /// /// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)... /// public string urlAddPzCount { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/savePzCountInc/{cIobConf.codIOB}?qty="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlAddPzCount"); } return answ; } } /// /// URL per check alive... /// public string urlAlive { get { return $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}"; } } /// /// URL per forzare split ODL... /// public string urlForceSplit { get { string answ = $"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMD_FORCLE_SPLIT_ODL}{cIobConf.codIOB}?doConfirm=true&qtyFromLast=true&roundStep=500"; // se richiesto inviare Key Richiesta --> accodo! if (sendKeyRichiesta) { answ += $"&keyRichiesta={nextKeyRich}"; } return answ; } } /// /// URL per salvataggio contapezzi... /// public string urlGetCurrODL { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrODL/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetCurrODL"); } return answ; } } /// /// URL per richiamo parametri da scrivere... /// public string urlGetParams2Write { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getObjItems2Write/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetParams2Write"); } return answ; } } /// /// URL per recupero contapezzi... /// public string urlGetPzCount { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCounter/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetPzCount"); } return answ; } } /// /// URL per recupero contapezzi REGISTRATI da TC... /// public string urlGetPzCountRec { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCounterTCRec/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetPzCountRec"); } return answ; } } /// /// URL per richiamo task da eseguire... /// public string urlGetTask2Exe { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getTask2Exe/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetTask2Exe"); } return answ; } } /// /// URL per recupero idle time IOB... /// public string urlIdleTime { get { return $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMD_IDLE_TIME}{cIobConf.codIOB}"; } } /// /// URL per recupero inizio ODL... /// public string urlInizioOdlIob { get { return $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMD_ODL_STARTED}{cIobConf.codIOB}"; } } /// /// URL per check se abilitato... /// public string urlIobEnabled { get { return $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDENABLED}{cIobConf.codIOB}"; } } /// /// URL per segnalazione reboot... /// public string urlReboot { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDREBO}{cIobConf.codIOB}&mac={GetMACAddress()}"; } catch { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDREBO}{cIobConf.codIOB}"; } return answ; } } /// /// URL per richiamo task da eseguire... /// public string urlRemTask2Exe { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/remTask2Exe/{cIobConf.codIOB}?taskName="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlRemTask2Exe"); } return answ; } } /// /// URL per salvataggio dati PARAMETRI IOB... /// public string urlSaveAllParams { get { string answ = ""; try { string machineName = Environment.MachineName; answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setObjItems/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSaveMemConf"); } return answ; } } /// /// URL per salvataggio dati conf memoria IOB... /// public string urlSaveMemMap { get { string answ = ""; try { string machineName = Environment.MachineName; answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveConf/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSaveMemConf"); } return answ; } } /// /// URL per INVIO di un update dello status di un certo allarme /// public string urlSendAlarm { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/sendAlarmBankUpdate/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlAddPzCount"); } return answ; } } /// /// URL per salvataggio dati associazione Machine 2 IOB... /// public string urlSetM2IOB { get { string answ = ""; try { string machineName = Environment.MachineName; answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setM2IOB/{cIobConf.codIOB}?IOB_name={machineName}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSetM2IOB"); } return answ; } } /// /// URL per salvataggio VALORI opzionali... /// public string urlSetOptVal { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/addOptPar/{cIobConf.codIOB}?"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSetOptVal"); } return answ; } } /// /// URL per salvataggio contapezzi... /// public string urlSetPzCount { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/{cIobConf.codIOB}?counter="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSetPzCount"); } return answ; } } /// /// URL per salvataggio contapezzi (quelli della macchina: PLC/CNC)... /// public string urlSetPzCountMAC { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/MAC_{cIobConf.codIOB}?counter="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSetPzCountMAC"); } return answ; } } /// /// URL per salvataggio in UPSERT dei PARAMETRI IOB scritti... /// public string urlUpdateWriteParams { get { string answ = ""; try { string machineName = Environment.MachineName; answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/upsertObjItems/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSaveMemConf"); } 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; } } #endregion Public Properties #region Private Methods /// /// 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); } /// /// 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) { 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); } } } } } } /// /// 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); } private void processMem2Write() { List updatedPar = new List(); // ciclo tutti gli oggetti write x vedere se modificati... foreach (var item in currProdData) { bool needWrite = false; // li cerco su last... se non ci sono o modificati --> salvo da scrivere e copio if (lastProdData.ContainsKey(item.Key)) { // verifico se variato... if (!item.Value.Equals(lastProdData[item.Key])) { needWrite = true; lastProdData[item.Key] = item.Value; } } // aggiungo else { needWrite = true; lastProdData.Add(item.Key, item.Value); } // se devo scrivere --> riporto if (needWrite) { // preparo obj da scrivere objItem newWrite = new objItem() { uid = item.Key, name = item.Key, reqValue = memMap.mMapWrite[item.Key].value, lastRequest = DateTime.Now, writable = true }; updatedPar.Add(newWrite); } } // se ho da scrivere... scrivo TUTTI! if (updatedPar.Count > 0) { // scrivo valore! lgInfo("Chiamate processMem2Write --> plcWriteParams"); plcWriteParams(ref updatedPar); // invio su cloud parametri! string rawData = JsonConvert.SerializeObject(updatedPar); utils.callUrl($"{urlUpdateWriteParams}", rawData); lgInfo("Notifica a server scrittura parametri"); } } /// /// 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); lastWatchDog = DateTime.Now; } else { // invio in blocco listaValori = QueueFLog.ToList(); // invio sendDataBlock(urlType.FLog, listaValori); // svuoto! QueueFLog = new ConcurrentQueue(); lastWatchDog = DateTime.Now; } } else { // INVIO SINGOLO...!!! QueueFLog.TryDequeue(out currVal); sendToMoonPro(urlType.FLog, currVal); lastWatchDog = DateTime.Now; } } 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 = Task.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 = Task.Run(() => svuotaCodaSignIN()); Task taskFlog = Task.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); } // se arrivo è tutto ok... currSendErrors = 0; } 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"); } #if false currSendErrors++; // se ho + di 30 errori --> riavvio if (currSendErrors > 30) { lgInfo("Superati 30 errori alive --> disconnecting"); currSendErrors = 0; tryDisconnect(); } #endif } } catch (Exception exc) { lgError($"Errore in fase trySendValues | currSendErrors: {currSendErrors}{Environment.NewLine}{exc}"); currDispData.semOut = Semaforo.SR; #if false currSendErrors++; // controllo reboot if (currSendErrors > maxSendErrors) { lgInfo("Superato limite errori Send --> tryDisconnect"); currSendErrors = 0; tryDisconnect(); } #endif } 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 PARAMETRI string jsonParams = getOptPar("PARAM_CONF"); if (!string.IsNullOrEmpty(jsonParams)) { string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonParams}"; lgInfo($"Apertura file {jsonFileName}"); StreamReader reader = new StreamReader(jsonFileName); string jsonData = reader.ReadToEnd(); if (!string.IsNullOrEmpty(jsonData)) { lgInfo($"File json PARAMETRI composto da {jsonData.Length} caratteri"); try { memMap = JsonConvert.DeserializeObject(jsonData); setupMemMap(); } 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"); } // inizializzo LUT decodifica ALLARMI string jsonAlarms = getOptPar("ALARM_CONF"); if (!string.IsNullOrEmpty(jsonAlarms)) { string jsonFileName = $"{Application.StartupPath}\\DATA\\CONF\\{jsonAlarms}"; lgInfo($"Apertura file {jsonFileName}"); StreamReader reader = new StreamReader(jsonFileName); string jsonData = reader.ReadToEnd(); if (!string.IsNullOrEmpty(jsonData)) { lgInfo($"File json ALLARMI composto da {jsonData.Length} caratteri"); try { alarmMaps = JsonConvert.DeserializeObject>(jsonData); setupAlarmMap(); } 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 ALARM_CONF in file INI"); } // loggo lgInfo("DONE loadMemConf"); } /// /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC /// /// protected virtual void plcWriteParams(ref 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(ref updatedPar); // invio su cloud parametri! string rawData = JsonConvert.SerializeObject(updatedPar); lgInfo("Notifica a server scrittura parametri"); utils.callUrl($"{urlUpdateWriteParams}", rawData); } } catch (Exception exc) { lgError($"Eccezione in processMemWriteRequests:{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(); } /// /// setup gestione allarmi da conf /// protected void setupAlarmMap() { // indico quanti allarmi foreach (var item in alarmMaps) { item.setupData(); // loggo lgInfo($"Decodifica aree alarmMap: {item.description} | {item.memAddr} x {item.size} byte | {item.messages} messaggi allarme"); } // invio oggetto alarmMap al server x successiva decodifica // FIXME TODO FARE !!!! invio PUT del file *_alarm.json } /// /// setup parametri da file di conf /// protected void setupMemMap() { lgInfo($"setupMemMap | trovati {memMap.mMapRead.Count} parametri Read (TSVC)"); lgInfo($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write"); if (utils.CRB("verbose")) { string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented); lgInfo($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}"); } // 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); var resp = 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, UM = item.Value.unit, valMin = item.Value.minVal, valMax = item.Value.maxVal }; 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, UM = item.Value.unit, valMin = item.Value.minVal, valMax = item.Value.maxVal }; allParam.Add(currItem); } // invio su cloud parametri! rawData = JsonConvert.SerializeObject(allParam); utils.callUrl($"{urlSaveAllParams}", rawData); lgInfo($"setupMemMap | salvata conf memoria R/W"); } } #endregion Protected Methods #region Public Methods /// /// Effettua chiamata URL e restituisce risultato /// /// /// invio in modalità async (NON GARANTITO ordine...) /// public static string callUrl(string URL, bool doAsync) { string answ = ""; // Chiamata ASINCRONA if (doAsync) { //Task resp = utils.callUrlAsync(URL); //answ = resp.Result; answ = utils.callUrlAsync(URL); } // chiamata SOLO NORMALE SINCRONA... else { answ = utils.callUrl(URL); } return answ; } /// /// Effettua chiamata URL e restituisce risultato /// /// /// /// invio in modalità async (NON GARANTITO ordine...) /// public static string callUrlWithPayload(string URL, string payload, bool doAsync) { string answ = ""; // Chiamata ASINCRONA if (doAsync) { answ = utils.callUrlAsync(URL, payload); } // chiamata SOLO NORMALE SINCRONA... else { answ = utils.callUrl(URL, payload); } return answ; } /// /// processa dataLayer e se necessario salva/mostra /// public static void checkSavePersDataLayer() { } public static string GetMACAddress() { NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); String sMacAddress = string.Empty; foreach (NetworkInterface adapter in nics) { if (string.IsNullOrEmpty(sMacAddress))// only return MAC Address from first card { IPInterfaceProperties properties = adapter.GetIPProperties(); //sMacAddress = adapter.GetPhysicalAddress().ToString(); sMacAddress = string.Join(":", (from z in adapter.GetPhysicalAddress().GetAddressBytes() select z.ToString("X2")).ToArray()); } } return sMacAddress; } public static void resetDebugConsole() { } /// /// Reset dei webclients /// public static void resetWebClients() { 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) { // controllo se memMap != null... if (memMap != 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... // recupero dati da memMap... if (memMap != null && memMap.mMapWrite != null) { 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}"; } } else { taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}"; } // salvo in currProd.. saveProdData(new KeyValuePair(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); } } else { lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!"); } } return taskDone; } /// /// Effettua verifica delle memorie WRITE, se ci siano variazioni da riportare verso il PLC /// Da impiegare ad esempio in caso di chiamate complesse (ad 2 aree di memoria condivise sul PLC; coem commessa + articolo EWON) /// /// /// 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; } /// /// forza reset ODL /// public void forceResetOdl() { lgInfo("Registrato richiesta forzatura reset ODL"); pzCountResetted = true; } /// /// Effettua chiamata x split ODL /// /// public bool forceSplitOdl() { bool fatto = false; if (vetoSplit < DateTime.Now) { // imposto veto x 1 minuto ad altre chiamate... vetoSplit = DateTime.Now.AddMinutes(1); // eseguo SOLO SE sono online... if (MPOnline && IobOnline) { string fullUrl = ""; string rawSplit = ""; string IOB_MULTI_CNAME = ""; string[] elencoMulti = null; try { /*************************************************** * Descrizione procedura (OK X SIMULATORI...) * * - chiamata su MP/IO * - verifica che su DB sia abilitato AUTO ODL * - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW * - viene duplicato e chiuso ODL corrente * - viene fatto partire ODL nuovo ADESSO * - num pezzi come ODL precedente (o da media 3 ODL precedenti) * - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL) * - reset contapezzi PLC locale... * * * * DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE * - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI) * - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!) * - viene fatto reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...) * - viene fatto reset + nuovo ODL (con stessi articoli e quantità) su follower, SENZA avere gestione x ODL di un codice esterno (quindi registra TUTTO ma NON RITORNERA' dati non avendo link verso esterno) * - serve NUOVA TABELLA delle macchine LEADER | FOLLOWER (1:n) e gestione da MP/IO * * ***************************************************/ if (isMulti) { // devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali... IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME"); elencoMulti = IOB_MULTI_CNAME.Split(','); } // se normale splitto! if (!isMulti) { // invio chiamata URL x reset ODL su macchina rawSplit = callUrl(urlForceSplit, false); fatto = (rawSplit != "KO") ? true : false; } // se multi gestisco il bit delle tavole... else { foreach (string item in elencoMulti) { // invio chiamata URL x reset ODL su macchina, ATTENZIONE scriviamo | al posto di "#" che in URL sarebbe filtrato... fullUrl = $"{urlForceSplit}&multi={item}"; rawSplit = callUrl(fullUrl, false); } fatto = (rawSplit == "OK") ? true : false; } } catch (Exception exc) { lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}"); } // se fatto --> resetto contapezzi!!! if (fatto) { contapezziPLC = 0; contapezziIOB = 0; } } else { lgError("Richiesto forceSplitOdl ma MP/IOB offline --> NON eseguito"); } } else { lgError("Richiesto forceSplitOdl ma veto attivo --> NON eseguito"); } 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(); processMem2Write(); } 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($"Veto Time Elapsed | lastConnectTry: {lastConnectTry} | waitRecMSec {waitRecMSec} ms) | NOW tryConnect"); lastConnectTry = DateTime.Now; tryConnect(); } } currDispData.semIn = Semaforo.SR; processDisconnectedTask(); } raiseRefresh(currDispData); } /// /// Recupera eventuali allarmi CNC... /// public virtual Dictionary getCncAlarms() { Dictionary outVal = new Dictionary(); return outVal; } /// /// Restituisce info DINAMICHE /// /// public virtual Dictionary getDynData() { Dictionary outVal = new Dictionary(); 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 /// /// public virtual Dictionary getOverrides() { Dictionary outVal = new Dictionary(); return outVal; } /// /// Restituisce programma in esecuzione /// public virtual string getPrgName() { return ""; } /// /// Restituisce info sistema /// /// public virtual Dictionary getSysInfo() { Dictionary outVal = new Dictionary(); 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 = -999999; // cerco VC... if (TSVC_Data.ContainsKey(VCName)) { try { switch (TSVC_Data[VCName].Funzione) { case VC_func.POINT: // prendo PRIMO answ = TSVC_Data[VCName].dataArray.FirstOrDefault(); break; case VC_func.AVG: answ = TSVC_Data[VCName].dataArray.Average(); break; case VC_func.MEDIAN: answ = TSVC_Data[VCName].dataArray.Median(); break; case VC_func.MIN: answ = TSVC_Data[VCName].dataArray.Min(); break; case VC_func.MAX: default: answ = TSVC_Data[VCName].dataArray.Max(); break; } } catch { } // 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 /// /// Tipo di URL (eventi / FLog) /// elenco di valori da coda string salvata /// public string jsonPayload(urlType tipoUrl, List elencoValori) { string answ = ""; if (elencoValori != null) { if (tipoUrl == urlType.FLog) { flogData currData = new flogData(); flogJsonPayload fullObj = new flogJsonPayload(); fullObj.fluxData = new List(); string[] valori; int counter = 0; DateTime dtEve = DateTime.Now; // inizio processando ogni valore foreach (var item in elencoValori) { valori = qDecodeIN(item); //DateTime.TryParse(valori[0], out dtEve); CultureInfo provider = CultureInfo.InvariantCulture; DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); int.TryParse(valori[3], out counter); currData = new flogData() { flux = valori[1], valore = valori[2], dtEve = dtEve, dtCurr = DateTime.Now, cnt = counter }; fullObj.fluxData.Add(currData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullObj); } catch (Exception exc) { lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } } else { evData currData = new evData(); evJsonPayload fullObj = new evJsonPayload(); fullObj.eventList = new List(); string[] valori; int counter = 0; DateTime dtEve = DateTime.Now; // inizio processando ogni valore foreach (var item in elencoValori) { valori = qDecodeIN(item); //DateTime.TryParse(valori[0], out dtEve); CultureInfo provider = CultureInfo.InvariantCulture; DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); int.TryParse(valori[2], out counter); currData = new evData() { valore = valori[1], dtEve = dtEve, dtCurr = DateTime.Now, cnt = counter }; fullObj.eventList.Add(currData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullObj); } catch (Exception exc) { lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } } } 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 /// public void processAutoOdl() { bool fatto = false; if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL"))) { lgInfo("Richiesta processAutoOdl"); string IOB_MULTI_CNAME = ""; string[] elencoMulti = null; string fullUrl = ""; if (isMulti) { // devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali... IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME"); elencoMulti = IOB_MULTI_CNAME.Split(','); } // controllo SIA abilitato... bool doProc = false; DateTime adesso = DateTime.Now; bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProc); if (doProc) { lgInfo("AUTO_CHANGE_ODL abilitato"); bool callChangeODL = false; // modalità change ODL string CHANGE_ODL_MODE = "TIME"; if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE"))) { CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE"); } if (CHANGE_ODL_MODE == "PZCOUNT_RESET") { /* verifico se sia "armato" il reset cambio ODL, DEVO essere : * - NON in produzione * - contapezzi ACT < contapezzi LAST * */ lgInfo("processAutoOdl: caso PZCOUNT_RESET"); if ((!isRunning || forceResetInRun) && pzCountResetted) { callChangeODL = true; lgInfo("Attivato cambio ODL da PZCOUNT_RESET"); } else { lgInfo($"isRunning: {isRunning} | pzCountResetted: {pzCountResetted} | forceResetInRun: {forceResetInRun} | contapezziIOB: {contapezziIOB} | contapezziPLC: {contapezziPLC}"); } } else if (CHANGE_ODL_MODE == "TIME") { // carico i parametri di configurazione x reset ODL... string CHANGE_ODL_HOURS = getOptPar("CHANGE_ODL_HOURS"); string CHANGE_ODL_IDLE_MIN = getOptPar("CHANGE_ODL_IDLE_MIN"); if (!string.IsNullOrEmpty(CHANGE_ODL_HOURS) && !string.IsNullOrEmpty(CHANGE_ODL_IDLE_MIN)) { int minOdlDurHours = -1; int minPlcIdelMin = -1; int.TryParse(CHANGE_ODL_HOURS, out minOdlDurHours); int.TryParse(CHANGE_ODL_IDLE_MIN, out minPlcIdelMin); // controllo parametri validi if (minOdlDurHours > 0 && minPlcIdelMin >= 0) { // leggo da server inizio ODL... se non multi 1 solo... DateTime inizioOdl = DateTime.Now; string rawDataInizio = ""; if (!isMulti) { rawDataInizio = callUrl(urlInizioOdlIob, false); DateTime.TryParse(rawDataInizio, out inizioOdl); } else { DateTime tmpData = DateTime.Now; // prendo il + vecchio... foreach (var item in elencoMulti) { fullUrl = $"{urlInizioOdlIob}|{item}"; rawDataInizio = callUrl(fullUrl, false); DateTime.TryParse(rawDataInizio, out tmpData); inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl; } } // verifico se sia scaduto... if (inizioOdl.AddHours(minOdlDurHours) < adesso) { string rawIdle = ""; int idlePeriod = 0; if (!isMulti) { // controllo SE sono fermo (spento o in manuale) per il periodo minimo richiesto... rawIdle = callUrl(urlIdleTime, false); int.TryParse(rawIdle, out idlePeriod); } else { int tmpIdle = 0; // prendo il + grande... foreach (var item in elencoMulti) { fullUrl = $"{urlIdleTime}|{item}"; rawIdle = callUrl(fullUrl, false); int.TryParse(rawIdle, out tmpIdle); idlePeriod = tmpIdle > idlePeriod ? tmpIdle : idlePeriod; } } if (idlePeriod >= minPlcIdelMin) { callChangeODL = true; } } } } } // vero processing... if (callChangeODL) { lgInfo("Chiamata: processAutoOdl --> forceSplitODL"); fatto = forceSplitOdl(); pzCountResetted = false; lgInfo("Esecuzione processAutoOdl completata --> pzCountResetted = false"); } } else { lgInfo("AUTO_CHANGE_ODL DISABILITATO"); } } // loggo se fatto if (fatto) { lgInfo("Effettuato processAutoOdl"); } } /// /// Effettua processing degli allarmi CNC SE disponibili /// public void processCncAlarms() { if (utils.CRB("enableAlarms")) { Dictionary currAlarms = new Dictionary(); if (connectionOk) { currAlarms = getCncAlarms(); } else { lgError("Errore connessione mancante x getCncAlarms"); } // verifico SE sia cambiato il programma... if (currAlarms.Count > 0) { try { string sVal = ""; if (lastAlarm != currAlarms["CNC_ALARM"]) { // salvo! lastAlarm = currAlarms["CNC_ALARM"]; // per ogni valore del dizionario mostro ed accodo! foreach (var item in currAlarms) { sVal = string.Format("[CNC_ALARM]{0}|{1}", item.Key, item.Value); // chiamo accodamento... accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); } } // accodo ALTRI allarmi NON CNC... foreach (var item in currAlarms.Where(X => X.Key != "CNC_ALARM")) { sVal = $"{item.Key} | {item.Value}"; accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); } } catch (Exception exc) { lgError("Eccezione in processCncAlarms{0}{1}", Environment.NewLine, exc); } } } } /// /// Effettua processing contapezzi (ed eventualmente alza il bit di contapezzo...) /// public virtual void processContapezzi() { } /// /// Task periodici SE disconnesso /// public virtual void processDisconnectedTask() { } /// /// Effettua processing del recupero dei valori dinamici: /// es: speed (RPM, feedrate) degli assi, valori pressioni, temeprature /// public void processDynData() { bool enableByApp = utils.CRB("enableDynData"); bool enableByIob = (getOptPar("ENABLE_DYN_DATA") == "TRUE"); bool forceSendByIob = (getOptPar("FORCE_DYN_DATA") == "TRUE"); Dictionary currDynData = new Dictionary(); if (enableByApp || enableByIob) { lgInfo("Inizio processDynData"); if (connectionOk) { currDynData = getDynData(); } else { lgError("Errore connessione mancante x getDynData"); } try { string sVal = ""; // se richiesto send diretto... if (forceSendByIob) { // per ogni valore del dizionario mostro ed accodo! foreach (var item in currDynData) { sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); // chiamo accodamento... accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); } } // altrimenti verifico SE sia cambiato il valore dei DynData... else if (lastDynDataCtrlVal != currDynData["DYNDATA"]) { // salvo! lastDynDataCtrlVal = currDynData["DYNDATA"]; // per ogni valore del dizionario mostro ed accodo! foreach (var item in currDynData) { sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); // chiamo accodamento... accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); } } // salvo array... lastDynData = currDynData; } catch (Exception exc) { lgError(exc, "Eccezione in processDynData"); } } } /// /// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC... /// public void processMemoryDiscon() { // init obj display newDisplayData currDispData = new newDisplayData(); // controllo contatore invio "keepalive"... invio solo a scadenza if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec")) { // resetto tutti i vlaori BYTE IN/PREV/OUT... così invio macchina spenta... B_input = 0; B_output = 0; B_previous = 0; accodaSigIN(ref currDispData); // update controllo lastDisconnCheck = DateTime.Now; } raiseRefresh(currDispData); } /// /// Effettua processing mode/status (EDIT/MDI/...) /// public virtual void processMode() { } /// /// Effettua processing ALTRI contatori/parametri (ed invia ad IO) /// public virtual void processOtherCounters() { } /// /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid) /// public virtual void processOverride() { bool enableByApp = utils.CRB("enableOverrides"); bool enableByIob = (getOptPar("ENABLE_OVERRIDES") == "TRUE"); Dictionary currOverride = new Dictionary(); if (enableByApp || enableByIob) { lgInfo("Inizio processOverride"); if (connectionOk) { currOverride = getOverrides(); } else { lgError("Errore connessione mancante x getOverrides"); } // SE sono connesso... if (connectionOk) { // se HO dei valori override... if (currOverride.Count > 0) { // verifico SE sia cambiato il programma... if (lastOverrideFS != currOverride["FEED_OVER"] || lastOverrideRapid != currOverride["RAPID_OVER"]) { // salvo! lastOverrideFS = currOverride["FEED_OVER"]; lastOverrideRapid = currOverride["RAPID_OVER"]; // per ogni valore del dizionario mostro ed accodo! string sVal = ""; foreach (var item in currOverride) { sVal = string.Format("[OVERRIDES]{0}|{1}", item.Key, item.Value); // chiamo accodamento... accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); } } } } } } /// /// 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 S01: {task2exe.Count} task ricevuti:"); int idTask = 0; foreach (var item in task2exe) { idTask++; lgInfo($"[{idTask:00}] - {item.Key} --> {item.Value}"); } // chiamo procedura esecutiva (diversa x ogni IOB) taskDone = executeTasks(task2exe); lgInfo($"Task2Exe S02: eseguiti {taskDone.Count} task"); // 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 /// public virtual void processVHF() { } /// /// Classe fittizia in caso di processing watchdog data /// public virtual void processWhatchDog() { } /// /// 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 /// public virtual void readAllData(ref newDisplayData currDispData) { if (currDispData == null) { currDispData = new newDisplayData(); } try { if (DemoIn) { // segnalo che sono in Demo currDispData.semIn = Semaforo.SV; } if (connectionOk) { readSemafori(ref currDispData); } else { lgError("Errore connessione mancante x readSemafori"); } nReadIN++; // aggiorno valore mostrato... displayRawData(ref currDispData); } catch { currDispData.semIn = Semaforo.SR; } raiseRefresh(currDispData); } /// /// Effettua lettura semafori principale /// Parametri da aggiornare x display in form /// public virtual void readSemafori(ref newDisplayData currDispData) { lastReadPLC = DateTime.Now; } /// /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati (32bit) /// 2021.08.27: filtrati secondo i valori setup start/size RawDataInput /// 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}"); // Do x scontato siano VALIDI i valori di RawDataInputStart / size --> filtro... var filtRawData = new byte[RawDataInputSize]; Array.Copy(RawInput, RawDataInputStart, filtRawData, 0, RawDataInputSize); int i = RawDataInputStart; foreach (var item in filtRawData) { 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 /// /// tipo di DUMP public virtual void saveMemDump(dumpType tipo) { } /// /// 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); } } /// /// Effettua salvataggio in LUT del valore ricevuto /// /// /// /// /// public virtual void saveValue(ref Dictionary outVal, double valore, string chiave) { //check obj preliminare if (outVal == null) { outVal = new Dictionary(); } bool scaduto = stackVal_TSVC(chiave, valore); // recupero VC valore = getVal_TSVC(chiave, scaduto); if (scaduto) { // limite a 3 digit x valore float outVal.Add(chiave, $"{valore:N3}"); } LastTSVC[chiave] = valore; } /// /// Invio la variazione dello stato allarmi (se avvenuta) /// /// COD memoria allarmi /// Indice memoria allarmi /// ultimo stato rilevato /// stato corrente /// Lista allarmi validi per l'area /// public bool sendAlarmVariations(string memAddr, int index, uint lastStatus, uint currStatus, List AlarmList) { bool fatto = false; if (lastStatus != currStatus) { List ActiveAlarmList = new List(); // invio GET del MemoryAddress, del banco e del valore currStatus lastUrl = $"{urlSendAlarm}?memAddr={memAddr}&index={index}&currStatus={currStatus}"; if (currStatus == 0) { ActiveAlarmList.Add("ALL OK"); } else { // calcolo quali allarmi mostrare dato currStatus ed elenco allarmi string bitMap = Convert.ToString(currStatus, 2); int bitLen = bitMap.Length; // ciclo x associare tutti gli allarmi for (int i = 0; i < bitLen; i++) { // se è 1 --> aggiungo! if (bitMap[bitLen - i - 1] == '1') { // se sono entro limiti if (AlarmList.Count >= i) { ActiveAlarmList.Add($"{i:000}-{AlarmList.ElementAt(i)}"); } //else //{ // ActiveAlarmList.Add($"Unknown Bit.{i}"); //} } } } string rawData = JsonConvert.SerializeObject(ActiveAlarmList); string resp = utils.callUrlNow(lastUrl, rawData); if (!string.IsNullOrEmpty(resp)) { if (resp.ToUpper() == "OK") { // segnalo fatto fatto = true; } } } return fatto; } /// /// Invia una LISTA di valori /// /// /// public void sendDataBlock(urlType tipoUrl, List listQueueVal) { // init obj display newDisplayData currDispData = new newDisplayData(); if (listQueueVal != null) { try { // recupero e formatto URL dati da coda... lastUrl = urlDataBlock(tipoUrl); // in base al tipo di dato compongo il payload Json da inviare string payload = jsonPayload(tipoUrl, listQueueVal); // async a true SE FLog bool doAsync = tipoUrl == urlType.FLog ? true : false; // se NON sono in demo effettuo invio! if (!DemoOut) { // SE server alive... if (checkServerAlive) { // chiamo URL! string answ = callUrlWithPayload(lastUrl, payload, doAsync); // loggo! lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}"); // se "OK" verde, altrimenti errore --> ROSSO if (answ.Contains("OK")) { currDispData.semOut = Semaforo.SV; // se oltre 1 min NON era online --> check pezzi! if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) { lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } lastIobOnline = DateTime.Now; } else { currDispData.semOut = Semaforo.SR; } } else { lgInfo($"[SERVER KO] {listQueueVal.Count}"); } } else { currDispData.semOut = Semaforo.SV; // loggo! lgInfo($"{listQueueVal.Count} records --> [SIM]"); } nSendOut += listQueueVal.Count; // riporto cosa inviato currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... lastWatchDog = DateTime.Now; } catch { currDispData.semOut = Semaforo.SR; } } raiseRefresh(currDispData); } /// /// 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) { // init obj display newDisplayData currDispData = new newDisplayData(); try { // 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) { // SE server alive... if (checkServerAlive) { // chiamo URL! string answ = callUrl(lastUrl, false); // loggo! lgInfo(string.Format("[SEND] {0} -> {1}", queueVal, answ)); // se oltre 1 min NON era online --> check pezzi! if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) { lgInfo($"sendToMoonPro --> offline timeaout ({lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } // se richiesto effettuo refresh contapezzi if (needRefreshPzCount && !isMulti) { pzCntReload(true); needRefreshPzCount = false; } lastIobOnline = DateTime.Now; // se "OK" verde, altrimenti errore --> ROSSO if (answ == "OK") { currDispData.semOut = Semaforo.SV; } else { currDispData.semOut = Semaforo.SR; } } else { lgInfo(string.Format("[SERVER KO] {0}", queueVal)); } } else { currDispData.semOut = Semaforo.SV; // loggo! lgInfo(string.Format("{0} -> [SIM]", queueVal)); } nSendOut++; currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... lastWatchDog = DateTime.Now; } catch { currDispData.semOut = Semaforo.SR; } raiseRefresh(currDispData); } /// /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi... /// /// Pezzi richiesti /// public virtual bool setcontapezziPLC(int newPzCount) { 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)) { 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; } /// /// 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("[CLOSING] Svuotamento FORZATO coda segnali..."); while (QueueIN.Count > 0) { // INVIO COMUNQUE...!!! string valore = ""; QueueIN.TryDequeue(out valore); sendToMoonPro(urlType.SignIN, valore); } parentForm.displayTaskAndLog("[CLOSING] 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("[STOP] Stopping adapter..."); adpTryRestart = false; parentForm.displayTaskAndLog("[STOP] 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; } } } } /// /// Metodo base connessione... /// public virtual void tryConnect() { dtAvvioAdp = DateTime.Now; } /// /// 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"); } } /// /// Inserimento/aggiornamento chiavi/valore in currProdData /// /// /// public void upsertKey(string chiave, string valore) { if (currProdData.ContainsKey(chiave)) { currProdData[chiave] = valore; } else { currProdData.Add(chiave, valore); } } /// /// 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 = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{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 } /// /// Evento per incapsulare dati x refresh pagina /// public class iobRefreshedEventArgs : EventArgs { #region Private Fields /// /// classe obj privata /// private readonly newDisplayData _newDisplayData; #endregion Private Fields #region Public Constructors /// /// salvataggio obj /// /// public iobRefreshedEventArgs(newDisplayData newObject) { _newDisplayData = newObject; } #endregion Public Constructors #region Public Properties /// /// Proprietà lettura displayData aggiornato /// public newDisplayData DisplayDataObject { get { return _newDisplayData; } } #endregion Public Properties } }