using EgwProxy.Ftp; using IOB_UT_NEXT; using MapoSDK; using MathNet.Numerics.Statistics; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using System; using System.Collections; using System.Collections.Generic; 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.Web; using System.Windows.Forms; using System.Xml.Serialization; using static IOB_UT_NEXT.CustomObj; using static IOB_UT_NEXT.DataModel.Fimat; using static MapoSDK.WharehouseData; namespace IOB_WIN_NEXT.Iob { public class Generic : BaseObj { #region Public Fields public int numPzReqOdl = 0; #endregion Public Fields #region Public Constructors /// /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto /// /// /// public Generic(AdapterForm caller, IobConfiguration IOBConf) { if (IOBConf != null) { // init oggetto redis... redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.filenameIOB, $"{IOBConf.tipoIob}", IOBConf.minDeltaSec); //redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB, $"{IOBConf.tipoIob}", IOBConf.minDeltaSec); // initi oggetto TCMan tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz); // salvo il form chiamante parentForm = caller; // configurazione... cIobConf = IOBConf; // imposto le code! QueueIN = new DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue); lastConnectTry = DateTime.Now; // aggiungo nel logger IDX Macchina lg = LogManager.GetCurrentClassLogger(); lgInfo("Avvio preliminare AdapterGeneric"); lastLogStartup = DateTime.Now; // setup currProdData & last prod data currProdData = redisMan.redGetHashDict(rKeyCurrProdData); // aggiungo altri defaults setDefaults(true); // imposta valori memoria (e resetta parametri su server) setParamPlc(); // checkLogDir x shrink! checkShrinkDir(); // imposto veto invio per i prossimi sec dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn); lgInfoStartup($"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"); // concluso! lgInfoStartup("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 Public Properties /// /// 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 { lgError("SERVER OFFLINE in checkServerAlive"); parentForm.commSrvActive = 0; } // salvo nuovo status... MPOnline = answ; } else { // allungo periodo controllo... dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20); } } else { lgError($"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; } 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 RawTransf /// public int counterRawTransf { get; set; } /// /// Contatore x invio dati SignalIN /// public int counterSigIN { get; set; } /// /// Contatore x invio dati UserLog /// public int counterULog { 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 se la chiamata WDST dit racking watchdog sia disabilitata dall'invio nel FluxLog /// public bool disableWdst { get; set; } = false; /// /// 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 INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC) in una data SPECIFICA /// public string urlAddPzCountAtDate { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/savePzCountIncAtDate/{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 creazione di un PODL da cod art (metodo GET)... /// public string urlCreatePOdl { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/forceCreatePOdl/{cIobConf.codIOB}?"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlStartPODL"); } return answ; } } /// /// URL per chiamata generazione Snapshot Dossier giornalieri alla data /// public string urlFixDailyDossier { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/fixDailyDossier/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlFixDailyDossier"); } return answ; } } /// /// URL per chiamata generazione ODL giornalieri alla data /// public string urlFixDailyOdl { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/fixDailyOdl/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlFixDailyOdl"); } return answ; } } /// /// 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 recupero ARTICOLI correnti x macchina... /// public string urlGetCurrArt { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getLastArtByMacc/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetCurrArt"); } return answ; } } /// /// URL per recupero DOSS correnti x macchina... /// public string urlGetCurrDOSS { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getLastDossByMacc/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetCurrDOSS"); } return answ; } } /// /// URL per recupero dati ODL corrente... /// 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 recupero PODL correnti x macchina... /// public string urlGetCurrPODL { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrPODL/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetCurrPODL"); } return answ; } } /// /// URL per recupero ListValue tipo fasi... /// public string urlGetListValFasiPodl { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getListValByTable/PODL"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetListValFasiPodl"); } return answ; } } /// /// URL per recupero num pezzi ODL corrente... /// public string urlGetNumPzCurrODL { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrOdlQtaReq/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlGetNumPzCurrODL"); } 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 richiesta chiusura manuale ad utente x ODL corrente (metodo GET)... /// public string urlODLAskClose { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/askCloseODL/{cIobConf.codIOB}?idxOdl="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlODLClose"); } return answ; } } /// /// URL per chiusura ODL corrente (metodo GET)... /// public string urlODLClose { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/closeODL/{cIobConf.codIOB}?idxOdl="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlODLClose"); } return answ; } } /// /// URL per AVVIO di un ODL da PODL indicato (metodo GET)... /// public string urlOdlStartFromPOdl { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/forceStartPOdl/{cIobConf.codIOB}?idxPODL="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlStartPODL"); } return answ; } } /// /// URL per chiusura PODL --> ODL corrente (metodo GET)... /// public string urlPODLClose { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/closePODL/{cIobConf.codIOB}?idxPOdl="; } catch (Exception exc) { lgError(exc, "Errore in composizione urlPODLClose"); } return answ; } } /// /// 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 chiusura PODL --> ODL corrente (metodo GET)... /// public string urlSetHashDict { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setRedisHashDict/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlSetHashDict"); } 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 effettuare salvataggio parametri (snapshot) macchina... /// public string urlTakeSnapshot { get { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/takeFlogSnapshot/{cIobConf.codIOB}"; } catch (Exception exc) { lgError(exc, "Errore in composizione urlTakeSnapshot"); } 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 urlUpdateWriteParams"); } 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 Public Methods /// /// 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 da coda FLog per superamento maxLengh: {currVal}"); } } } // loggo! lgTrace(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 parentForm.WriteTextSafe(newLine); } /// /// Accumula in coda i valori RawData + log /// /// /// public void accodaRawData(rawTransfType mesType, object mesContent) { /*-------------------------------- * nuova gestione coda dictionary * fixme todo da fare !!! * * - conterrà una lista di oggetti baseRawTransf * - i dati vanno poi "scodati" dal + vecchio ed inviati a MP/IO * - mostra un sunto delle info da inviare * - accodamento vero e proprio * - verifica (opzionale) coda massima x gestire roundRobin ultimi eventi * - trace della coda * - counter invio??? valutare se c'è dataora e poi sono da salvare su MongoDb / Redis * * */ // serializzo il valore... JObject njObj; if (mesType == rawTransfType.IcoelBatch || mesType == rawTransfType.IcoelVarInfo) { njObj = (JObject)mesContent; } else { njObj = (JObject)JToken.FromObject(mesContent); } BaseRawTransf newVal = new BaseRawTransf(DateTime.Now, njObj, mesType); string encodedVal = JsonConvert.SerializeObject(newVal); // --> accodo (valore già formattato)! QueueRawTransf.Enqueue(encodedVal); // se abilitato controllo coda Max (superiore a 0...) if (maxQueueRawTransf > 0) { // se ho una coda superiore a max ammesso if (QueueRawTransf.Count > maxQueueRawTransf) { // elimino valori iniziali fino a tornare al max ammesso... while (QueueRawTransf.Count > maxQueueRawTransf) { string currVal = ""; QueueRawTransf.TryDequeue(out currVal); lgInfo($"Eliminazione da coda RawTransf per superamento maxLengh: {currVal}"); } } } // loggo! lgTrace(string.Format("[QUEUE-RTRANSF] {0}", encodedVal)); counterRawTransf++; if (counterRawTransf > 9999) { counterRawTransf = 0; } } /// /// Accumula in coda i valori Signal IN e logga... Parametri /// da aggiornare x display in form /// public void accodaSigIN(ref newDisplayData currDispData) { DateTime adesso = DateTime.Now; // mostro dati variati letti... displayInData(ref currDispData); // verifico veto a invio status macchina string keyName = "VETO_SIG_IN"; if (getOptPar(keyName).ToUpper() == "TRUE") { lgTrace($"Filtrato accodamento valore da conf IOB | {keyName} | [QUEUE-IN] {qEncodeIN}"); } else { // verifico non sia in veto invio iniziale... if (queueInEnabCurr) { // --> accodo (valore già formattato)! QueueIN.Enqueue(qEncodeIN); // loggo! lgDebug($"[QUEUE-IN] {qEncodeIN}"); } else { lgDebug($"[VETO FOR QUEUE-IN] | {qEncodeIN} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}"); checkVetoQueueIn(); } // aggiorno counters ed eventuale reset nReadFilt++; if (nReadFilt > int.MaxValue - 1) { nReadFilt = 0; // per evitare buffer overflow... } counterSigIN++; if (counterSigIN > 9999) { counterSigIN = 0; } } } /// /// Accumula in coda i valori USER LOG e logga... /// /// VALORE RAW (x display) /// VALORE già processato con qEncodeULog(...) public void accodaUserLog(string val, string encodedVal) { // mostro dati variati letti... displayOtherData(val); // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! QueueULog.Enqueue(encodedVal); // loggo! lgInfo(string.Format("[QUEUE-USER-LOG] {0}", encodedVal)); counterULog++; if (counterULog > 9999) { counterFLog = 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.newInData = $"{B_output}"; 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.setArt: case taskType.setComm: case taskType.setProg: case taskType.setPzComm: // 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 setParameter --> 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; case taskType.syncDbData: processDataSync(); break; case taskType.processOtherInfo: bool okProc = processOtherInfo(item.Key, item.Value); taskVal = okProc ? $"OK processOtherInfo | {item.Key} | {item.Value}" : $"ERROR processOtherInfo | {item.Key} | {item.Value}"; break; default: taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); break; } // aggiungo task! taskDone.Add(item.Key, taskVal); } } else { foreach (var item in task2exe) { // converto richiesta in enum... taskType tName = taskType.nihil; Enum.TryParse(item.Key, out tName); string taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED (no MemMap) | NO EXEC"; // aggiungo task! taskDone.Add(item.Key, taskVal); } lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe! Tutte le richieste sono state chiuse senza esecuzione"); } } 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) { lgInfo("Richiesto forceSplitOdl"); // 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 okReport 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 okReport reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...) * - viene okReport 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); lgDebug($"Esecuzione forceSplit | URL: {fullUrl} | esito: {rawSplit}"); } fatto = (rawSplit == "OK") ? true : false; } } catch (Exception exc) { lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}"); } // se okReport --> 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; } /// /// Processing degli allarmi (se presenti e gestiti) /// /// public virtual Dictionary getAlarmData() { Dictionary outVal = new Dictionary(); // ora aggiungo (se ci fossero) gli allarmi... if (alarmMaps != null && alarmMaps.Count > 0) { if (hasAlarms()) { string bankVal = ""; foreach (var item in alarmMaps) { bankVal = ""; var currState = currAlarmsState(item); // calcolo vettore stringa degli allarmi... bankVal = getAlarmState(item, currState); bankVal = string.IsNullOrEmpty(bankVal) ? "OK" : bankVal; saveAlarmString(ref outVal, bankVal, item.description); } } } if (periodicLog || outVal.Count > 0) { lgDebug($"Esito getAlarmData: {outVal.Count} allarmi attivi/validi in outVal"); } return outVal; } /// /// Effettua conversione da valore bitmap ai valori configurati x allarme /// /// Configurazione banco allarmi /// BitState allarmi /// public string getAlarmState(BaseAlarmConf memConf, byte[] bState) { string valTransl = ""; List descAttivi = new List(); BitArray bitAttivi = new BitArray(bState); bool[] bitStati = new bool[bitAttivi.Count]; bitAttivi.CopyTo(bitStati, 0); // cerco bit attivi int idx = 0; foreach (var item in bitStati) { if (item && memConf.messages.Count > idx) { string currState = memConf.messages[idx]; descAttivi.Add(currState); } idx++; } // combino string valTransl = string.Join(",", descAttivi); return valTransl; } /// /// 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) { processCustomTaskLF(); processOtherCounters(); processProgram(); // verifico se devo gestire cambio ODL in modo automatico processAutoOdl(); // verifico se devo gestire auto generazione dossier quotidiana processAutoDossier(); // effettua gestione import file se configurato... processFileImport(); // effettua process ritorno ricette processRecipeFileRet(); } else if (ciclo == gatherCycle.VLF) { if (utils.CRB("enableContapezzi")) { // rilettura contapezzi da server... lgTrace("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")) { try { logTimeResults(); } catch { } } processRecipeSyncArch(); if (enableSlowData) { processSlowDataRead(); } } // 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) { lgDebug("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 { // anche se NON connesso alcuni task di bassa freq li eseguo... if (ciclo == gatherCycle.LF) { // verifico se devo gestire cambio ODL in modo automatico processAutoOdl(); // verifico se devo gestire auto generazione dossier quotidiana processAutoDossier(); // effettua gestione import file se configurato... processFileImport(); // effettua process ritorno ricette processRecipeFileRet(); } else if (ciclo == gatherCycle.VLF) { processRecipeSyncArch(); } // provo a riconnettere SE abilitato tryRestart... if (adpTryRestart && !connectionOk) { // controllo se sia scaduto periodi di veto al tryConnect... int waitRecMSec = utils.CRI("waitRecMSec") * 2; // cerco se ci sia un valore in ovverride x il singolo IOB... if (cIobConf.waitRecMSec > 0) { waitRecMSec = cIobConf.waitRecMSec; } 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(); processMemoryDiscon(); } raiseRefresh(currDispData); } /// /// Effettua task òettura a bassa velocità /// /// protected virtual bool processSlowDataRead() { bool answ = false; return answ; } /// /// Effettua conversione da valore bitmap ai valori configurati /// /// /// /// public string getBitmapState(dataConfTSVC memConf, byte[] bState) { string valTransl = ""; List descAttivi = new List(); BitArray bitAttivi = new BitArray(bState); bool[] bitStati = new bool[bitAttivi.Count]; bitAttivi.CopyTo(bitStati, 0); // cerco bit attivi int idx = 0; foreach (var item in bitStati) { if (item && memConf.decodeMap.Count > idx) { string currState = memConf.decodeMap[idx]; descAttivi.Add(currState); } idx++; } // combino string valTransl = string.Join(",", descAttivi); return valTransl; } /// /// 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 nei KVP (JSON) e lo restituisce /// /// /// public string getOptJsonKVP(string key) { string answ = ""; // controllo SE HO il parametro if (memMap.optKVP != null && memMap.optKVP.Count > 0) { if (memMap.optKVP.ContainsKey(key)) { answ = memMap.optKVP[key]; } } return answ; } /// /// Cerca se esiste il parametro opzionale e lo restituisce /// /// /// public string getOptPar(string key) { string answ = ""; if (cIobConf.optPar.Count > 0) { // controllo SE HO il parametro if (cIobConf.optPar.ContainsKey(key)) { answ = cIobConf.optPar[key]; } } return answ; } /// /// 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) { string[] valori; int counter = 0; DateTime dtEve = DateTime.Now; switch (tipoUrl) { case urlType.FLog: flogData currFlData = new flogData(); flogJsonPayload fullFlObj = new flogJsonPayload(); fullFlObj.fluxData = new List(); // inizio processando ogni valore foreach (var item in elencoValori) { valori = qDecodeIN(item); CultureInfo provider = CultureInfo.InvariantCulture; DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); int.TryParse(valori[3], out counter); currFlData = new flogData() { flux = valori[1], valore = valori[2], dtEve = dtEve, dtCurr = DateTime.Now, cnt = counter }; fullFlObj.fluxData.Add(currFlData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullFlObj); } catch (Exception exc) { lgError($"FLog Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } break; case urlType.SignIN: evData currSigData = new evData(); evJsonPayload fullEvObj = new evJsonPayload(); fullEvObj.eventList = new List(); // inizio processando ogni valore foreach (var item in elencoValori) { valori = qDecodeIN(item); CultureInfo provider = CultureInfo.InvariantCulture; DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); int.TryParse(valori[2], out counter); currSigData = new evData() { valore = valori[1], dtEve = dtEve, dtCurr = DateTime.Now, cnt = counter }; fullEvObj.eventList.Add(currSigData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullEvObj); } catch (Exception exc) { lgError($"SignIN Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } break; case urlType.RawTransf: BaseRawTransf currRTData = new BaseRawTransf(); #if false rawTransfJsonPayload fullRTObj = new rawTransfJsonPayload(); fullRTObj.rawTransfData = new List(); // inizio processando ogni valore foreach (var item in elencoValori) { try { currRTData = JsonConvert.DeserializeObject(item); } catch (Exception exc) { lgError($"Eccezione in deserializzazione BaseRawTransf:{Environment.NewLine}{exc}"); } fullRTObj.rawTransfData.Add(currRTData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullRTObj); } catch (Exception exc) { lgError($"RawTransf Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } #endif // provo una serializzazione "brutale", ovvero aggiungo alla stringa il // valore di testa... string rawResult = ""; foreach (var item in elencoValori) { rawResult += $"{item},"; } if (rawResult.Length > 0) { rawResult = rawResult.Substring(0, rawResult.Length - 1); } answ = $"[{rawResult}]"; //answ = "{" + $"\"rawTransfData\":[{rawResult}]" + "}"; break; case urlType.ULog: int numVal = 0; int matrOp = 0; ulogData currUlData = new ulogData(); ulogJsonPayload fullUlObj = new ulogJsonPayload(); fullUlObj.fluxData = new List(); // inizio processando ogni valore foreach (var item in elencoValori) { valori = qDecodeIN(item); CultureInfo provider = CultureInfo.InvariantCulture; DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); int.TryParse(valori[3], out matrOp); int.TryParse(valori[5], out numVal); int.TryParse(valori[6], out counter); currUlData = new ulogData() { flux = valori[1], valore = valori[2], dtEve = dtEve, dtCurr = DateTime.Now, cnt = counter, matrOpr = matrOp, label = valori[4], valNum = numVal }; fullUlObj.fluxData.Add(currUlData); } // conversione finale try { answ = JsonConvert.SerializeObject(fullUlObj); } catch (Exception exc) { lgError($"ULog Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); } break; default: break; } } 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 Dossier quotidiani automatica /// public void processAutoDossier() { bool fatto = false; string callResp = ""; if (!string.IsNullOrEmpty(getOptPar("AUTO_SNAPSHOT_DOSSIER"))) { lgTrace("Richiesta processAutoDossier"); string IOB_MULTI_CNAME = ""; string[] elencoMulti = null; 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_SNAPSHOT_DOSSIER"), out doProc); if (doProc) { lgTrace("AUTO_SNAPSHOT_DOSSIER abilitato"); // chiamo stored x creare Snapshot Dossier giornalieri fino alla data... callResp = utils.callUrl(urlFixDailyDossier); fatto = callResp == "OK"; lgTrace($"Esecuzione processAutoDossier completata --> esito: {callResp}"); } else { lgTrace("AUTO_SNAPSHOT_DOSSIER DISABILITATO"); } } // loggo se fatto if (fatto) { lgTrace($"Effettuato processAutoDossier, esito positivo | {DateTime.Now:HH.mm.ss}"); } } /// /// Abilitazione gestione slow data /// protected bool enableSlowData = false; /// /// Verifica e processing x gestione ODL automatica /// public void processAutoOdl() { bool fatto = false; if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL"))) { lgTrace("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) { lgTrace("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 * */ lgTrace("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 == "DAILY") { // chiamo stored x creare ODL giornalieri alla data... string autoOdlRes = utils.callUrl(urlFixDailyOdl); } else if (CHANGE_ODL_MODE == "TIME" || CHANGE_ODL_MODE == "SIMUL") { // 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; } } } // se NON fosse scaduto MA è simulato esegue controllo sul num pezzi da // fare, se sfora (RANDOM) > +(50...110)% --> cambia! if (!callChangeODL && CHANGE_ODL_MODE == "SIMUL") { var rawCount = callUrl(urlGetNumPzCurrODL, false); if (int.TryParse(rawCount, out var numPzReqOdl)) { int limitQty = (numPzReqOdl * rndGen.Next(150, 210)) / 100; callChangeODL = contapezziPLC > limitQty; } } } } // vero processing... if (callChangeODL) { lgTrace("Chiamata: processAutoOdl --> forceSplitODL"); fatto = forceSplitOdl(); pzCountResetted = false; lgInfo("Esecuzione processAutoOdl completata --> pzCountResetted = false"); } } else { lgTrace("AUTO_CHANGE_ODL DISABILITATO"); } } // loggo se okReport if (fatto) { lgTrace("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() { } /// /// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente /// ogni 5 sec se base timer 10ms, vedere app.config) /// public virtual void processCustomTaskLF() { } /// /// 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) { // verifico se ho fattore demoltiplica... if (demFactDynData > 1) { lgTrace($"Non eseguo processDynData: fatt veto {demFactDynData}"); // riduco... demFactDynData--; } else { lgInfo("Inizio processDynData"); // sistemo il valore di demoltiplica a default fixDemFactDynData(); // proseguo if (connectionOk) { currDynData = getDynData(); var currAlarmData = getAlarmData(); // se ho allarmi if (currAlarmData != null && currAlarmData.Count > 0) { foreach (var item in currAlarmData) { if (!currDynData.ContainsKey(item.Key)) { // aggiungo! currDynData.Add(item.Key, item.Value); } } } 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"); } } else { lgError("Errore connessione mancante x getDynData"); } } } } /// /// 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 valori BYTE IN/PREV/OUT... così invio macchina spenta... B_input = 0; B_output = 0; B_previous = -1; 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(); // eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)... if (!cIobConf.disableExeTask) { 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, string forceMach = "") { // legge da IO server ULTIMO valore CONTPEZZI al riavvio... string currServerCount = ""; string lastIdxODL = ""; string calcUrl = ""; if (checkServerAlive) { // leggo PRIMA ODL .... calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetCurrODL : urlGetCurrODL.Replace(cIobConf.codIOB, forceMach); lastIdxODL = utils.callUrl(calcUrl); lgTrace($"Lettura ODL dall'url {calcUrl} --> {lastIdxODL}"); // se ho valori in coda da trasmettere uso dati REDIS if (forceCountRec) { // uso dati da TCiclo registrati... calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCountRec : urlGetPzCountRec.Replace(cIobConf.codIOB, forceMach); currServerCount = utils.callUrl(calcUrl); lgInfo($"Lettura contapezzi da TCiclo dall'url {calcUrl} --> num pz: {currServerCount}"); } else { // uso il contapezzi dichiarato dall'IOB stesso calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCount : urlGetPzCount.Replace(cIobConf.codIOB, forceMach); currServerCount = utils.callUrl(calcUrl); lgInfo($"Lettura contapezzi dall'url {calcUrl} --> {currServerCount}"); } // controllo: SE NON HO ODL... if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0") { // NON AGGIORNO contapezziIOB = contapezziPLC; lgError($"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; lgError($"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); lgInfo($"ODL | currIdxODL: {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#flusso#valore#cont Flusso datiValore da salvare /// public string qEncodeFLog(string flusso, string valore) { string answ = ""; try { answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterULog}"; } catch { } return answ; } /// /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato /// dtEve#flux#valReq#cont DataOra evento /// registratoFlusso datiValore da salvare /// public string qEncodeFLog(DateTime eventDT, string flusso, string valore) { string answ = ""; try { answ = $"{eventDT:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterFLog}"; } catch { } return answ; } /// /// Fornisce il valore di UserLog e valore in formato valido x messa in coda nel formato: /// dtEve#flusso#valReq#cont#matrOpr#label#valNum Flusso dati /// (RC/RS/DI)Valore da inviare /// (valStringMatricola operatoreValore etichetta: causale scarto / tagCodeValore numerico: esitoOk (0/1) / nuo scarti /// public string qEncodeULog(string flusso, string valore, int matrOpr, string label, int valNum) { string answ = ""; try { answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{matrOpr}#{label}#{valNum}#{counterULog}"; } 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) { if (!cIobConf.disableStateCh) { readSemafori(ref currDispData); } else { // forzo lettura OK currDispData.semIn = Semaforo.SV; } } 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; } /// /// Effettua salvataggio in LUT del valore ricevuto (valori string) /// - non serve calcolo medie o altro /// - accoda in out val e basta /// /// Array eventi da popolare /// valore da salvare /// ID/chiave di riferimento /// public virtual void saveAlarmString(ref Dictionary outVal, string valore, string chiave) { DateTime adesso = DateTime.Now; //check obj preliminare if (outVal == null) { outVal = new Dictionary(); } // default invio: blindato a 120 sec SE non trova conf x fluxLogResendPeriod int vetoSendAlarms = 120; if (fluxLogReduce) { vetoSendAlarms = 60 * fluxLogResendPeriod; } // verifico se sia scaduto OVVERO variato rispetto a prima oppure oltre tempo bool scaduto = !LastTSSSend.ContainsKey(chiave) || (LastTSSSend[chiave].AddSeconds(vetoSendAlarms) < adesso); bool cambiato = !LastTSS.ContainsKey(chiave) || !LastTSS[chiave].Equals(valore); if (scaduto || cambiato) { outVal.Add(chiave, valore); LastTSS[chiave] = valore; LastTSSSend[chiave] = adesso; } } /// /// 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) { upsertKey(item.Key, item.Value); } /// /// Effettua salvataggio in LUT del valore ricevuto (valori numerici) /// /// /// /// /// public virtual void saveValue(ref Dictionary outVal, string chiave, double valore) { //check obj preliminare if (outVal == null) { outVal = new Dictionary(); } bool scaduto = stackVal_TSVC(chiave, valore); // recupero VC valore = getVal_TSVC(chiave, scaduto); // 2023.11.15: gestione deduplica (opzionale) fluxlog... in caso invalida scaduto... if (fluxLogReduce && scaduto) { /*------------------------------- * logica processing: * - confronto con valore precedente (se non c'è allora registro questo) * - se variato OLTRE deadband --> nulla cambia * - se NON variato x deadband --> accodo SOLO SE scaduto tempo resend * ------------------------------ */ DateTime adesso = DateTime.Now; // cerco tra i veto... if (fluxLogReduceVeto.ContainsKey(chiave)) { // per prima cosa verifico che sia ancora valido il veto... if (adesso < fluxLogReduceVeto[chiave]) { // verifico valore precedente + deadband x decidere se sia davvero scaduto if (fluxLogReduceLast.ContainsKey(chiave)) { scaduto = Math.Abs(fluxLogReduceLast[chiave] - valore) > fluxLogRedDeadBand; } } // altrimenti creo un nuovo veto lasciando inalterata la scadenza... else { fluxLogReduceVeto[chiave] = adesso.AddMinutes(fluxLogResendPeriod); } } // se non ci fosse metto veto con periodo std... else { fluxLogReduceVeto.Add(chiave, adesso.AddMinutes(fluxLogResendPeriod)); if (fluxLogReduceLast.ContainsKey(chiave)) { fluxLogReduceLast[chiave] = valore; } else { fluxLogReduceLast.Add(chiave, valore); } } } if (scaduto) { // limite a 3 digit x valore float outVal.Add(chiave, $"{valore:N3}"); } LastTSVC[chiave] = valore; } /// /// Effettua salvataggio in LUT del valore ricevuto (valori string) /// - non serve calcolo medie o altro /// - accoda in out val e basta /// /// Array eventi da popolare /// ID/chiave di riferimento /// valore da salvare /// public virtual void saveValueString(ref Dictionary outVal, string chiave, string valore) { DateTime adesso = DateTime.Now; //check obj preliminare if (outVal == null) { outVal = new Dictionary(); } int maxVetoSeconds = 60; // cerco VC... if (TSVC_Data.ContainsKey(chiave)) { maxVetoSeconds = TSVC_Data[chiave].Period; } // se ho attivo il veto invio fluxLogReduce metto periodo a minuti indicati... if (fluxLogReduce) { maxVetoSeconds = fluxLogResendPeriod * 60; } // verifico se sia scaduto OVVERO variato rispetto a prima oppure oltre tempo bool scaduto = !LastTSSSend.ContainsKey(chiave) || (LastTSSSend[chiave].AddSeconds(maxVetoSeconds) < adesso); bool cambiato = !LastTSS.ContainsKey(chiave) || !LastTSS[chiave].Equals(valore); if (scaduto || cambiato) { outVal.Add(chiave, valore); LastTSS[chiave] = valore; LastTSSSend[chiave] = adesso; } } /// /// 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, recipeRows); string resp = utils.callUrlAsync(lastUrl, rawData); if (!string.IsNullOrEmpty(resp)) { if (resp.ToUpper() == "OK") { // segnalo okReport fatto = true; } } else { lgError($"Errore in invio richiesta registrazione allarme | resp: {resp} | URL: {lastUrl}{Environment.NewLine}Payload:{Environment.NewLine}{rawData}"); } } return fatto; } /// /// Invia una LISTA di valori /// /// /// public bool sendDataBlock(urlType tipoUrl, List listQueueVal, bool force = false) { bool fatto = false; // 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 || false) { // chiamo URL! string answ = callUrlWithPayload(lastUrl, payload, doAsync); // attesa opzionale se configurata // loggo! lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}"); // se "OK" verde, altrimenti errore --> ROSSO if (answ.Contains("OK")) { fatto = true; 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); return fatto; } /// /// 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! lgDebug(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 timeout ({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 { lgError(string.Format("[SERVER KO] {0}", queueVal)); } } else { currDispData.semOut = Semaforo.SV; // loggo! lgDebug(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 delle info public virtual void startAdapter(bool resetQueue) { DateTime adesso = DateTime.Now; DateTime scaduto = adesso.AddMinutes(-10); lgInfo("Starting adapter..."); maxJsonData = utils.CRI("maxJsonData"); maxJsonDataEv = utils.CRI("maxJsonDataEv"); parentForm.commPlcActive = false; adpRunning = true; dtAvvioAdp = adesso; lastWatchDog = scaduto; lastPING = scaduto; lastReadPLC = scaduto; lastDisconnCheck = scaduto; TimingData.resetData(); // aggiungo altri defaults setDefaults(resetQueue); adpTryRestart = true; // sistemo altri check avvio queueInEnabCurr = false; dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn); lgInfoStartup($"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"); lgDebug("startAdapter completed"); } /// /// 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 delle info public virtual void stopAdapter(bool tryRestart, bool forceDequeue) { // controllo che non ci sia redis queue altrimenti NON forza svuotamento... if (forceDequeue && !cIobConf.EnableRedisQueue) { // svuoto le code dei valori letti e non ancora trasmessi... parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda segnali...", true); while (QueueIN.Count > 0) { // INVIO COMUNQUE...!!! string valore = ""; QueueIN.TryDequeue(out valore); sendToMoonPro(urlType.SignIN, valore); } parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda FluxLOG...", true); // 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 DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue); //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); } } // svuoto coda ULog while (QueueULog.Count > 0) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta if (QueueULog.Count > maxJsonData) { string currVal = ""; // prendoi primi maxJsonDataValori for (int i = 0; i < maxJsonData; i++) { QueueULog.TryDequeue(out currVal); listaValori.Add(currVal); } sendDataBlock(urlType.ULog, listaValori); } else { // invio in blocco listaValori = QueueULog.ToList(); // invio sendDataBlock(urlType.ULog, listaValori); // svuoto! QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue); //QueueULog = new ConcurrentQueue(); } } } parentForm.displayTaskAndLog("[STOP] Stopping adapter...", true); adpTryRestart = false; parentForm.displayTaskAndLog("[STOP] Stopping adapter - last periodic data read...", true); // chiudo la connessione all'adapter... tryDisconnect(); dtStopAdp = DateTime.Now; adpTryRestart = tryRestart; adpRunning = false; // chiudo! parentForm.displayTaskAndLog("Adapter Stopped.", true); DateTime adesso = DateTime.Now; lastReadPLC = adesso; lastPING = adesso; 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 DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue); //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; queueInEnabCurr = true; } /// /// Metodo base disconnessione... /// public virtual void tryDisconnect() { queueInEnabCurr = false; } /// /// 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(string forceMachName) { // 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}"; if (!string.IsNullOrEmpty(forceMachName)) { lastUrl = lastUrl.Replace(cIobConf.codIOB, forceMachName); } 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 fullUrl = $"{urlSetPzCount}{contapezziIOB}"; if (!string.IsNullOrEmpty(forceMachName)) { fullUrl = fullUrl.Replace(cIobConf.codIOB, forceMachName); } string retVal = utils.callUrl(fullUrl); // verifica se tutto OK if (retVal != contapezziIOB.ToString()) { // errore salvataggio contapezzi lgError($"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 { lgError("Impossibile trySendPzCountBlock: IobOnline è false"); } } else { lgError("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); } // salvo in redis... redisMan.redSaveHashDict(rKeyCurrProdData, currProdData); } /// /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi /// /// /// public string urlDataBlock(urlType tipoUrl) { // verifico la parte di link "tipoComando" string tipoComando = ""; switch (tipoUrl) { case urlType.FLog: tipoComando = cIobConf.serverData.CMDFLOG_JSON; break; case urlType.SignIN: tipoComando = cIobConf.serverData.CMDBASE_JSON; break; case urlType.RawTransf: tipoComando = cIobConf.serverData.CMDRAWTRANSF_JSON; break; case urlType.ULog: tipoComando = cIobConf.serverData.CMDULOG_JSON; break; default: break; } // 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; } /// /// URL per recupero dati ODL alla data... /// public string urlGetOdlAtDate(DateTime dtRif) { string answ = ""; try { answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getOdlAtDate/{cIobConf.codIOB}?dateRif={dtRif:yyyyMMdd}"; } catch (Exception exc) { lgError(exc, $"Errore in composizione urlGetOdlAtDate per {dtRif}{Environment.NewLine}{exc}"); } 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; } /// /// Fornisce URL di tipo UserLog /// /// valore salvato in coda nel formato dtEve#flux#valore#counter /// public string urlULog(string queueVal) { // URL base x input string answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDULOG}"; // decodifica valore! string[] valori = qDecodeIN(queueVal); // aggiungo macchina e valore... answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]); // aggiondo dataOra evento e corrente + contatore... answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]); return answ; } #endregion Public Methods #region Internal Properties internal bool disDynDataRangeCheck { get; set; } = false; #endregion Internal Properties #region Protected Fields 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 = -1; /// /// Cod grupo IOB x creazione PODL al volo /// protected string CodGruppoIob = "ND-00"; /// /// Determina se sia prevista gestione PODL (creazione/avvio/chiusura) come Soitaab /// protected bool EnabelPodlManFull = false; /// /// 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; /// /// Fattore di demoltiplicazione dei DynData x ridurre campionamento /// protected int demFactDynData = 1; /// /// Boolean x indicare contapezzi disabilitato forzatamente da IOB /// protected bool disablePzCounteByIob = false; /// /// Boolean x indicare contapezzi abilitato a livello di conf apoplicazione /// protected bool enablePzCountByApp = true; /// /// Boolean x indicare contapezzi abilitato forzatamente da IOB /// protected bool enablePzCountByIob = true; /// /// dizionario (opzionale) xz decodifica file da importare /// protected Dictionary FileDecod = new Dictionary(); /// /// indica che è richeisto forzatamente reset contapezzi /// protected bool forcePzReset = false; /// /// indica che è richeisto forzatamente reset contapezzi /// protected DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1); /// /// Determina se siano gestite le ricette /// protected bool hasRecipe = false; /// /// Determina se sia gestita riduzione dati FluxLog /// protected bool fluxLogReduce = false; /// /// DeadBand x riduzione dati FluxLog (se 0 non gestita) /// protected double fluxLogRedDeadBand = 0; /// /// Finestra in minuti x invio dati FluxLog quando invariati /// protected int fluxLogResendPeriod = 60; /// /// Dizionario dei veto send x ogni variabile quando non variata /// protected Dictionary fluxLogReduceVeto = new Dictionary(); /// /// Dizionario dei valori FluxLog ultimi verificati x veto /// protected Dictionary fluxLogReduceLast = new Dictionary(); /// /// Dizionario dei valori FluxLog ultimi tipo STRING verificati x veto /// protected Dictionary fluxLogReduceLastString = new Dictionary(); /// /// Array dei contatori x segnali blinking /// protected int[] i_counters; /// /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) /// protected bool inSetup = false; /// /// ultimo tentativo connessione... /// protected DateTime lastConnectTry; /// /// Dizionario ULTIMI valori impostati x produzione /// protected Dictionary lastProdData = new Dictionary(); /// /// Ultimo invio contapezzi (x invio delayed) /// protected DateTime lastPzCountSend; /// /// Dizionario ultimi valori (string) delle TSS /// protected Dictionary LastTSS = new Dictionary(); /// /// Dizionario ultimi valori (string) delle TSS /// protected Dictionary LastTSSSend = new Dictionary(); /// /// 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"); /// /// Soglia massima errori priam di disconnesisone automatica in check /// protected int maxErroriCheck = utils.CRI("maxErroriCheck"); /// /// Quantità massima per singola ricetta (per determinare num ricette da inviare) /// protected int maxQtyPerFile = 0; /// /// 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"); /// /// Numero massimo di tentativi x test ping preliminare /// protected int maxTryPing = 1; /// /// indica se serva refresh parametri e quindi PLC... /// protected bool needRefresh = true; /// /// Variabile numero errori vari (in lettura) --> se supera soglia maxErroriCheck --> disconnette /// protected int numErroriCheck = 0; /// /// 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(); /// /// Indica se usare archivio locale ricette vs scarico http/REST /// protected bool useLocalRecipe = true; /// /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a /// scadenza periodo...) /// protected Dictionary VarArray = new Dictionary(); /// /// Durata in secondi del divieto accodamento segnali IN alla fase di startup /// protected int vetoQueueIn = 1; /// /// Periodo wathdog di default (2 sec se non specificato) /// protected int watchDogPeriod = 2; #endregion Protected Fields #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 keyReq richiesta attiva /// - gestito tramite Redis /// - a scadenza 25h /// - incrementato ogni invio di auto ODL /// protected int countKeyRichiesta { get { // calcolo keyReq odierna int answ = redisMan.getKReqCount(dailyKey); return answ; } set { // calcolo keyReq odierna redisMan.setKReqCount(dailyKey, value); } } protected string dailyKey { get { return DateTime.Today.ToString("yyMMdd"); } } /// /// Folder in cui salvare i file importati (es excel) /// protected string fileArchiveFolder { get; set; } = ""; /// /// Folder da cui importare i file (es excel) /// protected string fileImportFolder { get; set; } = ""; /// /// Folder in cui è presente il tool import excel + file json di conf /// protected string exclToolDirPath { get; set; } = ""; /// /// Tipologia di files da importare (default excel) /// protected string fileImportType { get; set; } = "*.xslx"; /// /// Dati fluxLog serializzati in redis /// protected List fluxLogData { get { List answ = new List(); string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog"); var rawData = redisMan.getRSV(redKeyFLog); if (!string.IsNullOrEmpty(rawData)) { answ = JsonConvert.DeserializeObject>(rawData); } return answ; } set { string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog"); string fluxLogRaw = JsonConvert.SerializeObject(value); redisMan.setRSV(redKeyFLog, fluxLogRaw); } } /// /// 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; /// /// Variabile isRunning (NON realtime) /// protected bool isRunningState { get { bool answ = false; string cStatus = redisMan.getRSV(redKeyProdRunState); bool.TryParse(cStatus, out answ); return answ; } set { redisMan.setRSV(redKeyProdRunState, $"{value}"); } } protected string lastArtDescr { get => redisMan.getRSV(redKeyProdLastArt); set => redisMan.setRSV(redKeyProdLastArt, value); } protected List ListaArticoli { get; set; } = new List(); protected List ListaJobs { get; set; } = new List(); /// /// 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"); /// /// Coda massima ammessa per FLog (se ‹= 0 disattivata...) /// protected int maxQueueRawTransf { get; set; } = utils.CRI("maxQueueRawTransf"); /// /// 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; } /// /// Dizionario condizioni di check BIT da usare x valori controllo (es ModBus TCP Imax) /// protected Dictionary OptCheckCondBit { get; set; } = new Dictionary(); /// /// Dizionario condizioni di check INT da usare x valori controllo bitmap (es ModBus TCP Zetapack) /// protected Dictionary OptCheckCondInt { get; set; } = new Dictionary(); /// /// Elenco di valori da tradurre da valori BIT (es ModBus TCP FIMAT) /// NB: potrebbero essere contemporaneamente attivi + valori e + traduzioni /// protected Dictionary OptVar2TranslBit { get; set; } = new Dictionary(); /// /// Elenco di valori da tradurre da valori INT esclusivi (es ModBus TCP FIMAT) /// protected Dictionary OptVar2TranslInt { get; set; } = new Dictionary(); /// /// Elenco dei path utili x processing /// protected Dictionary pathList { get; set; } = new Dictionary(); /// /// Gestione archivio serializzato PODL inviati (tramite file temp locale/REDIS) /// protected Dictionary POdlSentFileArch { get { Dictionary answ = new Dictionary(); string redKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:POdlSent"); string rawData = redisMan.getRSV(redKey); if (!string.IsNullOrEmpty(rawData)) { try { answ = JsonConvert.DeserializeObject>(rawData); lgInfo($"Rilettura status POdlSentFileArch: trovati {answ.Count} record"); } catch (Exception exc) { lgError($"Errore in deserializzazione POdlSentFileArch{Environment.NewLine}{exc}"); } } return answ; } set { string rawVal = JsonConvert.SerializeObject(value); string redKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:POdlSent"); redisMan.setRSV(redKey, rawVal); lgDebug($"Salvataggio status POdlSentFileArch | {value.Count} record"); } } /// /// Elenco corrente (in memory) PODL inviati all'impianto /// chiave: idxPODL /// valore: record PODL /// protected Dictionary POdlSentList { get; set; } = new Dictionary(); /// /// 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#valReq#cont /// protected string qEncodeIN { get { string answ = ""; try { answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN); } catch { } return answ; } } protected bool queueInEnabCurr { get => qInEnabCurr; set { qInEnabCurr = value; lgInfo($"SET queueInEnabCurr: {value} | {DateTime.Now:HHmmss}"); } } /// /// Definizioni x replace in file ricette /// protected Dictionary RecipeReplRules { get; set; } = new Dictionary(); protected string redKeyLogfileAct { get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act"); } protected string redKeyLogfileLast { get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Last"); } protected string redKeyProdLastArt { get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:prodDecod:lastArtDesc"); } protected string redKeyProdRunState { get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:prodDecod:isRunningState"); } protected Random rndGen { get; set; } = new Random(); /// /// Indica se vada inviata la keyReq 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.cncPingAddr, 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 Protected Methods /// /// Aggiunge o aggiorna kvp ad un Dict /// /// /// /// protected void DictUpsert(ref Dictionary currDict, string newKey, string newVal) { if (currDict.ContainsKey(newKey)) { currDict[newKey] = newVal; } else { currDict.Add(newKey, newVal); } } /// /// 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 + '#' + valReq + '#' + cont /// protected static string[] qDecodeIN(string queueVal) { string[] answ = null; if (!string.IsNullOrEmpty(queueVal)) { try { answ = queueVal.Split('#'); } catch { } } return answ; } /// /// Classe di base implementazione traduzione di una LUT da memoria come valore BIT a valore /// esplicito x FLog /// protected virtual void checkTranslateBit() { // verifico se devo processare decodifica di qualche valore... if (OptVar2TranslBit != null && OptVar2TranslBit.Count > 0) { } } /// /// Classe di base implementazione traduzione di una LUT da memoria come valore INT a valore /// esplicito x FLog /// protected virtual void checkTranslateInt() { if (OptVar2TranslInt != null && OptVar2TranslInt.Count > 0) { } } /// /// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile /// protected void checkVetoQueueIn() { queueInEnabCurr = dtVetoQueueIN < DateTime.Now; } /// /// Conversione string row in log generico /// /// /// protected virtual GenLogRow convertToMachineLog(string dailyLog) { GenLogRow answ = new GenLogRow(); if (!string.IsNullOrEmpty(dailyLog)) { // preventivamente: doppio ";;" --> ";" dailyLog = dailyLog.Replace(";;", ";"); var sSplit = dailyLog.Split(';'); answ.dtRif = DateTime.ParseExact($"{sSplit[0]} {sSplit[1]}", "yyyy-M-d HH:mm:ss", null); answ.valString = $"{sSplit[2]};{sSplit[3]}"; } return answ; } /// /// Restituisce stato allarmi in formato byte[] /// /// /// protected byte[] currAlarmsState(BaseAlarmConf item) { byte[] answ = new byte[item.size]; int currStatus = 0; int[] listInt = new int[2]; // banchi in array Int16 --> scompongo for (int i = 0; i < item.size / 2; i++) { currStatus = getAlarmStatus(item); // aggiornamento blink counters dato nuovo valore item.checkBlinkCounter(i, (uint)currStatus); // calcolo indice errori if ((uint)(item.alarmsMask[i] & currStatus) > 0) { var currByte = BitConverter.GetBytes(currStatus); // salvo nell'array di byte Buffer.BlockCopy(currByte, 0, answ, i * 2, 2); } } return answ; } /// /// Recupera ODL attivo su impianto /// protected int CurrOdl() { int answ = 0; string sCurrODL = utils.callUrl(urlGetCurrODL); int.TryParse(sCurrODL, out answ); return answ; } /// /// Imposta eventuali altri valori default /// protected 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; } string s_maxErrCheck = getOptPar("MAX_ERR_CHECK"); if (!string.IsNullOrEmpty(s_maxErrCheck)) { int numErr = 50; int.TryParse(s_maxErrCheck, out numErr); maxErroriCheck = numErr; } string s_watchDogPer = getOptPar("WATCHDOG_PERIOD"); if (!string.IsNullOrEmpty(s_watchDogPer)) { int numPer = 3; int.TryParse(s_watchDogPer, out numPer); watchDogPeriod = numPer; } // sistemo conf folder acquisizione files fileImportFolder = getOptPar("FILE_IMPORT_FOLDER"); fileImportType = getOptPar("FILE_IMPORT_TYPE"); fileArchiveFolder = getOptPar("FILE_ARCHIVE_FOLDER"); // fisso tool dir exclToolDirPath = getOptPar("EXCL_TOOL_PATH"); } /// /// Implementazione di riferimento della verifica stato allarmi /// /// /// protected virtual int getAlarmStatus(BaseAlarmConf item) { int answ = 0; return answ; } /// /// Implementazione di riferimento della verifica stato allarmi formato UInt /// /// /// protected virtual uint getAlarmStatusUInt(BaseAlarmConf item) { uint answ = 0; return answ; } /// /// Recupera valore da dizionario CurrProdData o restituisce val default /// /// Chiave richiesta /// Valore di default /// protected string getCurrProdData(string key, string defVal) { string answ = ""; if (currProdData.ContainsKey(key)) { answ = string.IsNullOrEmpty(currProdData[key]) ? defVal : currProdData[key]; } return answ; } /// /// Fornisce formato valido x messa in coda nel formato dtEve#valReq#cont /// protected string getEncodSigLog(DateTime DtEvent, int val2log, int counter) { string answ = ""; try { answ = $"{DtEvent:yyyyMMddHHmmssfff}#{val2log:X2}#{counter}"; } catch { } return answ; } /// /// Recupero dati da FluxLog /// /// /// /// protected string getFluxVal(DossierFluxLogDTO resultSet, string codFlux) { string answ = ""; var searchRec = resultSet.ODL.Where(x => x.CodFlux == codFlux).FirstOrDefault(); if (searchRec != null) { answ = !string.IsNullOrEmpty(searchRec.ValoreEdit) ? searchRec.ValoreEdit : searchRec.Valore; } return answ; } /// /// Recupero dati da FluxLog formato double /// /// /// /// protected double getFluxValDouble(DossierFluxLogDTO resultSet, string codFlux) { double answ = 0; var style = NumberStyles.AllowDecimalPoint; var culture = CultureInfo.InvariantCulture; string rawVal = getFluxVal(resultSet, codFlux); if (rawVal.Contains(",")) { rawVal = rawVal.Replace(",", "."); } if (!string.IsNullOrEmpty(rawVal)) { double.TryParse(rawVal, style, culture, out answ); } return answ; } /// /// Recupero dati da FluxLog formato INT /// /// /// /// protected int getFluxValInt(DossierFluxLogDTO resultSet, string codFlux) { int answ = 0; double dVal = 0; var style = NumberStyles.AllowDecimalPoint; var culture = CultureInfo.InvariantCulture; string rawVal = getFluxVal(resultSet, codFlux); if (rawVal.Contains(",")) { rawVal = rawVal.Replace(",", "."); } if (!string.IsNullOrEmpty(rawVal)) { double.TryParse(rawVal, style, culture, out dVal); answ = (int)dVal; } return answ; } /// /// Converte un file di log della macchina in un dizionario /// /// /// protected List getGenLogFromMachineLog(string dailyLog) { List result = new List(); //se ho valori.. faccio split! if (!string.IsNullOrEmpty(dailyLog)) { var rowList = dailyLog.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); if (rowList != null && rowList.Length > 0) { result = rowList .Where(x => !string.IsNullOrEmpty(x)) .Select(x => convertToMachineLog(x)) .ToList(); } } return result; } /// /// Effettua traduzione da tabella listValue per chiave /// /// /// /// protected string getLV(Dictionary currDict, string key) { string answ = currDict.ContainsKey(key) ? currDict[key] : key; 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 == "[]") { answ = utils.callUrlNow(url2call); } return answ; } /// /// Recupera file log da analizzare /// /// protected virtual bool getRemoteLog() { bool fatto = false; return fatto; } /// /// 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; } /// /// Verifica presenza eventuali allarmi /// /// protected virtual bool hasAlarms() { bool answ = false; int numErrors = 0; int currStatus = 0; ushort full16 = 65535; int[] listInt = new int[2]; if (alarmMaps != null) { // leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme... foreach (var item in alarmMaps) { // banchi in array Int16 --> scompongo for (int i = 0; i < item.size / 2; i++) { currStatus = getAlarmStatus(item); // ora filtro x l'i-esimo banco a 16 bit... if (i == 0) { currStatus = (ushort)(currStatus & full16); } else { var temp = currStatus >> 16; currStatus = (ushort)temp; } // aggiornamento blink counters dato nuovo valore item.checkBlinkCounter(i, (uint)currStatus); // calcolo indice errori if ((uint)(item.alarmsMask[i] & currStatus) > 0) { numErrors++; } // verifico SE sia variato... confronto allarmi filtrato stile blink per bit status: // - allarmi che iniziano per # IGNORATI // - altri allarmi con un countdown da MAX_COUNTER_BLINK a 0 per il fronte // di discesa if (item.isChanged(i, (uint)currStatus)) { // registro gli allarmi attivi e trasmetto... if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], (uint)(item.alarmsMask[i] & currStatus), item.messages)) { // se inviato --> salvo stato da current... item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus)); } } } } } answ = numErrors > 0; return answ; } /// /// Recupera da server set di dati specifici x IOB /// /// protected virtual bool iobGetDataFromServer() { return false; } /// /// Effettua upload verso server FTP della macchina dei files nella folder indicata /// /// /// protected virtual bool iobSendFTP(string folderDir) { bool answ = false; //leggo CONF string ftpServ = getOptPar("FTP_SERVER"); string ftpUser = getOptPar("FTP_USER"); string ftpPass = getOptPar("FTP_PWD"); string ftpCert = getOptPar("FTP_CERT"); string doSkip = getOptPar("FTP_SKIP"); string remDir = getOptPar("FTP_REM_DIR"); string locDir = getOptPar("FTP_LOC_DIR"); // procedo SE TROVO conf... if (string.IsNullOrEmpty($"{ftpServ}{ftpUser}{ftpPass}")) { lgError($"Impossibile eseguire iobSendFTP x mancanza parametri | ftpServ: {ftpServ} | ftpUser: {ftpUser} | ftpPass: {ftpPass}"); } else { bool ftpSkip = false; bool.TryParse(doSkip, out ftpSkip); var ftpClient = new Manager(ftpServ, ftpUser, ftpPass, ftpCert, ftpSkip); var testServer = ftpClient.serverOk(); if (testServer) { lgInfo($"FTP: server found at {ftpServ}"); var srvType = ftpClient.serverType(); lgInfo($"FTP Server type: {srvType}"); var preTest = ftpClient.dirExists(remDir); if (!preTest) { var dirCreate = ftpClient.createDir(remDir); lgInfo($"FTP: created remote dir {remDir}"); } // test directory... string basePath = Application.StartupPath; string localPath = Path.Combine(basePath, locDir); lgInfo($"basePath: {basePath} | localPath: {localPath}"); var fileList = Directory.GetFiles(localPath, "*.csv"); int numfile = 0; foreach (var file in fileList) { string fName = file.Split('\\').Last(); string remName = $"{remDir}\\{fName}"; ftpClient.sendFile(file, remName); numfile++; } var dirUploaded = (numfile == fileList.Count()); if (dirUploaded) { lgInfo($"FTP: uploaded dir content {locDir} --> {remDir}"); } // se ok --> sposto invio DateTime adesso = DateTime.Now; string archBasePath = Path.Combine(basePath, "DATA", "HIST", $"{adesso:yyyy}"); baseUtils.checkDir(archBasePath); string archPath = Path.Combine(archBasePath, $"{adesso:MMddHHmmss}"); baseUtils.checkDir(archPath); lgInfo($"Richiesta Dir Move | {localPath} | {archPath}"); foreach (var item in fileList) { string fName = item.Split('\\').Last(); string destName = $"{archPath}\\{fName}"; File.Move(item, destName); } //Directory.Move(localPath, archBasePath); lgInfo($"FTP: Archived dir content {localPath} --> {archPath}"); } } return answ; } /// /// Prepara files CSV da inviare alla macchina /// protected virtual bool iobWriteLocalCSV() { bool answ = false; // salvo articoli string locDir = getOptPar("FTP_LOC_DIR"); string sAddHeader = getOptPar("CSV_ADD_HEADER"); bool addHeader = false; if (!string.IsNullOrEmpty(sAddHeader)) { bool.TryParse(sAddHeader, out addHeader); } string basePath = Application.StartupPath; string tempDir = Path.Combine(basePath, locDir); lgInfo($"iobWriteLocalCSV | locDir: {locDir} | sAddHeader: {sAddHeader} | tempDir: {tempDir}"); baseUtils.checkDir(tempDir); string filePath = Path.Combine(tempDir, "articoli.csv"); answ = DataExport.SaveToCsv(ListaArticoli, filePath, addHeader); if (answ) { lgInfo($"CSV: saved ART file as articoli.csv at {filePath}"); // salvo PODL string csvName = $"{DateTime.Now:dd-MM-yyyy}.csv"; filePath = Path.Combine(tempDir, $"{csvName}"); answ = DataExport.SaveToCsv(ListaJobs, filePath, addHeader); if (answ) { lgInfo($"CSV: saved PODL file {csvName} at {filePath}"); } } return answ; } /// /// Effettua traduzione ITEM da LUT parametrica (keyReq: tipo+id) del file di conf, se non /// trovo uso keyReq /// /// /// /// protected virtual string itemTranslation(string tipo, string id) { string answ = ""; if (cIobConf.itemTranslation != null) { string lemma = id; if (!string.IsNullOrEmpty(tipo)) { lemma = $"{tipo}_{id}"; } // cerco nel dizionario delle traduzioni SE esiste un valore e prendo quello, // altrimenti uso il lemma... if (cIobConf.itemTranslation.ContainsKey(lemma)) { answ = cIobConf.itemTranslation[lemma]; } else { answ = lemma; } } return answ; } /// /// Effettua traduzione ITEM da LUT parametrica (keyReq: lemma) del file di conf, se non /// trovo uso keyReq /// /// /// protected virtual string itemTranslation(string lemma) { string answ = ""; if (cIobConf.itemTranslation != null) { if (cIobConf.itemTranslation.ContainsKey(lemma)) { answ = cIobConf.itemTranslation[lemma]; } else { answ = lemma; } } return answ; } /// /// 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); 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("loadMemConf.01"); lgInfoStartup("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"); } lgInfo("loadMemConf.02"); // gruppo macchina CodGruppoIob = getOptPar("CodGruppoIob"); CodGruppoIob = string.IsNullOrEmpty(CodGruppoIob) ? "ND-00" : CodGruppoIob; // gestione completa PODL string sEnabelPodlManFull = getOptPar("EnabelPodlManFull"); if (!string.IsNullOrEmpty(sEnabelPodlManFull)) { bool.TryParse(sEnabelPodlManFull, out EnabelPodlManFull); } // abilitazione invio WDST disabilitazione controllo range valori DynData if (!string.IsNullOrEmpty(getOptPar("DISABLE_SEND_WDST"))) { bool checkDisWdst = false; bool.TryParse(getOptPar("DISABLE_SEND_WDST"), out checkDisWdst); disableWdst = checkDisWdst; } // disabilitazione controllo range valori DynData if (!string.IsNullOrEmpty(getOptPar("disDynDataRangeCheck"))) { bool checkDis = false; bool.TryParse(getOptPar("disDynDataRangeCheck"), out checkDis); disDynDataRangeCheck = checkDis; } lgInfo("loadMemConf.03"); // inizializzo LUT decodifica PARAMETRI string jsonParams = getOptPar("PARAM_CONF"); if (!string.IsNullOrEmpty(jsonParams)) { string jsonFileName = $"{Application.StartupPath}\\DATA\\CONF\\{jsonParams}"; lgInfoStartup($"Apertura file {jsonFileName}"); StreamReader reader = new StreamReader(jsonFileName); string jsonData = reader.ReadToEnd(); if (!string.IsNullOrEmpty(jsonData)) { lgInfoStartup($"File json PARAMETRI composto da {jsonData.Length} caratteri"); lgInfo("loadMemConf.04"); try { memMap = JsonConvert.DeserializeObject(jsonData); setupMemMap(); setupOptMemPar(); setupFileDecod(); setupSpecialParams(); lgInfo("loadMemConf.05"); } catch (Exception exc) { lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}"); } } else { lgError("Errore in loadMemConf: file json vuoto!"); } reader.Dispose(); } else { lgInfoStartup("loadMemConf: non trovata opzione PARAM_CONF in file INI"); } lgInfo("loadMemConf.06"); // inizializzo LUT decodifica ALLARMI string jsonAlarms = getOptPar("ALARM_CONF"); if (!string.IsNullOrEmpty(jsonAlarms)) { string jsonFileName = $"{Application.StartupPath}\\DATA\\CONF\\{jsonAlarms}"; lgInfoStartup($"Apertura file {jsonFileName}"); StreamReader reader = new StreamReader(jsonFileName); string jsonData = reader.ReadToEnd(); reader.Dispose(); if (!string.IsNullOrEmpty(jsonData)) { lgInfoStartup($"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!"); } } else { lgInfoStartup("loadMemConf: non trovata opzione ALARM_CONF in file INI"); } // loggo lgInfo("loadMemConf.07"); lgInfoStartup("DONE loadMemConf"); } /// /// Recupera elenco PODL assegnabili /// /// protected List MachineNextPodl() { List reqPOdlList = new List(); // per prima cosa recupero elenco PODL da gestire.... var rawListPODL = callUrl(urlGetCurrPODL, false); if (!string.IsNullOrEmpty(rawListPODL)) { try { reqPOdlList = JsonConvert.DeserializeObject>(rawListPODL); } catch (Exception exc) { lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}"); } } else { lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto"); } return reqPOdlList; } /// /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC /// NB: deve resettare reqValue /// /// protected virtual void plcWriteParams(ref List updatedPar) { // non faccio nulla di base... } /// /// Effettua sync dati da e verso impianto /// /// protected virtual void processDataSync() { } /// /// Effettua eventuale file import, archiviando file importati /// - es gestione file excel di Giacovelli /// - es gestione ritorno ricette FIMAT /// /// protected virtual bool processFileImport() { bool answ = false; /* ------------------------------------------ * esecuzione cablata, si potrebbe costruire una cosa più flessibile tramite conf: * Oggetto MultiStepFileImport * { * StepName: Step01 * ProcType: Excel2Json * FolderIn: xxx * FolderOut: xxx * FolderErr: xxx * },{ * StepName: Step02 * ProcType: Json2RegGiac * FolderIn: xxx * FolderOut: xxx * FolderErr: xxx * } * ------------------------------------------*/ if (!string.IsNullOrEmpty(fileImportFolder)) { // verifico esistenza folders... if (Directory.Exists(fileImportFolder)) { string errore = ""; // verifico archivio string dirArchive = Path.Combine(fileImportFolder, "archive"); baseUtils.checkDir(dirArchive); string dirConvert = Path.Combine(fileImportFolder, "converted"); baseUtils.checkDir(dirConvert); Dictionary statsColl = new Dictionary(); // cerco files da convertire var listFiles2Conv = Directory.GetFiles(fileImportFolder, fileImportType); // se li trovo --> chiamo import! if (listFiles2Conv != null && listFiles2Conv.Length > 0) { // leggo file conf di riferimento FileProcMan fpm = new FileProcMan(dirArchive, dirConvert, exclToolDirPath, "ExcImport.exe", "RefExcConv.json", "DB Loco"); int idxODL = 0; foreach (var fileItem in listFiles2Conv) { // calcolo ODL x il file... string fileName = Path.GetFileNameWithoutExtension(fileItem); DateTime dataDoc = DateTime.Today; DateTime.TryParse(fileName, out dataDoc); // cerco lotto x giornata... string sIdxODL = utils.callUrl(urlGetOdlAtDate(dataDoc)); int.TryParse(sIdxODL, out idxODL); // chiamo conversione TimeSpan timeElaps = fpm.doProcess(fileItem, idxODL); statsColl.Add($"ExcImport conversion executed for {fileItem}", timeElaps); lgTrace(($"ExcImport conversion executed for {fileItem} | {timeElaps.Milliseconds}ms")); } } // cerco i file json x invio var listFiles2Upload = Directory.GetFiles(dirConvert, "*.json"); // se li trovo --> chiamo import! if (listFiles2Upload != null && listFiles2Upload.Length > 0) { foreach (var fileItem in listFiles2Upload) { // leggo il file json Dictionary list2Send = new Dictionary(); string rawData = File.ReadAllText(fileItem); if (!string.IsNullOrEmpty(rawData)) { var convData = JsonConvert.DeserializeObject>(rawData); if (convData != null) { list2Send = convData; // ora accodo contenuto file json... accodaRawData(rawTransfType.RegGiacenze, list2Send); } } // processo invio: provo forzare send... answ = svuotaCodaRawTransf(true); if (!answ) { errore = $"Errore in fase di invio dati svuotaCodaRawTransf x {fileItem}"; } // archivio il file else { // sposto file... File.Move(fileItem, $"{dirArchive}/{Path.GetFileName(fileItem)}"); // fixme todo !!! eliminare json?!?!? } // log eventuali errori if (!answ) { string dirError = $"{fileImportFolder}/errors"; baseUtils.checkDir(dirError); // sposto file... File.Move(fileItem, $"{dirError}/{Path.GetFileName(fileItem)}"); // segno errore! string fileError = $"{fileImportFolder}/errors.log"; File.AppendAllText(fileError, errore); } } } } } return answ; } /// /// 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(); string currOut = ""; // 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; currOut = $" | Parameter {item.uid} | {item.value} --> {item.reqValue}"; // sistemo valori item.value = item.reqValue; lgInfo($"Richiesta update parametro {currOut}"); // salvo in lista da ritrasmettere updatedPar.Add(item); } else { currOut = $" | Error: parameter {item.uid} not found"; lgError($"Errore {currOut}"); } // accodo in stringa taskVal... answ += currOut; } // richiamo scrittura parametri su PLC plcWriteParams(ref updatedPar); // invio su cloud parametri! string rawData = JsonConvert.SerializeObject(updatedPar); utils.callUrl($"{urlUpdateWriteParams}", rawData); lgInfo($"Notifica a server scrittura {updatedPar.Count} parametri"); } } catch (Exception exc) { lgError($"Eccezione in processMemWriteRequests:{Environment.NewLine}{exc}"); } } else { lgError("Non è stata ricevuta risposta x task da eseguire"); } return answ; } /// /// Cerca di recuperare i file generati dall'impianto in merito al processing degli ordini /// /// /// /// Processing di dati "OtherInfo" da implementare caso x caso (qui riportato caso FIMAT ricette...) /// /// /// protected virtual bool processOtherInfo(string keyReq, string valReq) { bool answ = false; // test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT... if (hasRecipe) { string archBasePath = Path.Combine(pathList["path-locBase"], pathList["path-03-Recv"]); string reportBasePath = pathList["path-outReport"]; baseUtils.checkDir(reportBasePath); // verifico x cosa è stato richiesto attività (che esista la folder...) string folderPath = Path.Combine(archBasePath, keyReq); if (Directory.Exists(folderPath)) { // calcolo il nome zip di destinazione finale int anno = DateTime.Today.Year; int.TryParse(keyReq.Substring(0, 4), out anno); string zipDir = Path.Combine(archBasePath, $"{anno}"); baseUtils.checkDir(zipDir); string zipName = Path.Combine(zipDir, $"{keyReq}.zip"); // gestione report OUT string reportPath = Path.Combine(reportBasePath, $"{keyReq}"); // verifico il tipo di attività e procedo switch (valReq) { case "Arch": // solo archiviazione ZIP della folder... answ = doZipArchiveFolder(folderPath, zipName); // se fatto --> elimino record da REDIS (locale e remoto) if (answ) { RecipeRemoveWeekStatus(keyReq); } break; case "SendArch": // preparazione file di resoconto contumi answ = RecipeDoConsumeReport(folderPath, reportPath); if (answ) { // infine archiviazione ZIP della folder... bool okArchive = doZipArchiveFolder(folderPath, zipName); // se fatto --> elimino record da REDIS (locale e remoto) if (okArchive) { RecipeRemoveWeekStatus(keyReq); } } break; default: break; } } } return answ; } protected virtual bool processRecipeFileRet() { bool answ = false; // test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT... if (hasRecipe) { // recupera i NUOVI file e li sposta in folder locale temp string remoPath = Path.Combine(pathList["path-locBase"], pathList["path-05-remExe"]); string archBasePath = Path.Combine(pathList["path-locBase"], pathList["path-03-Recv"]); string tempPath = Path.Combine(archBasePath, "TEMP"); baseUtils.checkDir(tempPath); bool okRetrieve = RecipeTaskDoneRetrieve(remoPath, tempPath); // ora cerca nella folder locale e processa i files... bool okCheck = RecipeDoCheckFileProc(tempPath, archBasePath); // verifica se sia da creare/inviare il record settimanale di consumo (dopo la // mezzanotte del lunedì inizio settimana) bool okSent = RecipeDoProcCons(); } return answ; } /// /// Sync archivio ricette (Macchina ‹--› MES) /// /// protected virtual bool processRecipeSyncArch() { bool answ = false; // test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT... if (hasRecipe) { DateTime adesso = DateTime.Now; // verifico veto sync ricette (x non ripetere troppo spesso) if (adesso > dtVetoCheckSyncRecipe) { // effettua sync eventuali NUOVI file ricette bool okSync = RecipeDoSyncRecipe(); // imposto veto a 1h... dtVetoCheckSyncRecipe = adesso.AddHours(1); } } 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)); } } } } /// /// Registra i file in directory suddivise x anno/settimana e restitusice elenco /// /// Percorso file XML /// Percorso base archivi /// protected List RecipeDoArchiveWeek(string recipeFile, string archBasePath) { List weeksProc = new List(); // init var int week = 0; string archPath = ""; Calendar cal = new CultureInfo("it-IT").Calendar; // leggo contenuto XML string rawData = File.ReadAllText(recipeFile); // deserializza file XML x recuperare righe consumo XmlSerializer serializer = new XmlSerializer(typeof(ARecipe)); using (StringReader reader = new StringReader(rawData)) { try { var currRecipe = (ARecipe)serializer.Deserialize(reader); if (currRecipe != null) { // recupero data-ora ricetta da campo string DateTime recExeDt = DateTime.Today; DateTime.TryParse(currRecipe.DesRecipe.DesData.Date, out recExeDt); // calcolo week da data-ora file... week = cal.GetWeekOfYear(recExeDt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); // verifico se ci sia la settimana indicata in elenco... string currWeek = $"{recExeDt:yyyy}-{week:00}"; if (!weeksProc.Contains(currWeek)) { weeksProc.Add(currWeek); } // sposto file x in area week string rName = Path.GetFileName(recipeFile); archPath = Path.Combine(archBasePath, $"{recExeDt:yyyy}-{week:00}"); baseUtils.checkDir(archPath); string destPath = Path.Combine(archPath, rName); // sposto files File.Move(recipeFile, destPath); } } catch (Exception exc) { lgError($"Eccezione in RecipeDoArchiveWeek{Environment.NewLine}{exc}"); } } return weeksProc; } /// /// Verifica i file recuperati /// - PODL da inviare a MAPO /// - accumulo consumo materiali /// /// /// /// protected virtual bool RecipeDoCheckFileProc(string localPath, string archBasePath) { bool answ = false; List weekProcNew = new List(); // recupero elenco files... var fileList = Directory.GetFiles(localPath); if (fileList != null && fileList.Count() > 0) { foreach (var recipeFile in fileList) { // processo eventuale apertura/chiusura PODL bool okPOdl = RecipeDoProcessPODL(recipeFile); // processa i file archiviando x settimana var procWeeks = RecipeDoArchiveWeek(recipeFile, archBasePath); // aggiungo all'elenco settimane SE mancassero le sett inserite foreach (var item in procWeeks) { if (!weekProcNew.Contains(item)) { weekProcNew.Add(item); } } } if (weekProcNew.Count > 0) { // predispongo azioni di base ammesse Dictionary redHashWeek = new Dictionary(); Dictionary stdActList = new Dictionary(); stdActList.Add("Arch", "Archivia"); stdActList.Add("SendArch", "Invia + Archivia"); List weekHashUpdate = new List(); // aggiorna in REDIS (hashList) status delle settimane coinvolte foreach (var cWeek in weekProcNew) { string weekPath = Path.Combine(archBasePath, $"{cWeek}"); var weekFileList = Directory.GetFiles(weekPath, "*.xml"); PeriodInfo cPerInfo = new PeriodInfo() { NumFilesReceived = weekFileList.Count(), CurrStatus = "Ricevute", ActionList = stdActList }; string rawWeek = JsonConvert.SerializeObject(cPerInfo); redHashWeek.Add(cWeek, rawWeek); } // salvo in redis... string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats"); var okHashDict = redisMan.redSaveHashDict(fullKey, redHashWeek); // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; string dictPayload = JsonConvert.SerializeObject(redHashWeek); callUrlWithPayload(remUrl, dictPayload, false); } } return answ; } /// /// Verifica se si debba registrare/inviare il report periodico di consumo indicato /// /// protected bool RecipeDoProcCons() { bool answ = false; // calcolo quale sia la settimana corrente... int week = 0; DateTime adesso = DateTime.Now; Calendar cal = new CultureInfo("it-IT").Calendar; // calcolo week da data-ora file... week = cal.GetWeekOfYear(adesso, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); // verifico se ci sia la settimana indicata in elenco... string currWeek = $"{adesso:yyyy}-{week:00}"; // recupero elenco delle settimane da processare da redis/WeekStats string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats"); Dictionary currStats = redisMan.redGetHashDict(fullKey); if (currStats != null && currStats.Count > 0) { // definisco il DICT delle settimane da processare (= settimane passate, NON la corrente...) foreach (var weekData in currStats) { if (weekData.Key != currWeek) { // ...x ogni settimana PASSATA effettua SendArch... --> lancia un task! processOtherInfo(weekData.Key, "SendArch"); } } answ = true; } return answ; } /// /// Acquisisce eventuali nuove ricette /// /// protected bool RecipeDoSyncRecipe() { bool answ = false; DateTime asdesso = DateTime.Now; try { string recLocalArchPath = Path.Combine(pathList["path-locBase"], pathList["path-00-Arch"]); string recRemArchPath = pathList["path-06-remRec"]; // leggo da redis (se disponibile) elenco hast file/MD5.... string fullKey = redisMan.redHash($"IOB:Remote:{cIobConf.codIOB}:FileCheck"); Dictionary dictRecMd5 = redisMan.redGetHashDict(fullKey); // leggo la directory remota... var remoteList = Directory.GetFiles(recRemArchPath); string fileName = ""; string newFilePath = ""; bool doAcquire = false; int recNum = 0; int newNum = 0; string currMd5; foreach (var remoteFile in remoteList) { recNum = 0; doAcquire = false; // verifico MD5... currMd5 = baseUtils.GetFileMd5(remoteFile); fileName = Path.GetFileName(remoteFile); // verifo se c'è il file... if (!dictRecMd5.ContainsKey(fileName)) { dictRecMd5.Add(fileName, currMd5); doAcquire = true; } else { //// verifico SE sia modificato da meno di 30 gg... //DateTime lastMod = File.GetLastWriteTime(remoteFile); if (dictRecMd5[fileName] != currMd5) { dictRecMd5[fileName] = currMd5; doAcquire = true; } } // se devo aggiungere processo --> fix numRicetta + 10'000 if (doAcquire) { answ = true; // leggo contenuto... string recContent = File.ReadAllText(remoteFile); // calcolo nuovo nome var namePart = fileName.Split('.'); int.TryParse(namePart[0], out recNum); newNum = recNum + 10000; // modifico ricetta recContent = recContent.Replace($"{namePart[0]}", $"{newNum}"); // salvo ricetta modificata in archivio newFilePath = Path.Combine(recLocalArchPath, $"{newNum}.xml"); // elimino eventuale file precedente... if (File.Exists(newFilePath)) { File.Delete(newFilePath); } File.WriteAllText(newFilePath, recContent); } } // se ho importato qualcosa... if (answ) { // salvo hash dei files... redisMan.redSaveHashDict(fullKey, dictRecMd5); } } catch (Exception exc) { lgError($"Eccezione durante RecipeDoSyncRecipe{Environment.NewLine}{exc}"); } return answ; } /// /// restitusice i record di consumo dei materiali associati al file: /// /// Percorso file XML /// protected List RecipeGetCons(string recipeFile) { List listConsumi = new List(); // leggo contenuto XML string rawData = File.ReadAllText(recipeFile); // deserializza file XML x recuperare righe consumo XmlSerializer serializer = new XmlSerializer(typeof(ARecipe)); using (StringReader reader = new StringReader(rawData)) { try { var currRecipe = (ARecipe)serializer.Deserialize(reader); if (currRecipe != null) { if (currRecipe.ColRecipe != null && currRecipe.ColRecipe.Count > 0) { List RigheConsumi = new List(); foreach (var rigaComp in currRecipe.ColRecipe) { ColData datiComp = rigaComp.ColData; RigheConsumi.Add(datiComp); } // recupero data-ora ricetta da campo string DateTime recExeDt = DateTime.Today; DateTime.TryParse(currRecipe.DesRecipe.DesData.Date, out recExeDt); // converto le righe di consumo ColData in ConsRec listConsumi = RigheConsumi.Select(x => new ConsRec() { FileRef = recipeFile, DtRif = recExeDt, ColourCode = x.ColourCode, Description = x.Description, WeightGrTot = x.Weightgrtotal }).ToList(); } } } catch (Exception exc) { lgError($"Eccezione in RecipeGetCons{Environment.NewLine}{exc}"); } } return listConsumi; } /// /// Prepara la ricetta indicata partendo dai dati della ricetta template + dati ODL di riferimento /// /// Path del file template della ricetta /// Record del PODL da inviare /// Path della ricetta da inviare protected virtual bool RecipePrepare(string tempPath, string recFilePath, PODLModel podlReq) { bool fatto = false; // leggo il file template ricetta var rawData = File.ReadAllText(recFilePath); string fixContent = rawData; // eseguo sostituzioni foreach (var rule in RecipeReplRules) { string replVal = rule.Value; // calcolo la sostituzione per i valori SPECIFICI... if (replVal.Contains("{{PODL}}")) { replVal = replVal.Replace("{{PODL}}", $"PODL{podlReq.IdxPromessa:00000000}"); } if (replVal.Contains("{{Qty}}")) { replVal = replVal.Replace("{{Qty}}", $"{podlReq.NumPezzi}"); } if (replVal.Contains("{{Note}}")) { replVal = replVal.Replace("{{Note}}", $"{podlReq.Note}"); } fixContent = fixContent.Replace(rule.Key, replVal); } // scrivo file (secondo quantità...)! string destPath = Path.Combine(tempPath, $"PODL{podlReq.IdxPromessa:00000000}.xml"); if (podlReq.NumPezzi <= maxQtyPerFile) { File.WriteAllText(destPath, fixContent); fatto = true; } else { int numReq = (int)Math.Ceiling((double)podlReq.NumPezzi / maxQtyPerFile); // splitto e scrivo... for (int i = 1; i <= numReq; i++) { destPath = Path.Combine(tempPath, $"PODL{podlReq.IdxPromessa:00000000}-{i}.xml"); File.WriteAllText(destPath, fixContent); } fatto = true; } return fatto; } /// /// Prepara le ricette dato path temp scaricando elenco PODL /// /// /// Percorso temp di appoggio x preparare ricette (compreso nome macchina) /// /// /// Indica se usare le copie locali delle ricette oppure richiedere da remoto (REST get) /// /// protected virtual bool RecipeReqWriteLocal(string tempPath, bool useLocal) { bool fatto = false; lgTrace($"RecipeReqWriteLocal start | tempPath {tempPath} | useLocal {useLocal}"); List reqPOdlList = new List(); // per prima cosa recupero elenco PODL da gestire.... var rawListPODL = callUrl(urlGetCurrPODL, false); if (!string.IsNullOrEmpty(rawListPODL)) { try { reqPOdlList = JsonConvert.DeserializeObject>(rawListPODL); } catch (Exception exc) { lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}"); } } else { lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto"); } // proseguo se ne ho trovati... if (reqPOdlList.Count > 0) { // in questo elenco devo considerare solo PODL ATTIVI... List actPOdlList = reqPOdlList.Where(x => x.Attivabile).ToList(); // da questo elenco,se è rimasto qualcosa, ciclo x ricette da inviare foreach (var actPodlReq in actPOdlList) { // devo escludere i PODL già gestiti if (!POdlSentList.ContainsKey(actPodlReq.IdxPromessa)) { string recFilePath = ""; try { // calcolo path ricetta... recFilePath = Path.Combine(pathList["path-locBase"], pathList["path-00-Arch"], actPodlReq.Recipe); // preparo file ricetta x PODL bool fileOk = RecipePrepare(tempPath, recFilePath, actPodlReq); if (fileOk) { // aggiungo PODL ad elenco dei processati POdlSentList.Add(actPodlReq.IdxPromessa, actPodlReq); // indico okReport (almeno per 1 è ok...) fatto = true; } } catch (Exception exc) { lgError($"recipeReqWriteLocal | Errore durante ciclo verifica ricetta | idxPODL {actPodlReq.IdxPromessa} | recFilePath {recFilePath}{Environment.NewLine}{exc}"); } } } // salvo su file elenco PODL gestiti/inviati POdlSentFileArch = POdlSentList; lgInfo($"RecipeReqWriteLocal | reqPOdlList: {reqPOdlList.Count} | actPOdlList: {actPOdlList.Count} | preparate: {POdlSentList.Count}"); } return fatto; } /// /// Effettua invio delle ricette alla macchina /// /// Area dove si trovano i fiel da trasmettere /// Area archivio /// Area remota dove inviare files /// protected bool RecipeSend(string localPath, string localArch, string remotePath) { bool fatto = false; lgTrace($"RecipeSend start | localPath {localPath} | localArch {localArch} | remotePath {remotePath}"); // recupero elenco files... var fileList = Directory.GetFiles(localPath); string archName = ""; string destName = ""; if (fileList != null && fileList.Count() > 0) { int nMove = 0; foreach (var file in fileList) { string fileName = Path.GetFileName(file); archName = Path.Combine(localArch, fileName); destName = Path.Combine(remotePath, fileName); lgInfo($"RecipeSend | fileName: {fileName} | archName: {archName} | destName: {destName} "); File.Copy(file, destName, true); File.Move(file, archName); nMove++; } fatto = true; lgInfo($"RecipeSend | trovate {fileList.Count()} file | {nMove} trasferiti"); } return fatto; } /// /// Effettua recupero in locale dei task eseguiti dalla macchina /// /// Area remota da dove recuperare files /// Area dove parcheggiare temporaneamente i fiels x processing /// protected virtual bool RecipeTaskDoneRetrieve(string remotePath, string localPath) { bool fatto = false; // recupero elenco files... var fileList = Directory.GetFiles(remotePath); string destName = ""; if (fileList != null && fileList.Count() > 0) { foreach (var file in fileList) { string fileName = Path.GetFileName(file); destName = Path.Combine(localPath, fileName); lgInfo($"Recupero Task Eseguiti | fileName: {fileName} | destName: {destName} "); File.Move(file, destName); } fatto = true; } return fatto; } /// /// 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; } /// /// Processa chiusura ODL /// /// Idx dell'ODL da chiudere /// DataOra di riferimento evento stop protected bool SendCloseOdl(int idxOdl, DateTime dtRif) { bool answ = false; DateTime dtCurr = DateTime.Now; string resp = callUrl($"{urlODLClose}{idxOdl}&dtEve={dtRif}&dtCurr={dtCurr}", false); answ = resp == "OK"; return answ; } /// /// Processa chiusura PODL /// /// Idx del PODL da chiudere /// DataOra di riferimento evento stop protected bool SendClosePOdl(int idxPOdl, DateTime dtRif) { bool answ = false; DateTime dtCurr = DateTime.Now; string resp = callUrl($"{urlPODLClose}{idxPOdl}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false); answ = resp == "OK"; 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()); } /// /// Processa avvio PODL 8ed eventuale chiusura precedente) /// /// Idx del PODL da avviare /// DataOra di riferimento evento start protected bool SendStartPodl(int idxPOdl, DateTime dtRif) { bool answ = false; DateTime dtCurr = DateTime.Now; // chiamata avvio PODL (a posteriori) string resp = callUrl($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false); answ = resp == "OK"; return answ; } /// /// Invio contapezzi alla dataora indicata /// /// Num pezzi da registrare /// DataOra di riferimento evento protected bool SendPzIncrAtDate(int numPz, DateTime dtRif) { bool answ = false; DateTime dtCurr = DateTime.Now; // chiamata avvio PODL (a posteriori) string resp = callUrl($"{urlAddPzCountAtDate}{numPz}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false); answ = resp == "OK"; return answ; } /// /// 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 lgDebug($"Decodifica aree alarmMap: {item.description} | {item.memAddr} x {item.size} byte | {item.messages.Count} messaggi allarme"); } // invio oggetto alarmMap al server x successiva decodifica // FIXME TODO FARE !!!! invio PUT del file *_alarm.json } /// /// Setup parametri decode file excel (opzionale) /// protected virtual void setupFileDecod() { // verifica se siano necessari configuraizoni speciali dalla excDecod: // es: lettura/invio file excel if (memMap.fileDecod != null && memMap.fileDecod.Count > 0) { FileDecod = memMap.fileDecod; } } /// /// setup parametri da file di conf /// protected void setupMemMap() { bool serverOk = testPingServer == IPStatus.Success; lgDebug($"setupMemMap | trovati {memMap.mMapRead.Count} parametri Read (TSVC)"); lgDebug($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write"); if (utils.CRB("verbose")) { string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented); lgDebug($"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, UM = item.Value.unit, DTStart = DateTime.Now.AddHours(-1), dataArray = new List() }; TSVC_Data.Add(item.Key, currConf); } // documento... foreach (var item in TSVC_Data) { lgTrace($"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, Formatting.Indented); // controllo ping al server... if (serverOk) { var resp = utils.callUrlNow($"{urlSaveMemMap}", rawData); } else { lgInfo($"Mancata risposta ping da server, saltato step invio memMap a {urlSaveMemMap}"); } // salvo ANCHE come parametri i valori... objItem currItem = new objItem(); List allParam = new List(); string currValore = ""; // valori WRITE foreach (var item in memMap.mMapWrite) { currValore = ""; //se ho valori current li impiego... if (currProdData.ContainsKey(item.Value.name)) { currValore = currProdData[item.Value.name]; item.Value.value = currValore; } currItem = new objItem() { uid = item.Value.name, name = item.Value.name, description = !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, value = currValore, displOrdinal = item.Value.displOrdinal }; allParam.Add(currItem); } // valori READ foreach (var item in memMap.mMapRead) { // se non ci fosse aggiungo var trovato = allParam.FirstOrDefault(x => x.uid == item.Value.name); if (trovato == null) { currValore = ""; // se ho valori current li impiego... if (currProdData.ContainsKey(item.Value.name)) { currValore = currProdData[item.Value.name]; item.Value.value = currValore; } currItem = new objItem() { uid = item.Value.name, name = item.Value.name, description = !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, value = currValore, displOrdinal = item.Value.displOrdinal }; allParam.Add(currItem); } } // invio su cloud parametri SE sono connesso alla macchina... in pratica reset parametri... if (serverOk) { string tipoCall = urlSaveAllParams; rawData = JsonConvert.SerializeObject(allParam, Formatting.Indented); if (serverOk) { // verifica se sia un IOB "parziale") if (cIobConf.disableExeTask || cIobConf.disableStateCh) { // versione upsert tipoCall = urlUpdateWriteParams; } var resp = utils.callUrl($"{tipoCall}", rawData); } else { lgInfo($"Mancata risposta ping server, non effettuata chiamata {tipoCall}"); } } lgDebug($"setupMemMap | salvata conf memoria R/W"); } } /// /// Setup parametri memoria opzionali /// es: BitCond e IntCond x ricerca valori target /// protected virtual void setupOptMemPar() { // verifica se siano necessari configuraizoni speciali dalla optMemPar: // es: per ricerca condizioni status bolleane come ModbusTCP... if (memMap.optMemPar != null && memMap.optMemPar.Count > 0) { // cerco condizioni BIT speciali x Auto, Estop, Work.. devono essere *BitCond var listPar2add = memMap.optMemPar.Where(x => x.Key.EndsWith("BitCond")).ToList(); if (listPar2add != null && listPar2add.Count > 0) { foreach (var item in listPar2add) { addCheckConditionBit(item.Key, item.Value); } } // cerco condizioni INT speciali x Auto, Estop, Work.. devono essere *IntCond var listParInt2add = memMap.optMemPar.Where(x => x.Key.EndsWith("IntCond")).ToList(); if (listParInt2add != null && listParInt2add.Count > 0) { foreach (var item in listParInt2add) { addCheckConditionInt(item.Key, item.Value); } } // cerco valori *2Translate var listVal2Translate = memMap.optMemPar.Where(x => x.Key.Contains("2Translate")).ToList(); if (listVal2Translate != null && listVal2Translate.Count > 0) { // cerco solo valori BIT... var listValBit = listVal2Translate.Where(x => x.Key.StartsWith("VarBit2Translate")).ToList(); if (listValBit != null && listValBit.Count > 0) { foreach (var item in listValBit) { addVal2TranslBit(item.Value); } } // ...e poi valori INT... var listValInt = listVal2Translate.Where(x => x.Key.StartsWith("VarInt2Translate")).ToList(); if (listValInt != null && listValInt.Count > 0) { foreach (var item in listValInt) { addVal2TranslInt(item.Value); } } } } } /// /// Init parametri speciali, tipicamente KVP opzionali da json /// protected virtual void setupSpecialParams() { string sCond = ""; // per prima cosa inizializzo lista PODL inviati POdlSentList = POdlSentFileArch; // verifico se sia attiva gestione riduzione FluxLog... if (!string.IsNullOrEmpty(getOptJsonKVP("fluxLogReduce"))) { bool.TryParse(getOptJsonKVP("fluxLogReduce"), out fluxLogReduce); double.TryParse(getOptJsonKVP("fluxLogRedDeadBand"), NumberStyles.Any, CultureInfo.InvariantCulture, out fluxLogRedDeadBand); int.TryParse(getOptJsonKVP("fluxLogResendPeriod"), out fluxLogResendPeriod); lgInfo($"FluxLog Redux | fluxLogReduce: {fluxLogReduce} | fluxLogRedDeadBand: {fluxLogRedDeadBand:N2} | fluxLogResendPeriod: {fluxLogResendPeriod} min"); } // check modalità ricetta attiva bool.TryParse(getOptJsonKVP("hasRecipe"), out hasRecipe); // verifico se usare ricette locali/http... bool.TryParse(getOptJsonKVP("useLocalRecipe"), out useLocalRecipe); // recupero quantitativo massimo KG x PODL int.TryParse(getOptJsonKVP("maxPodlQty"), out maxQtyPerFile); // recupero le folder specifiche x IN/OUT ricette filtrando direttamente l'area di memoria... sCond = "path"; var dirList = memMap.optKVP .Where(x => x.Key.StartsWith(sCond)) .ToList(); pathList = dirList.ToDictionary(x => x.Key, x => x.Value); // gestione replace in file ricette... sCond = "replace-"; var ruleList = memMap.optKVP .Where(x => x.Key.StartsWith(sCond)) .ToList(); RecipeReplRules = ruleList.ToDictionary(x => x.Key.Replace(sCond, ""), x => x.Value); } /// /// Cleanup stringa x impiego tipo ident da char dubbi /// /// /// protected string strFixId(string origData) { return origData.Replace(".", "").Replace(" ", "_"); } /// /// Effettua chiamata MP-IO per cheidere all'utente se vuole effettuare chiusura ODL /// corrente della macchina /// /// protected bool tryAskCloseCurrODL() { bool fatto = false; string fullUrl = $"{urlODLAskClose}{currIdxODL}"; try { // invio chiamata URL x chiusura ODL su macchina string callResp = callUrl(fullUrl, false); fatto = (callResp != "KO") ? true : false; } catch { } lgInfo($"Eseguito tryAskCloseCurrODL per {currIdxODL} | url: {fullUrl} | esito: {fatto}"); return fatto; } /// /// Effettua chiamata MP-IO per tentare chiusura ODL corrente della macchina /// /// protected bool tryCloseCurrODL() { bool fatto = false; string fullUrl = $"{urlODLClose}{currIdxODL}"; try { // invio chiamata URL x chiusura ODL su macchina string callResp = callUrl(fullUrl, false); fatto = (callResp != "KO") ? true : false; } catch { } lgInfo($"Eseguito tryCloseCurrODL per {currIdxODL} | url: {fullUrl} | esito: {fatto}"); return fatto; } /// /// Effettua chiamata MP-IO per tentare chiusura ODL specifico /// /// /// protected bool tryCloseODL(int IdxODL) { bool fatto = false; string fullUrl = $"{urlODLClose}{IdxODL}"; try { // invio chiamata URL x chiusura ODL su macchina string callResp = callUrl(fullUrl, false); fatto = (callResp != "KO") ? true : false; } catch { } lgInfo($"Eseguito tryCloseODL per {IdxODL} | url: {fullUrl} | esito: {fatto}"); return fatto; } /// /// Effettua chiamata MP-IO per tentare chiusura PODL --> ODL specifico /// /// /// protected bool tryClosePODL(int idxPODL) { bool fatto = false; string fullUrl = $"{urlPODLClose}{idxPODL}"; try { // invio chiamata URL x chiusura ODL su macchina string callResp = callUrl(fullUrl, false); fatto = (callResp != "KO") ? true : false; } catch { } lgInfo($"Eseguito tryClosePODL per {idxPODL} | url: {fullUrl} | esito: {fatto}"); return fatto; } /// /// Effettua chiamata MP-IO per tentare chiusura PODL --> ODL specifico /// /// /// /// /// /// protected bool tryClosePODL(int idxPODL, DateTime dtEve, DateTime dtCurr) { bool fatto = false; string fullUrl = $"{urlPODLClose}{idxPODL}&dtEve={dtEve:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}"; try { // invio chiamata URL x chiusura ODL su macchina string callResp = callUrl(fullUrl, false); fatto = (callResp != "KO") ? true : false; } catch { } lgInfo($"Eseguito tryClosePODL per {idxPODL} | url: {fullUrl} | esito: {fatto}"); return fatto; } /// /// Processa avvio PODL (ed eventuale chiusura precedente) /// /// Cod Articolo del PODL da avviare /// Cod Gruppo dell'impianto come default /// num pz richiesti protected int TryCreatePodl(string codArt, string codGruppo, int numPz) { int answ = 0; string urlEncoded = $"{urlCreatePOdl}CodArt={codArt}&CodGruppo={codGruppo}&numPz={numPz}"; string resp = callUrl(urlEncoded, false); int.TryParse(resp, out answ); return answ; } /// /// Cerca di inviare su un altro thread i vari dati accumulati... /// protected 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()); Task taskRawTransf = Task.Run(() => svuotaCodaRawTransf()); Task taskULog = Task.Run(() => svuotaCodaULog()); } 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(); // coda RawTransf svuotaCodaRawTransf(); // coda UserLog svuotaCodaULog(); // refresh 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) { lgError("IOB - SERVER NOT READY"); } } } catch (Exception exc) { lgError($"Errore in fase trySendValues | currSendErrors: {currSendErrors}{Environment.NewLine}{exc}"); currDispData.semOut = Semaforo.SR; } raiseRefresh(currDispData); } /// /// Effettua chiamata MP-IO per tentare setup del PODL indicato /// /// /// protected bool trySetupPODL(int idxPODL) { bool fatto = false; string fullUrl = $"{urlOdlStartFromPOdl}{idxPODL}"; try { // invio chiamata URL x avvio PODL su macchina string rawSplit = callUrl(fullUrl, false); fatto = (rawSplit != "KO") ? true : false; } catch { } return fatto; } /// /// Effettua chiamata MP-IO per tentare setup del PODL indicato con indicazioni estese /// (confirm pezzi, dtEvento, dtCorrente) /// /// /// /// /// /// protected bool trySetupPODL(int idxPODL, bool doConfirm, DateTime dtEve, DateTime dtCurr) { bool fatto = false; //string format = "yyyyMMddHHmmssfff"; string fullUrl = $"{urlOdlStartFromPOdl}{idxPODL}&doConfirm={doConfirm}&dtEve={dtEve:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}"; try { // invio chiamata URL x avvio PODL su macchina string rawSplit = callUrl(fullUrl, false); fatto = (rawSplit != "KO") ? true : false; } catch { } return fatto; } #endregion Protected Methods #region Private Fields /// /// Dizionario dei valori bloccati x evitare log eccessivo /// private Dictionary vetoLogError = new Dictionary(); /// /// Periodo di veto log in minuti /// private int vetoPeriodMin = 15; #endregion Private Fields #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; } } private bool qInEnabCurr { get; set; } = false; /// /// Redis key del dizionari valori currProdData persistiti /// private string rKeyCurrProdData { get => redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:CurrProdData"); } /// /// 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); reply = pingSender.Send(IPAddress.Loopback, pingTOut); lgError($"ping to loopback addres per mancanza address server"); } } 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 Private Methods /// /// Aggiunge in setup memoria la checkCondition BIT "decodificata" /// /// Chiave da aggiungere /// Valore da decodificare e poi aggiungere private void addCheckConditionBit(string ompKey, string ompVal) { var newCond = new BitConditionCheck(ompKey, ompVal); // cerco x aggiornare o aggiungere... if (OptCheckCondBit.ContainsKey(ompKey)) { OptCheckCondBit[ompKey] = newCond; } else { OptCheckCondBit.Add(ompKey, newCond); } } /// /// Aggiunge in setup memoria la checkCondition INT "decodificata" /// /// Chiave da aggiungere /// Valore da decodificare e poi aggiungere private void addCheckConditionInt(string ompKey, string ompVal) { var newCond = new IntConditionCheck(ompKey, ompVal); // cerco x aggiornare o aggiungere... if (OptCheckCondInt.ContainsKey(ompKey)) { OptCheckCondInt[ompKey] = newCond; } else { OptCheckCondInt.Add(ompKey, newCond); } } /// /// Aggiunge in setup memoria l'oggetto tipo valore BIT (NON mutuamente esclusivo) da tradurre /// /// Elenco valori da aggiungere formato csv (es "a,b,c") private void addVal2TranslBit(string csvList) { var list2add = csvList.Split(','); foreach (var memKey in list2add) { // cerco x aggiungere... if (!OptVar2TranslBit.ContainsKey(memKey)) { // init traduzione vuota OptVar2TranslBit.Add(memKey, ""); } } } /// /// Aggiunge in setup memoria l'oggetto tipo valore INT (mutuamente esclusivo) da tradurre /// /// Elenco valori da aggiungere formato csv (es "a,b,c") private void addVal2TranslInt(string csvList) { var list2add = csvList.Split(','); foreach (var memKey in list2add) { // cerco x aggiungere... if (!OptVar2TranslInt.ContainsKey(memKey)) { // init traduzione vuota OptVar2TranslInt.Add(memKey, ""); } } } /// /// Verifica e se necessario comprime directory log... /// private void checkShrinkDir() { // comprimo x prima cosa la folder dell'IOB corrente string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", cIobConf.codIOB); try { baseUtils.shrinkDir(path); // provo a comprimere anche la folder MAIN.... path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "MAIN"); baseUtils.shrinkDir(path); } catch (Exception exc) { lgError($"Eccezione in checkShrinkDir{Environment.NewLine}{exc}"); } } /// /// 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; } /// /// Archivia una cartella in un file zip /// /// Cartelal da archiviare /// Nome del file zip da produrre... private bool doZipArchiveFolder(string folderPath, string zipName) { bool fatto = false; if (Directory.Exists(folderPath)) { fatto = fileMover.zippaDirectory(folderPath, zipName); // se fatto elimino vecchia directory... if (fatto) { Directory.Delete(folderPath, true); } } return fatto; } /// /// 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) { lgTrace("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 { lgTrace("END BLINK: B{0}", i); } } } } } } /// /// Legge da conf il valore di demoltiplica lettura dynData (se presente) o ignora e pone a 1... /// private void fixDemFactDynData() { var rawDemFact = getOptPar("DEM_FACT_DYN_DATA"); if (string.IsNullOrEmpty(rawDemFact)) { demFactDynData = 1; } else { int.TryParse(rawDemFact, out demFactDynData); } } /// /// 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; } /// /// Verifica se il log di un dato errore sia permesso /// /// ID del valore log da loggare/verificare /// private bool logValuePermit(string logKey) { bool doLog = false; if (vetoLogError.ContainsKey(logKey)) { // verifico se veto scaduto... if (DateTime.Now > vetoLogError[logKey]) { doLog = true; vetoLogError[logKey] = DateTime.Now.AddMinutes(vetoPeriodMin); } } else { doLog = true; vetoLogError.Add(logKey, DateTime.Now.AddMinutes(vetoPeriodMin)); } return doLog; } /// /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... /// private void processAllMemory() { //verifico se sia abilitato processing... if (queueInEnabCurr) { // 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); } else { lgInfo($"VETO on processAllMemory: queueIn disabled"); checkVetoQueueIn(); } } 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) { if (memMap.mMapWrite.ContainsKey(item.Key)) { // preparo obj da scrivere objItem newWrite = new objItem() { uid = item.Key, name = item.Key, description = memMap.mMapWrite[item.Key].description, //reqValue = memMap.mMapWrite[item.Key].value, reqValue = item.Value, value = item.Value, lastRequest = DateTime.Now, writable = true, displOrdinal = memMap.mMapWrite[item.Key].displOrdinal }; updatedPar.Add(newWrite); } } } // se ho da scrivere... scrivo TUTTI! if (updatedPar.Count > 0) { // scrivo valore! lgInfo($"Chiamata di plcWriteParams da processMem2Write: {updatedPar.Count} updatedPar"); plcWriteParams(ref updatedPar); // invio su cloud parametri! string rawData = JsonConvert.SerializeObject(updatedPar); utils.callUrl($"{urlUpdateWriteParams}", rawData); lgInfo($"Notificato a server scrittura {updatedPar.Count} 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)); } } } } /// /// Effettua calcolo dei consumi + esportazione in file richiesto /// /// Folder dei file da processare /// Percorso file out di export private bool RecipeDoConsumeReport(string folderPath, string reportPath) { // var di base bool fatto = false; string expMode = "csv"; List ListConsDet = new List(); List ListConsSum = new List(); // file conf x conversioni... string confSetupPath = pathList["path-confSetup"]; ConvSetup currConf = new ConvSetup(); bool addHeader = false; int numDec = 4; string codMag = "NA"; if (!string.IsNullOrEmpty(confSetupPath)) { string rawConfFile = File.ReadAllText(confSetupPath); if (!string.IsNullOrEmpty(rawConfFile)) { currConf = JsonConvert.DeserializeObject(rawConfFile); if (currConf != null) { addHeader = currConf.addHeader; numDec = currConf.numDec; codMag = currConf.codMag; expMode = currConf.mode.ToLower(); } } } // ciclo x ogni file ricevendone i consumi ed accumulandoli in lista... var fileList = Directory.GetFiles(folderPath); foreach (var cFile in fileList) { var consumiFile = RecipeGetCons(cFile); ListConsDet.AddRange(consumiFile); } // se ho trovato dati double ratioConv = 1; if (ListConsDet.Count > 0) { var dirName = new DirectoryInfo(folderPath).Name; // recupero elenco colori var elencoColori = ListConsDet .GroupBy(x => x.ColourCode) .Select(grp => grp.Last()) .ToList(); // faccio le somme x colore foreach (var colore in elencoColori) { ratioConv = 1; // ricerca in conf (% di consumo) if (currConf != null && currConf.convRatio != null) { if (currConf.convRatio.ContainsKey(colore.ColourCode)) { ratioConv = currConf.convRatio[colore.ColourCode]; } } // calcolo peso equivalente in kg con conversione ratio var totWeightKg = ListConsDet .Where(x => x.ColourCode == colore.ColourCode) .Sum(x => x.WeightGrTot) / 1000 * ratioConv; // record finale trasformato in KG... ConsOut colTotal = new ConsOut() { CodMag = codMag, ColourCode = colore.ColourCode, Description = colore.Description, UM = "KG", WeightKgProc = totWeightKg > 0.01 ? Math.Round(totWeightKg, numDec) : 0.01, DtRif = colore.DtRif }; ListConsSum.Add(colTotal); } // infine serializzo x output in base alla modalità richiesta switch (expMode) { case "fixwidth": fatto = DataExport.SaveFixedWidth(ListConsSum, $"{reportPath}.txt", currConf.fieldLength, currConf.fieldLPad, currConf.fieldNDec); break; case "csv": default: fatto = DataExport.SaveToCsv(ListConsSum, $"{reportPath}.csv", addHeader); break; } } return fatto; } /// /// Processa la ricetta alla ricerca dei dati PODL x inviare chiamate ad MP-IO /// /// Path RIcetta private bool RecipeDoProcessPODL(string recipeFile) { bool answ = false; // init var int idxPOdl = 0; string rawVal = ""; // leggo righe file! var recipeRows = File.ReadAllLines(recipeFile); // per ogni file ricevuto controlla se viene dal MES (cerca PODL) string tokenSearch = "PODL"; // recupero riga PODL... var rowPodl = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault(); if (rowPodl != null) { DateTime dtStartPOdl = DateTime.Today; int duration = 0; DateTime dtEndPOdl = DateTime.Today.AddMinutes(10); string dtEve = ""; string dtCurr = ""; // inizio ricerca PODL rawVal = rowPodl.Trim().Replace(tokenSearch, "").Replace("", ""); int.TryParse(rawVal, out idxPOdl); if (idxPOdl > 0) { // leggo inizio commessa tokenSearch = ""; var rowStart = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault(); if (rowStart != null) { rawVal = rowStart.Trim().Replace(tokenSearch, "").Replace("", ""); DateTime.TryParse(rawVal, out dtStartPOdl); } // leggo durata commessa tokenSearch = ""; var rowDurSec = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault(); if (rowDurSec != null) { rawVal = rowDurSec.Trim().Replace(tokenSearch, "").Replace("", ""); int.TryParse(rawVal, out duration); dtEndPOdl = dtStartPOdl.AddSeconds(duration); } // prova ad avviare/chiudere PODL relativo (eventualmente duplicandolo) dtEve = $"{dtStartPOdl:yyyyMMddHHmmssfff}"; dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}"; callUrl($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false); // ora chiamo chiusura... dtEve = $"{dtEndPOdl:yyyyMMddHHmmssfff}"; dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}"; callUrl($"{urlPODLClose}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false); } answ = true; } return answ; } /// /// Elimino record da REDIS (locale e remoto) /// /// private void RecipeRemoveWeekStatus(string keyReq) { string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats"); var okHashDict = redisMan.redRemoveHashField(fullKey, keyReq); // rileggo status hash var currDict = redisMan.redGetHashDict(fullKey); // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; string dictPayload = JsonConvert.SerializeObject(currDict); callUrlWithPayload(remUrl, dictPayload, false); } 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 delle 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"); // fix url wait random... urlRandWait = utils.CRI("urlRandWait"); // fix fattore demoltiplica dynData fixDemFactDynData(); // svuoto code se richiesto if (resetQueue) { QueueAlarm = new DataQueue(cIobConf.codIOB, "QueueAlarm", cIobConf.EnableRedisQueue); //QueueAlarm = new ConcurrentQueue(); QueueIN = new DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue); //QueueIN = new ConcurrentQueue(); QueueFLog = new DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue); //QueueFLog = new ConcurrentQueue(); QueueMessages = new DataQueue(cIobConf.codIOB, "QueueMessages", cIobConf.EnableRedisQueue); //QueueMessages = new ConcurrentQueue(); QueueRawTransf = new DataQueue(cIobConf.codIOB, "QueueRawTransf", cIobConf.EnableRedisQueue); //QueueRawTransf = new ConcurrentQueue(); QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue); //QueueULog = new ConcurrentQueue(); } // imposto contatori blink a zero... i_counters = new int[32]; lastPeriodicLog = DateTime.Now; // fix parametri generali... enablePrgName = true; // valore standard divieto accodamento segnali IN string VETO_QUEUE_IN = getOptPar("VETO_QUEUE_IN"); if (!string.IsNullOrEmpty(VETO_QUEUE_IN)) { int.TryParse(VETO_QUEUE_IN, out vetoQueueIn); } // fix slow data string ENABLE_SLOW_DATA = getOptPar("ENABLE_SLOW_DATA"); if (!string.IsNullOrEmpty(ENABLE_SLOW_DATA)) { bool.TryParse(getOptPar("ENABLE_SLOW_DATA"), out enableSlowData); } } 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 if (!isMulti) { 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() { bool sendOnMachineOff = false; // 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... SE non disabilitato.. if (!disableWdst) { accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); } lastWatchDog = DateTime.Now; sendOnMachineOff = true; lgInfo("Impostato sendOnMachineOff a true"); } // 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 || sendOnMachineOff) { // 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 DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue); //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; } } } } /// /// Processo la coda RawTransf... /// private bool svuotaCodaRawTransf(bool force = false) { bool fatto = false; // verifico SE la coda abbia dei valori... if (QueueRawTransf.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... if (QueueRawTransf.Count > 0) { string currVal = ""; if (MPOnline || force) { if (IobOnline || force) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta if (QueueRawTransf.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { QueueRawTransf.TryDequeue(out currVal); listaValori.Add(currVal); } fatto = sendDataBlock(urlType.RawTransf, listaValori, force); } else { // invio in blocco listaValori = QueueRawTransf.ToList(); // invio fatto = sendDataBlock(urlType.RawTransf, listaValori, force); if (fatto) { // svuoto se ha okReport! QueueRawTransf = new DataQueue(cIobConf.codIOB, "QueueRawTransf", cIobConf.EnableRedisQueue); //QueueRawTransf = new ConcurrentQueue(); } } } else { break; } } else { break; } } else { break; } } } return fatto; } /// /// Processo la coda UserLog... /// private void svuotaCodaULog() { // verifico SE la coda abbia dei valori... if (QueueULog.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... if (QueueULog.Count > 0) { string currVal = ""; if (MPOnline) { if (IobOnline) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta if (QueueULog.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { QueueULog.TryDequeue(out currVal); listaValori.Add(currVal); } sendDataBlock(urlType.ULog, listaValori); } else { // invio in blocco listaValori = QueueULog.ToList(); // invio sendDataBlock(urlType.ULog, listaValori); // svuoto! QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue); //QueueULog = new ConcurrentQueue(); } } else { break; } } else { break; } } else { break; } } } } /// /// 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 } }