diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 064ec765..0b7db10f 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -19,3600 +19,3365 @@ using System.Windows.Forms; namespace IOB_WIN { - public class IobGeneric - { - #region variabili serializzate in REDIS - - /// - /// Oggetto connessione REDIS - /// - public RedisIobCache redisMan; - - #endregion variabili serializzate in REDIS - - #region variabili ed oggetti base - - /// - /// wrapper di log - /// - protected static Logger lg; - - /// - /// 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 16 BIT valori in ingresso al filtro - /// - protected int B_input; - - /// - /// Vettore 16 BIT valori in uscita dal filtro - /// - protected int B_output; - - /// - /// Vettore 16 BIT valori precedenti - /// - protected int B_previous; - - /// - /// Dizionario valori impostati x produzione - /// - protected Dictionary currProdData = new Dictionary(); - - /// - /// Array dei contatori x segnali blinking - /// - protected int[] i_counters; - - /// - /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) - /// - protected bool inSetup = false; - - /// - /// Ultimo invio contapezzi (x invio delayed) - /// - protected DateTime lastPzCountSend; - - /// - /// Dizionario ultimi valori (double) delle TSVC - /// - protected Dictionary LastTSVC = new Dictionary(); - - /// - /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) - /// - protected DateTime lastWarnODL; - - /// - /// TImeout x ping al server - /// - protected int pingServerMsTimeout = utils.CRI("pingMsTimeout"); - - /// - /// Ritardo minimo x invio contapezzi - /// - protected int pzCountDelay; - - /// - /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) - /// - protected Dictionary TSVC_Data = new Dictionary(); - - /// - /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a scadenza periodo...) - /// - protected Dictionary VarArray = new Dictionary(); - - /// - /// valore booleano di check se sia stato AVVIATO l'adapter (Running) - /// - public bool adpRunning = false; - - /// - /// valore booleano di check se l'adapter STIA SALVANDO - /// - public bool adpSaving = false; - - /// - /// valore booleano (richiesta di riavvio automatico) - /// - public bool adpTryRestart; - - /// - /// Contapezzi attuale - /// - public Int32 contapezziIOB + public class IobGeneric { - get - { - return tcMan.pzCountIOB; - } - set - { - tcMan.pzCountIOB = value; - } - } + #region variabili serializzate in REDIS - /// - /// Ultima lettura variabile contapezzi da CNC - /// - public Int32 contapezziPLC - { - get - { - return tcMan.pzCountPLC; - } - set - { - tcMan.pzCountPLC = value; - } - } + /// + /// Oggetto connessione REDIS + /// + public RedisIobCache redisMan; - /// - /// Conteggio ATTUALE ore macchina IN LAVORO - /// - public double contOreMaccLav; + #endregion variabili serializzate in REDIS - /// - /// Conteggio ATTUALE ore macchina ON - /// - public double contOreMaccOn; + #region variabili ed oggetti base - /// - /// ODL attualmente sulla macchina - /// - public Int32 currIdxODL; + /// + /// valore booleano di check se sia stato AVVIATO l'adapter (Running) + /// + public bool adpRunning = false; - /// - /// Modo corrente (da classe ENUM) - /// - public CNC_MODE currMode; + /// + /// valore booleano di check se l'adapter STIA SALVANDO + /// + public bool adpSaving = false; - /// - /// Indica se sia richiesto campionamento memoria PERIODICO - /// - public bool doSampleMemory; + /// + /// valore booleano (richiesta di riavvio automatico) + /// + public bool adpTryRestart; - /// - /// Indica se si debba leggere e fare DUMP delle aree di memoria (1 volta solo all'avvio x debug...) - /// - public bool doStartMemDump; + /// + /// Conteggio ATTUALE ore macchina IN LAVORO + /// + public double contOreMaccLav; - /// - /// Data/ora ultimo avvio adapter - /// - public DateTime dtAvvioAdp = DateTime.Now; + /// + /// Conteggio ATTUALE ore macchina ON + /// + public double contOreMaccOn; - /// - /// Data/ora ultimo spegnimento adapter - /// - public DateTime dtStopAdp = DateTime.Now; + /// + /// ODL attualmente sulla macchina + /// + public Int32 currIdxODL; - /// - /// Indicazione VETO check status IOB x evitare loop troppo stretti... - /// - public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1); + /// + /// Modo corrente (da classe ENUM) + /// + public CNC_MODE currMode; - /// - /// Abilitazione lettura PrgName - /// - public bool enablePrgName = true; + /// + /// Indica se sia richiesto campionamento memoria PERIODICO + /// + public bool doSampleMemory; - /// - /// Abilitazione invio pezzi "in blocco" per recupero contapezzi - /// - public bool enableSendPzCountBlock = false; + /// + /// Indica se si debba leggere e fare DUMP delle aree di memoria (1 volta solo all'avvio x debug...) + /// + public bool doStartMemDump; - /// - /// Determina se sia encessario convertire valori little/big endian (SIEMENS=true, OSAI=FALSE) - /// - public bool hasBigEndian = false; + /// + /// Data/ora ultimo avvio adapter + /// + public DateTime dtAvvioAdp = DateTime.Now; - /// - /// dataOra ultima verifica CNC disconnesso... - /// - public DateTime lastDisconnCheck; + /// + /// Data/ora ultimo spegnimento adapter + /// + public DateTime dtStopAdp = DateTime.Now; - /// - /// Data/ora ultima volta che IOB è stato dichairato online - /// - public DateTime lastIobOnline = DateTime.Now.AddHours(-1); + /// + /// Indicazione VETO check status IOB x evitare loop troppo stretti... + /// + public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1); - /// - /// dataOra ultimo log periodico... - /// - public DateTime lastPeriodicLog; + /// + /// Abilitazione lettura PrgName + /// + public bool enablePrgName = true; - /// - /// dataOra ultimo PING inviato verso il PLC... - /// - public DateTime lastPING; + /// + /// Abilitazione invio pezzi "in blocco" per recupero contapezzi + /// + public bool enableSendPzCountBlock = false; - /// - /// DataOra ultima lettura da PLC - /// - public DateTime lastReadPLC; + /// + /// Determina se sia encessario convertire valori little/big endian (SIEMENS=true, OSAI=FALSE) + /// + public bool hasBigEndian = false; - /// - /// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...) - /// - public string lastSignInVal = ""; + /// + /// dataOra ultima verifica CNC disconnesso... + /// + public DateTime lastDisconnCheck; - /// - /// dataOra ultimo segnale inviato... - /// - public DateTime lastWatchDog; + /// + /// Data/ora ultima volta che IOB è stato dichairato online + /// + public DateTime lastIobOnline = DateTime.Now.AddHours(-1); - /// - /// Massimo numero di px da inviare in blocco - /// - public int maxSendPzCountBlock = 10; + /// + /// dataOra ultimo log periodico... + /// + public DateTime lastPeriodicLog; - /// - /// Minimo numero di px da inviare in blocco - /// - public int minSendPzCountBlock = 5; + /// + /// dataOra ultimo PING inviato verso il PLC... + /// + public DateTime lastPING; - /// - /// Variabile booleana che indica se sia necessario fare refresh del contapezzi - /// - public bool needRefreshPzCount = true; + /// + /// DataOra ultima lettura da PLC + /// + public DateTime lastReadPLC; - /// - /// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing "monoblocco" semplificato"= - /// - public bool procIotMem = false; + /// + /// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...) + /// + public string lastSignInVal = ""; - /// - /// Coda valori ALLARMI ove gestiti... - /// - public ConcurrentQueue QueueAlarm = new ConcurrentQueue(); + /// + /// dataOra ultimo segnale inviato... + /// + public DateTime lastWatchDog; - /// - /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) - /// - public ConcurrentQueue QueueFLog = new ConcurrentQueue(); + /// + /// Massimo numero di px da inviare in blocco + /// + public int maxSendPzCountBlock = 10; - /// - /// Oggetto della coda degli elementi letti (e non ancora trasmessi) - /// - public ConcurrentQueue QueueIN = new ConcurrentQueue(); + /// + /// Minimo numero di px da inviare in blocco + /// + public int minSendPzCountBlock = 5; - /// - /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... - /// - public ConcurrentQueue QueueMessages = new ConcurrentQueue(); + /// + /// Variabile booleana che indica se sia necessario fare refresh del contapezzi + /// + public bool needRefreshPzCount = true; - /// - /// alias booleano false = R - /// - public bool R = false; + /// + /// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing "monoblocco" semplificato"= + /// + public bool procIotMem = false; - /// - /// 32 byte input base (es strobe, 8 word da 32 bit di flags...) - /// - public byte[] RawInput = new byte[32]; + /// + /// Coda valori ALLARMI ove gestiti... + /// + public ConcurrentQueue QueueAlarm = new ConcurrentQueue(); - /// - /// 32 byte output base (es ack, 8 word da 32 bit di flags...) - /// - public byte[] RawOutput = new byte[32]; + /// + /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) + /// + public ConcurrentQueue QueueFLog = new ConcurrentQueue(); - /// - /// Oggetto cronometro x campionamento durate chiamate - /// - public Stopwatch stopwatch = new Stopwatch(); + /// + /// Oggetto della coda degli elementi letti (e non ancora trasmessi) + /// + public ConcurrentQueue QueueIN = new ConcurrentQueue(); - /// - /// Oggetto gestione TempiCiclo e contapezzi - /// - public TCMan tcMan = new TCMan(0.5, 1.3, 5); + /// + /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... + /// + public ConcurrentQueue QueueMessages = new ConcurrentQueue(); - /// - /// Imposta veto chiamata split (durante chiamata, per 60 sec) - /// - public DateTime vetoSplit = DateTime.Now.AddMinutes(1); + /// + /// alias booleano false = R + /// + public bool R = false; - /// - /// alias booleano true = W - /// - public bool W = true; + /// + /// 32 byte input base (es strobe, 8 word da 32 bit di flags...) + /// + public byte[] RawInput = new byte[32]; - /// - /// Evento Iob ha subito un refresh - /// - public event EventHandler eh_refreshed; + /// + /// 32 byte output base (es ack, 8 word da 32 bit di flags...) + /// + public byte[] RawOutput = new byte[32]; - /// - /// Coda massima ammessa per FLog (se <=0 disattivata...) - /// - protected int maxQueueFLog { get; set; } = utils.CRI("maxQueueFLog"); + /// + /// Oggetto cronometro x campionamento durate chiamate + /// + public Stopwatch stopwatch = new Stopwatch(); - /// - /// Numero letture IN da avvio - /// - protected int nReadFilt { get; set; } + /// + /// Oggetto gestione TempiCiclo e contapezzi + /// + public TCMan tcMan = new TCMan(0.5, 1.3, 5); - /// - /// Numero letture IN da avvio - /// - protected int nReadIN { get; set; } + /// + /// Imposta veto chiamata split (durante chiamata, per 60 sec) + /// + public DateTime vetoSplit = DateTime.Now.AddMinutes(1); - /// - /// Numero invii OUT (svuotamento coda) - /// - protected int nSendOut { get; set; } + /// + /// alias booleano true = W + /// + public bool W = true; - /// - /// Numero simulazioni ammesse... - /// - protected int numSim { get; set; } + /// + /// wrapper di log + /// + protected static Logger lg; - /// - /// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...) - /// - public static bool DemoInSample - { - get - { - return baseUtils.CRB("DemoInSample"); - } - } + /// + /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC + /// + protected bool adpCommAct; - /// - /// Verifica se sia in modalità DEMO x dati OUTPUT - /// - public static bool DemoOut - { - get - { - return utils.CRB("DemoOut"); - } - } + /// + /// porta x adapter (x restart) + /// + protected int adpPortNum; - /// - /// Indicazione VETO PING a server sino alla data-ora indicata - /// - public static DateTime dtVetoPing - { - get - { - return utils.dtVetoPing; - } - set - { - utils.dtVetoPing = value; - } - } + /// + /// DataOra ultimo avvio adapter x watchdog + /// + protected DateTime adpStartRun; - /// - /// Indicazione VETO invio a server sino alla data-ora indicata - /// - public static DateTime dtVetoSend - { - get - { - return utils.dtVetoSend; - } - set - { - utils.dtVetoSend = value; - } - } + /// + /// Vettore 32 BIT valori in ingresso al filtro + /// + protected int B_input; - /// - /// stato Online/Offline del server MP IO (su REDIS) - /// - public static bool MPOnline - { - get - { - return utils.MPIO_Online; - } - set - { - utils.MPIO_Online = value; - } - } + /// + /// Vettore 32 BIT valori in uscita dal filtro + /// + protected int B_output; - /// - /// Contatore x invio dati FluxLog - /// - public int counterFLog { get; set; } + /// + /// Vettore 32 BIT valori precedenti + /// + protected int B_previous; - /// - /// Contatore x invio dati SignalIN - /// - public int counterSigIN { get; set; } + /// + /// Dizionario valori impostati x produzione + /// + protected Dictionary currProdData = new Dictionary(); - /// - /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA... - /// - public bool DemoIn - { - get - { - return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn"); - } - } + /// + /// Array dei contatori x segnali blinking + /// + protected int[] i_counters; - /// - /// stato Online/Offline della IOB - /// - public bool IobOnline - { - get - { - return utils.IOB_Online; - } - set - { - utils.IOB_Online = value; - } - } + /// + /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) + /// + protected bool inSetup = false; - /// - /// Log verboso da configurazione (SOLO CHAIVE "verbose"... - /// - public bool isVerboseLog { get; set; } = utils.CRB("verbose"); + /// + /// Ultimo invio contapezzi (x invio delayed) + /// + protected DateTime lastPzCountSend; - /// - /// Ultimo Alarm letto - /// - public string lastAlarm { get; set; } + /// + /// Dizionario ultimi valori (double) delle TSVC + /// + protected Dictionary LastTSVC = new Dictionary(); - /// - /// Ultimo ARRAY DynData letto - /// - public Dictionary lastDynData { get; set; } = new Dictionary(); + /// + /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) + /// + protected DateTime lastWarnODL; - /// - /// Ultimo DynData (sunto) letto - /// - public string lastDynDataCtrlVal { get; set; } + /// + /// TImeout x ping al server + /// + protected int pingServerMsTimeout = utils.CRI("pingMsTimeout"); - /// - /// Ultimo Override set letto - /// - public string lastOverrideFS { get; set; } + /// + /// Ritardo minimo x invio contapezzi + /// + protected int pzCountDelay; - /// - /// Ultimo Override set letto - /// - public string lastOverrideRapid { get; set; } + /// + /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) + /// + protected Dictionary TSVC_Data = new Dictionary(); - /// - /// Ultimo programma letto - /// - public string lastPrgName { get; set; } + /// + /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a scadenza periodo...) + /// + protected Dictionary VarArray = new Dictionary(); - /// - /// Ultimo SysInfo letto - /// - public string lastSysInfo { get; set; } + /// + /// Evento Iob ha subito un refresh + /// + public event EventHandler eh_refreshed; - /// - /// Ultimo URL - /// - public string lastUrl { get; set; } - - /// - /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...) - /// - public bool periodicLog - { - get - { - bool answ = false; - answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); - if (answ) + /// + /// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...) + /// + public static bool DemoInSample { - lastPeriodicLog = DateTime.Now; - } - - return answ; - } - } - - /// - /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN) - /// - public bool verboseLog - { - get - { - bool answ = false; - int logEvery = utils.CRI("logEvery"); - if (logEvery < 1) - { - logEvery = 10; - } - - answ = utils.CRB("verbose") && (nReadIN % logEvery == 0); - return answ; - } - } - - /// - /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati - /// - public virtual void reportRawInput(ref newDisplayData currDispData) - { - // processo eventualmente aggiungendo ad elementi esistenti... - if (currDispData == null) - { - currDispData = new newDisplayData(); - } - try - { - StringBuilder sb = new StringBuilder(); - sb.Append($"B_input --> {(short)B_input}{Environment.NewLine}"); - sb.Append($"{baseUtils.binaryForm(B_input)}{Environment.NewLine}"); - sb.Append($"{Environment.NewLine}"); - sb.Append($"----------- RAW Data Memory -----------{Environment.NewLine}"); - int i = 0; - foreach (var item in RawInput) - { - sb.Append($"B{i:00} --> {baseUtils.binaryForm(item)} = {(short)item}{Environment.NewLine}"); - i++; - } - sb.Append("-------------------------------"); - currDispData.currBitmap = sb.ToString(); - } - catch - { } - } - - /// - /// Salva valori indicati in prod data - /// - /// Item KVP di cui salvare i dati in currProdData come chiave/valore - /// - public void saveProdData(KeyValuePair item) - { - // imposto i valori... - if (currProdData.ContainsKey(item.Key)) - { - currProdData[item.Key] = item.Value; - } - else - { - currProdData.Add(item.Key, item.Value); - } - } - - #endregion variabili ed oggetti base - - #region Protected Fields - - /// - /// ultimo tentativo connessione... - /// - protected DateTime lastConnectTry; - - /// - /// indica se serva refresh parametri e quindi PLC... - /// - protected bool needRefresh = true; - - /// - /// Form chiamante - /// - protected AdapterForm parentForm; - - #endregion Protected Fields - - /// - /// Conf adapter corrente - /// - public IobConfiguration cIobConf; - - /// - /// Struttura memoria PLC x lettura/scrittura da JSON file - /// - public plcMemMap memMap; - - /// - /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto - /// - /// - /// - public IobGeneric(AdapterForm caller, IobConfiguration IOBConf) - { - if (IOBConf != null) - { - // init oggetto redis... - redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB); - - // initi oggetto TCMan - tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz); - - // salvo il form chiamante - parentForm = caller; - // configurazione... - cIobConf = IOBConf; - - lastConnectTry = DateTime.Now; - - // aggiungo nel logger IDX Macchina - lg = LogManager.GetCurrentClassLogger(); - - lgInfo("Avvio preliminare AdapterGeneric"); - // aggiungo altri defaults - setDefaults(true); - - setParamPlc(); - - // checkLogDir x shrink! - checkShrinkDir(); - - // concluso! - lgInfo("Istanziata classe preliminare IOBGeneric"); - } - else - { - lgError("Error: IobCOnf is null!"); - } - } - - /// - /// Valore limite MASSIMO di invio di dati come array Json - /// - protected int maxJsonData { get; set; } = utils.CRI("maxJsonData"); - - /// - /// Valore limite MASSIMO di invio di dati come array Json x EVENTI - /// - protected int maxJsonDataEv { get; set; } = utils.CRI("maxJsonDataEv"); - - /// - /// Valore MINIMO limite x decidere invio di dati come array Json - /// - protected int minJsonData { get; set; } = utils.CRI("minJsonData"); - - /// - /// Secondi standard x veto check status e log - /// - protected int vetoSeconds { get; set; } = utils.CRI("vetoSeconds"); - - /// - /// Imposta eventuali altri valori default - /// - private void fixDefaultPar() - { - // parametro max tentativi PING... - string s_maxPingRetry = getOptPar("MAX_PING_RETRY"); - if (!string.IsNullOrEmpty(s_maxPingRetry)) - { - int numRetry = 5; - int.TryParse(s_maxPingRetry, out numRetry); - maxPingRetry = numRetry; - } - } - - /// - /// Imposto alcuni valori di default - /// - /// indica se sia richeisto di SVUOTARE le code delel info - private void setDefaults(bool resetQueue) - { - numSim = utils.CRI("numSim"); - lastPrgName = ""; - nReadIN = 0; - nReadFilt = 0; - nSendOut = 0; - currMode = 0; - lastAlarm = ""; - doStartMemDump = utils.CRB("doStartMemDump"); - doSampleMemory = utils.CRB("doSampleMemory"); - // svuoto code se richiesto - if (resetQueue) - { - QueueIN = new ConcurrentQueue(); - QueueFLog = new ConcurrentQueue(); - QueueAlarm = new ConcurrentQueue(); - QueueMessages = new ConcurrentQueue(); - } - // imposto contatori blink a zero... - i_counters = new int[32]; - lastPeriodicLog = DateTime.Now; - // fix parametri generali... - enablePrgName = true; - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgError(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(message); - if (sendToForm) - { - sendToLogWatch("ERROR", message); - } - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgError(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(message, args); - sendToLogWatch("ERROR", message, args); - } - - /// - /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - /// - protected void lgError(Exception exception, string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Error(exception, message, args); - sendToLogWatch("ERROR", message, exception, args); - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgFatal(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(message); - if (sendToForm) - { - sendToLogWatch("FATAL", message); - } - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgFatal(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(message, args); - sendToLogWatch("FATAL", message, args); - } - - /// - /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - /// - protected void lgFatal(Exception exception, string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Fatal(exception, message, args); - sendToLogWatch("FATAL", message, exception, args); - } - - /// - /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - protected void lgInfo(string message, bool sendToForm = true) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Info(message); - if (sendToForm) - { - sendToLogWatch("INFO", message); - } - } - - /// - /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... - /// - /// - /// - protected void lgInfo(string message, params object[] args) - { - lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; - lg.Info(message, args); - sendToLogWatch("INFO", message, args); - } - - /// - /// Lettura memorie conf speciali (json) - /// ...SE inserito in [OPTPAR] come PARAM_CONF=nome.json - /// - protected virtual void loadMemConf() - { - lgInfo("BEGIN loadMemConf"); - // variabili x gestione send contapezzi in blocco - string currPar = getOptPar("ENABLE_SEND_PZC_BLOCK"); - if (!string.IsNullOrEmpty(currPar)) - { - bool.TryParse(currPar, out enableSendPzCountBlock); - // se abilitato leggo num pezzi da reinviare in blocco - if (enableSendPzCountBlock) - { - int.TryParse(getOptPar("MAX_SEND_PZC_BLOCK"), out maxSendPzCountBlock); - int.TryParse(getOptPar("MIN_SEND_PZC_BLOCK"), out minSendPzCountBlock); - } - } - else - { - lgError("loadMemConf: parametro ENABLE_SEND_PZC_BLOCK non trovato, verificare anche MAX_SEND_PZC_BLOCK e MIN_SEND_PZC_BLOCK"); - } - // inizializzo LUT decodifica - string jsonConf = getOptPar("PARAM_CONF"); - if (!string.IsNullOrEmpty(jsonConf)) - { - string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonConf}"; - lgInfo($"Apertura file {jsonFileName}"); - StreamReader reader = new StreamReader(jsonFileName); - string jsonData = reader.ReadToEnd(); - if (!string.IsNullOrEmpty(jsonData)) - { - lgInfo($"File json composto da {jsonData.Length} caratteri"); - try - { - memMap = JsonConvert.DeserializeObject(jsonData); - lgInfo($"Decodifica aree memMap: trovati {memMap.mMapRead.Count} valori TSVC"); - lgInfo($"Decodifica aree memMap: trovati {memMap.mMapWrite.Count} parametri "); - // se ho variabili read --> genero dati TSVC... - if (memMap.mMapRead.Count > 0) + get { - 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() + return baseUtils.CRB("DemoInSample"); + } + } + + /// + /// Verifica se sia in modalità DEMO x dati OUTPUT + /// + public static bool DemoOut + { + get + { + return utils.CRB("DemoOut"); + } + } + + /// + /// Indicazione VETO PING a server sino alla data-ora indicata + /// + public static DateTime dtVetoPing + { + get + { + return utils.dtVetoPing; + } + set + { + utils.dtVetoPing = value; + } + } + + /// + /// Indicazione VETO invio a server sino alla data-ora indicata + /// + public static DateTime dtVetoSend + { + get + { + return utils.dtVetoSend; + } + set + { + utils.dtVetoSend = value; + } + } + + /// + /// stato Online/Offline del server MP IO (su REDIS) + /// + public static bool MPOnline + { + get + { + return utils.MPIO_Online; + } + set + { + utils.MPIO_Online = value; + } + } + + /// + /// Contapezzi attuale + /// + public Int32 contapezziIOB + { + get + { + return tcMan.pzCountIOB; + } + set + { + tcMan.pzCountIOB = value; + } + } + + /// + /// Ultima lettura variabile contapezzi da CNC + /// + public Int32 contapezziPLC + { + get + { + return tcMan.pzCountPLC; + } + set + { + tcMan.pzCountPLC = value; + } + } + + /// + /// Contatore x invio dati FluxLog + /// + public int counterFLog { get; set; } + + /// + /// Contatore x invio dati SignalIN + /// + public int counterSigIN { get; set; } + + /// + /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA... + /// + public bool DemoIn + { + get + { + return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn"); + } + } + + /// + /// stato Online/Offline della IOB + /// + public bool IobOnline + { + get + { + return utils.IOB_Online; + } + set + { + utils.IOB_Online = value; + } + } + + /// + /// Log verboso da configurazione (SOLO CHAIVE "verbose"... + /// + public bool isVerboseLog { get; set; } = utils.CRB("verbose"); + + /// + /// Ultimo Alarm letto + /// + public string lastAlarm { get; set; } + + /// + /// Ultimo ARRAY DynData letto + /// + public Dictionary lastDynData { get; set; } = new Dictionary(); + + /// + /// Ultimo DynData (sunto) letto + /// + public string lastDynDataCtrlVal { get; set; } + + /// + /// Ultimo Override set letto + /// + public string lastOverrideFS { get; set; } + + /// + /// Ultimo Override set letto + /// + public string lastOverrideRapid { get; set; } + + /// + /// Ultimo programma letto + /// + public string lastPrgName { get; set; } + + /// + /// Ultimo SysInfo letto + /// + public string lastSysInfo { get; set; } + + /// + /// Ultimo URL + /// + public string lastUrl { get; set; } + + /// + /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...) + /// + public bool periodicLog + { + get + { + bool answ = false; + answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); + if (answ) { - Funzione = funz, - Period = periodo, - DTStart = DateTime.Now.AddHours(-1), - dataArray = new List() - }; - TSVC_Data.Add(item.Key, currConf); - } - // documento... - foreach (var item in TSVC_Data) - { - lgInfo($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}"); - // salvo i valori PREC... - LastTSVC.Add(item.Key, 0); - } - } - // infine se obj memoria valido salvo in MP-IO x sue applicazioni - if (memMap != null) - { - // invio su cloud conf memoria... - string rawData = JsonConvert.SerializeObject(memMap); - utils.callUrlNow($"{urlSaveMemMap}", rawData); - // salvo ANCHE come parametri i valori... - objItem currItem = new objItem(); - List allParam = new List(); - // valori WRITE - foreach (var item in memMap.mMapWrite) - { - currItem = new objItem() - { - uid = item.Value.name, - name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, - writable = true - }; - allParam.Add(currItem); - } - // valori READ - foreach (var item in memMap.mMapRead) - { - currItem = new objItem() - { - uid = item.Value.name, - name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, - writable = false - }; - allParam.Add(currItem); - } - // invio su cloud parametri! - rawData = JsonConvert.SerializeObject(allParam); - utils.callUrl($"{urlSaveAllParams}", rawData); - } - } - catch (Exception exc) - { - lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}"); - } - } - else - { - lgError("Errore in loadMemConf: file json vuoto!"); - } - reader.Dispose(); - } - else - { - lgInfo("loadMemConf: non trovata opzione PARAM_CONF in file INI"); - } - // loggo - lgInfo("DONE loadMemConf"); - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - protected void sendToLogWatch(string messType, string message) - { - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; - parentForm.updateFormDisplay(currDispData); - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - /// - protected void sendToLogWatch(string messType, string message, params object[] args) - { - try - { - string expString = string.Format(message, args); - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}"; - parentForm.updateFormDisplay(currDispData); - } - catch - { } - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - /// - /// - protected void sendToLogWatch(string messType, string message, Exception exception, params object[] args) - { - try - { - string expString = string.Format(message, args); - newDisplayData currDispData = new newDisplayData(); - currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}{Environment.NewLine}{exception}"; - parentForm.updateFormDisplay(currDispData); - } - catch - { } - } - - /// - /// Invia messaggio a logWatcher - /// - /// - /// - protected void sendToTaskWatch(string messType, string message) - { - parentForm.taskWatcher = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; - } - - /// - /// Impostazioni parametri PLC - /// - protected virtual void setParamPlc() - { - loadMemConf(); - fixDefaultPar(); - } - - #region metodi adapter - - protected bool _connOk = false; - - /// - /// Max tentativi ping permessi (default: 5) - /// - protected int maxPingRetry { get; set; } = 5; - - /// - /// test ping all'indirizzo PLC/CNC impostato nei parametri - /// - /// - protected IPStatus testPingMachine - { - get - { - IPStatus answ = IPStatus.Unknown; - // se disabilitato salto... - if (pingDisabled) - { - answ = IPStatus.Success; - } - else - { - IPAddress address; - PingReply reply; - using (Ping pingSender = new Ping()) - { - address = IPAddress.Loopback; - IPAddress.TryParse(cIobConf.cncIpAddr, out address); - int pingMsTimeout = cIobConf.pingMsTimeout; - reply = pingSender.Send(address, pingMsTimeout); - answ = reply.Status; - } - } - return answ; - } - } - - /// - /// Salva verifica stato connessione OK - /// - /// - public virtual bool connectionOk - { - get - { - return _connOk || DemoIn; - } - set - { - _connOk = value; - } - } - - /// - /// indica se ping disabilitato da optPar - /// - public bool pingDisabled - { - get - { - bool answ = false; - bool.TryParse(getOptPar("NO_PING"), out answ); - return answ; - } - } - - /// - /// Verifica e se necessario comprime directory log... - /// - private void checkShrinkDir() - { - string path = string.Format("{0}logs\\{1}", AppDomain.CurrentDomain.BaseDirectory, cIobConf.codIOB); - baseUtils.shrinkDir(path); - } - - private void reportDataProc() - { - // update valori visualizzazione... - parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut); - } - - private void svuotaCodaContapezzi() - { - // permetto al max 2 tentativi infruttuosi... - int maxTry = 2; - int oldContapezzi = contapezziIOB; - // se ho contapezzi OLTRE limite... - while ((MPOnline) && (contapezziPLC > contapezziIOB + minSendPzCountBlock)) - { - lgInfo($"Ciclo svuotaCodaContapezzi --> contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}"); - if (!isMulti) - { - pzCntReload(true); - } - // provo invio - trySendPzCountBlock(); - // verifica per evitare loop infinito invio fallito - if (oldContapezzi == contapezziIOB) - { - maxTry--; - } - else - { - maxTry = 2; - oldContapezzi = contapezziIOB; - } - // verifico maxTry: se li ho esauriti esco! - if (maxTry <= 0) - { - return; - } - // aspetto x dare tempo calcolo - Thread.Sleep(400); - } - } - - /// - /// Cerca di inviare su un altro thread i vari dati accumulati... - /// - private void trySendValues() - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - try - { - // verifico se risponde il server... - if (checkServerAlive) - { - bool iobOk = false; - if (utils.CRB("sendDataByThread")) - { - Task taskCheck = TaskEx.Run(() => iobOk = checkIobEnabled); - } - else - { - iobOk = checkIobEnabled; - } - // verifico SE posso inviare dati - if (iobOk) - { - currDispData.semOut = Semaforo.SV; - // verificare come gestire il task secondario senza interferenza (chiamate update su FORM da thread secondari danno errori) - if (utils.CRB("sendDataByThread")) - { - // invio con thread separato... - Task taskSigIN = TaskEx.Run(() => svuotaCodaSignIN()); - Task taskFlog = TaskEx.Run(() => svuotaCodaFLog()); - } - else - { - // gestione queue SignalIN (invio, display) - svuotaCodaSignIN(); - currDispData.counter = contapezziIOB; - raiseRefresh(currDispData); - // provo a svuotare coda contapezzi - svuotaCodaContapezzi(); - currDispData.counter = contapezziIOB; - raiseRefresh(currDispData); - // gestione queue FluxLog (invio, display) - svuotaCodaFLog(); - raiseRefresh(currDispData); - } - } - else - { - // mostro VETO-SEND x invio... GIALLO - currDispData.semOut = Semaforo.SG; - if (periodicLog) - { - lgInfo("IOB - VETO SEND"); - } - } - } - else - { - // mostro SERVER KO x invio... ROSSO - currDispData.semOut = Semaforo.SR; - if (periodicLog) - { - lgInfo("IOB - SERVER NOT READY"); - } - } - } - catch (Exception exc) - { - lgError($"Errore in fase trySendValues{Environment.NewLine}{exc}"); - currDispData.semOut = Semaforo.SR; - } - raiseRefresh(currDispData); - } - - /// - /// Stringa raw dei parametri da scrivere... - /// - /// - protected string getParams2write() - { - string answ = ""; - string url2call = $"{urlGetParams2Write}"; - if (verboseLog) - { - lgInfo("chiamata URL " + url2call); - } - answ = utils.callUrlNow(url2call); - // se vuoto faccio seconda prova... - if (string.IsNullOrEmpty(answ)) - { - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Chiede elenco dei task da eseguire - /// - formato Json - /// - array di KVP / Dictionary - /// - formato definito da API x MP/IO/: - /// - KEY: task - /// - VALUE: array JSon KVP - /// - protected string getTask2exe() - { - string answ = ""; - if (checkServerAlive) - { - string url2call = $"{urlGetTask2Exe}"; - if (verboseLog) - { - lgInfo("chiamata URL " + url2call); - } - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Metodo da overridare x scrivere DAVVERO i aprametri sul PLC - /// - /// - protected virtual void plcWriteParams(List updatedPar) - { - // non faccio nulla di base... - } - - /// - /// Processa le richieste di scrittura memoria - /// - /// - protected string processMemWriteRequests() - { - string answ = ""; - // li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC) - List writeList = new List(); - List updatedPar = new List(); - // recupero elenco delle cose da fare - string resp = getParams2write(); - if (!string.IsNullOrEmpty(resp)) - { - try - { - writeList = JsonConvert.DeserializeObject>(resp); - // se ho da fare chiamo esecuzione.. - if (writeList.Count > 0) - { - foreach (var item in writeList) - { - // scrivo in memoria - if (memMap.mMapWrite.ContainsKey(item.uid)) - { - memMap.mMapWrite[item.uid].value = item.reqValue; - // accodo in stringa taskVal... - answ += $" | Parameter {item.uid} --> {item.reqValue}"; - // sistemo valori - item.value = item.reqValue; - lgInfo($"Effettuato update parametro: actVal = {item.value} | reqVal = {item.reqValue}"); - item.reqValue = ""; - // salvo in lista da ritrasmettere - updatedPar.Add(item); - } - else - { - answ += $" | Error: parameter {item.uid} not found"; - } - } - // richiamo scrittura parametri su PLC - plcWriteParams(updatedPar); - // invio su cloud parametri! - string rawData = JsonConvert.SerializeObject(updatedPar); - utils.callUrl($"{urlUpdateWriteParams}", rawData); - } - } - catch (Exception exc) - { - lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); - } - } - else - { - lgError("Non è stata ricevuta risposta x task da eseguire"); - } - return answ; - } - - protected void raiseRefresh(newDisplayData currDispData) - { - if (currDispData.hasData) - { - // segnalo refresh! - if (eh_refreshed != null) - { - eh_refreshed(this, new iobRefreshedEventArgs(currDispData)); - } - } - } - - /// - /// Cancella dal server i task eseguiti - /// - /// - /// - /// - protected string remTask2exe(string taskName, string esitoTask) - { - string answ = ""; - if (checkServerAlive) - { - string url2call = $"{urlRemTask2Exe}{taskName}"; - lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}"); - answ = utils.callUrlNow(url2call); - } - return answ; - } - - /// - /// Invia informazioni associazione IOB 2 machine - /// - protected void sendM2IOB() - { - if (checkServerAlive) - { - lgInfo("chiamata URL " + urlSetM2IOB); - utils.callUrlNow(urlSetM2IOB); - } - } - - /// - /// Invia al server IO i valori dei parametri opzionali (es counters) - /// - /// Nome parametro - /// Valore parametro - protected void sendOptVal(string paramName, string paramValue) - { - if (checkServerAlive) - { - string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}"; - lgInfo("chiamata URL " + url2call); - utils.callUrlNow(url2call); - } - } - - /// - /// Invia al server IO i valori dei parametri opzionali (es counters) - /// - /// Nome parametro - /// Valore parametro INT - protected void sendOptVal(string paramName, int paramValueInt) - { - // override! - sendOptVal(paramName, paramValueInt.ToString()); - } - - /// - /// processa dataLayer e se necessario salva/mostra - /// - public static void checkSavePersDataLayer() - { - } - - public static void resetDebugConsole() - { - } - - /// - /// Esecuzione dei task richiesti e pulizia coda richieste eseguite - /// - /// - public virtual Dictionary executeTasks(Dictionary task2exe) - { - // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti... - Dictionary taskDone = new Dictionary(); - if (task2exe != null) - { - bool taskOk = false; - string taskVal = ""; - // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4 - foreach (var item in task2exe) - { - taskOk = false; - taskVal = ""; - // converto richiesta in enum... - taskType tName = taskType.nihil; - Enum.TryParse(item.Key, out tName); - // controllo sulla KEY... - switch (tName) - { - case taskType.nihil: - case taskType.fixStopSetup: - case taskType.forceSetPzCount: - case taskType.sendWatchDogMes2Plc: - taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; - lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.setArt: - case taskType.setComm: - case taskType.setProg: - case taskType.setPzComm: - // recupero dati da memMap... - if (memMap.mMapWrite.ContainsKey(item.Key)) - { - dataConf currMem = memMap.mMapWrite[item.Key]; - string addr = currMem.memAddr; - taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte"; - // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà - memMap.mMapWrite[item.Key].value = item.Value; - } - else - { - taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}"; - } - break; - - case taskType.forceResetPzCount: - // reset contapezzi inizio setup - taskOk = resetcontapezziPLC(); - taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.startSetup: - // reset contapezzi inizio setup - taskOk = resetcontapezziPLC(); - taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.stopSetup: - // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO - if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE") - { - taskOk = resetcontapezziPLC(); - } - taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC"; - lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - - case taskType.setParameter: - // richiedo da URL i parametri WRITE da popolare - lgInfo("Chiamata processMemWriteRequests"); - taskVal = processMemWriteRequests(); - // se restituiscce "" faccio altra prova... - if (string.IsNullOrEmpty(taskVal)) - { - // i parametri me li aspetto come stringa composta paramName|paramvalue - if (item.Value.Contains("|")) - { - string[] paramsJob = item.Value.Split('|'); - taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; + lastPeriodicLog = DateTime.Now; } - else - { - taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; - } - } - break; - default: - taskVal = "SKIPPED | NO EXEC"; - lgInfo($"Chiamata default senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); - break; - } - // aggiungo task! - taskDone.Add(item.Key, taskVal); + return answ; + } } - } - return taskDone; - } - /// - /// effettua recupero dati ed invio valori modificati... - /// - /// - public void getAndSend(gatherCycle ciclo) - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!! - try - { - trySendValues(); - } - catch (Exception exc) - { - lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria"); - currDispData.semOut = Semaforo.SR; - } - // controllo connessione/connettività - if (connectionOk) - { - // controllo non sia già in esecuzione... - if (!adpCommAct) + /// + /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN) + /// + public bool verboseLog { - // 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... + get + { + bool answ = false; + int logEvery = utils.CRI("logEvery"); + if (logEvery < 1) + { + logEvery = 10; + } + + answ = utils.CRB("verbose") && (nReadIN % logEvery == 0); + return answ; + } + } + + /// + /// Coda massima ammessa per FLog (se <=0 disattivata...) + /// + protected int maxQueueFLog { get; set; } = utils.CRI("maxQueueFLog"); + + /// + /// Numero letture IN da avvio + /// + protected int nReadFilt { get; set; } + + /// + /// Numero letture IN da avvio + /// + protected int nReadIN { get; set; } + + /// + /// Numero invii OUT (svuotamento coda) + /// + protected int nSendOut { get; set; } + + /// + /// Numero simulazioni ammesse... + /// + protected int numSim { get; set; } + + /// + /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati + /// + public virtual void reportRawInput(ref newDisplayData currDispData) + { + // processo eventualmente aggiungendo ad elementi esistenti... + if (currDispData == null) + { + currDispData = new newDisplayData(); + } try { - bool showDebugData = false; - if (ciclo == gatherCycle.VHF) - { - processVHF(); - } - // processing dati memoria (lettura, filtraggio, enqueque) - else if (ciclo == gatherCycle.HF) - { - processWhatchDog(); - processAllMemory(); - processMode(); - } - else if (ciclo == gatherCycle.MF) - { - processServerRequests(); - processOverride(); - processContapezzi(); - processCncAlarms(); - processDynData(); - } - else if (ciclo == gatherCycle.LF) - { - processOtherCounters(); - processProgram(); - // verifico se devo gestire cambio ODL in modo automatico - processAutoOdl(); - } - else if (ciclo == gatherCycle.VLF) - { - if (utils.CRB("enableContapezzi")) + StringBuilder sb = new StringBuilder(); + sb.Append($"B_input --> {(short)B_input}{Environment.NewLine}"); + sb.Append($"{baseUtils.binaryForm(B_input)}{Environment.NewLine}"); + sb.Append($"{Environment.NewLine}"); + sb.Append($"----------- RAW Data Memory -----------{Environment.NewLine}"); + int i = 0; + foreach (var item in RawInput) { - // rilettura contapezzi da server... - lgInfo("Ciclo VLF: pzCntReload(true)"); - if (!isMulti) - { - pzCntReload(true); - } - // refresh associazione Macchina - IOB - sendM2IOB(); + sb.Append($"B{i:00} --> {baseUtils.binaryForm(item)} = {(short)item}{Environment.NewLine}"); + i++; } - // recupero dati SETUP (sysinfo) e li invio/mostro se variati... - processSysInfo(); - // checkLogDir x shrink! - checkShrinkDir(); - // eventuale log! - if (utils.CRB("recTime")) - { - logTimeResults(); - } - } - // mostra eventuali altri dati di processo... - reportDataProc(); - if (showDebugData) - { - // verifica se debba salvare e mostrare dati - checkSavePersDataLayer(); - } - } - catch (Exception exc) - { - // segnalo eccezione e indico disconnesso... - lgError(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc)); - parentForm.fermaAdapter(true, false, true); - } - // tolgo flag running - adpCommAct = false; - } - else - { - if (periodicLog) - { - lgInfo("ADP not running..."); - } - } - } - else - { - // log ADP running - lgError("Non eseguo chiamata: ADP ancora in running"); - // se è bloccato da oltre maxSec lo sblocco... - if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) - { - // tolgo flag running - adpCommAct = false; - adpStartRun = DateTime.Now; - } - } - } - else - { - // provo a riconnettere SE abilitato tryRestart... - if (adpTryRestart && !connectionOk) - { - // controllo se sia scaduto periodi di veto al tryConnect... - int waitRecMSec = utils.CRI("waitRecMSec") * 2; - DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); - if (DateTime.Now > dtVeto) - { - lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect"); - lastConnectTry = DateTime.Now; - tryConnect(); - } - } - currDispData.semIn = Semaforo.SR; - } - raiseRefresh(currDispData); - } - - /// - /// riporta il log di tutti i dati di results temporali registrati - /// - public void logTimeResults() - { - if (TimingData.results.Count > 0) - { - lgInfo("{0}--------------- START TIMING DATA ---------------", Environment.NewLine); - int globNumCall = 0; - TimeSpan globAvgMsec = new TimeSpan(0); - foreach (TimeRec item in TimingData.results) - { - // loggo SOLO se del mio IOB corrente... - if (item.classCall == cIobConf.codIOB) - { - lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); - globNumCall += item.numCall; - globAvgMsec += item.totMsec; - } - } - // riporto conteggio medio al secondo... - lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); - lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine); - // mostro in form statistiche globali! - parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds)); - } - } - - /// - /// Effettua ciclo controllo richieste server - /// - public void processServerRequests() - { - Dictionary task2exe = new Dictionary(); - Dictionary taskDone = new Dictionary(); - // recupero elenco delle cose da fare - string resp = getTask2exe(); - if (!string.IsNullOrEmpty(resp)) - { - try - { - task2exe = JsonConvert.DeserializeObject>(resp); - // se ho da fare chiamo esecuzione.. - if (task2exe.Count > 0) - { - taskDone = processTask(task2exe); - } - } - catch (Exception exc) - { - lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); - } - } - } - - public Dictionary processTask(Dictionary task2exe) - { - Dictionary taskDone = new Dictionary(); - if (task2exe != null) - { - lgInfo($"Task2Exe: trovati {task2exe.Count} task da eseguire, procedo"); - // chiamo procedura esecutiva (diversa x ogni IOB) - taskDone = executeTasks(task2exe); - // loggo tutti i task done... - foreach (var item in taskDone) - { - sendToTaskWatch(item.Key, item.Value); - } - // ora chiamo la cancellazione dei task eseguiti... - foreach (var item in taskDone) - { - remTask2exe(item.Key, item.Value); - } - } - return taskDone; - } - - /// - /// Effettua rilettura del contapezzi dal server MP/IO - /// - /// Forza rilettura da DB tempi ciclo rilevati - public void pzCntReload(bool forceCountRec) - { - // legge da IO server ULTIMO valore CONTPEZZI al riavvio... - string currServerCount = ""; - string lastIdxODL = ""; - if (checkServerAlive) - { - // leggo PRIMA ODL .... - lastIdxODL = utils.callUrl(urlGetCurrODL); - lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL); - // se ho valori in coda da trasmettere uso dati REDIS - if (forceCountRec) - { - // uso dati da TCiclo registrati... - currServerCount = utils.callUrl(urlGetPzCountRec); - lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount); - } - else - { - // uso il contapezzi dichiarato dall'IOB stesso - currServerCount = utils.callUrl(urlGetPzCount); - lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount); - } - // controllo: SE NON HO ODL... - if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0") - { - // NON AGGIORNO - contapezziIOB = contapezziPLC; - lgInfo($"Errore lettura ODL (vuoto) resta tutto invariato contapezzi: {contapezziIOB} | contapezziPLC {contapezziPLC}"); - } - else - { - if (!string.IsNullOrEmpty(currServerCount)) - { - // se "-1" resto a ultimo... - if (currServerCount != "-1") - { - int newVal = -1; - Int32.TryParse(currServerCount, out newVal); - contapezziIOB = newVal > -1 ? newVal : contapezziIOB; - lgInfo("Ricevuta conferma da server di {0} pezzi registrati per ODL", currServerCount); - } - else - { - // NON AGGIORNO - contapezziIOB = contapezziPLC; - lgInfo($"Errore lettura contapezzi (-1) - uso contapezziPLC --> {contapezziPLC}"); - } - } - else - { - // registro che ho UN NUOVO ODL - lgInfo($"Lettura ODL in pzCntReload, currIdxODL {currIdxODL} --> lastIdxODL {lastIdxODL}"); - // se ODL differente e NUOVO è zero --> resetto! - if (currIdxODL.ToString() != lastIdxODL && lastIdxODL == "0") - { - // cambiato ODL quindi reset... - contapezziIOB = 0; - lgInfo("Nuovo ODL==0, RESET contapezzi (-->ZERO)"); - } - // provo a salvare nuovo ODL - int.TryParse(lastIdxODL, out currIdxODL); - } - } - } - else - { - // se server NON pronto... - contapezziIOB = contapezziPLC; - lgError("Errore server NON pronto in pzCntReload"); - } - } - - /// - /// Metodo generico di reset contapezzi... - /// - /// - public virtual bool resetcontapezziPLC() - { - return false; - } - - /// - /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi... - /// - /// Pezzi richiesti - /// - public virtual bool setcontapezziPLC(int newPzCount) - { - return false; - } - - /// - /// Avvia l'adapter sulla porta richiesta - /// - /// indica se sia richeisto di SVUOTARE le code delel info - public virtual void startAdapter(bool resetQueue) - { - lgInfo("Starting adapter..."); - maxJsonData = utils.CRI("maxJsonData"); - maxJsonDataEv = utils.CRI("maxJsonDataEv"); - parentForm.commPlcActive = false; - adpRunning = true; - dtAvvioAdp = DateTime.Now; - lastWatchDog = dtAvvioAdp; - lastPING = dtAvvioAdp; - lastReadPLC = dtAvvioAdp.AddMinutes(-1); - lastDisconnCheck = dtAvvioAdp; - TimingData.resetData(); - // aggiungo altri defaults - setDefaults(resetQueue); - adpTryRestart = true; - parentForm.displayTaskAndLog("Adapter Started!"); - } - - /// - /// ferma l'adapter... - /// - /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato in automatico) - /// indica se sia richeisto di SVUOTARE le code delel info - public virtual void stopAdapter(bool tryRestart, bool forceDequeue) - { - if (forceDequeue) - { - // svuoto le code dei valori letti e non ancora trasmessi... - parentForm.displayTaskAndLog("Svuotamento FORZATO coda segnali..."); - while (QueueIN.Count > 0) - { - // INVIO COMUNQUE...!!! - string valore = ""; - QueueIN.TryDequeue(out valore); - sendToMoonPro(urlType.SignIN, valore); - } - parentForm.displayTaskAndLog("Svuotamento FORZATO coda FluxLOG..."); - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > minJsonData) - { - while (QueueFLog.Count > 0) - { - List listaValori = new List(); - // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) - { - string currVal = ""; - // prendoi primi maxJsonDataValori - for (int i = 0; i < maxJsonData; i++) - { - QueueFLog.TryDequeue(out currVal); - listaValori.Add(currVal); - } - sendDataBlock(urlType.FLog, listaValori); - } - else - { - // invio in blocco - listaValori = QueueFLog.ToList(); - // invio - sendDataBlock(urlType.FLog, listaValori); - // svuoto! - QueueFLog = new ConcurrentQueue(); - } - } - // HO FINITO invio di FLog... - } - else - { - string currVal = ""; - while (QueueFLog.Count > 0) - { - // INVIO COMUNQUE...!!! - QueueFLog.TryDequeue(out currVal); - sendToMoonPro(urlType.FLog, currVal); - } - } - } - parentForm.displayTaskAndLog("Stopping adapter..."); - adpTryRestart = false; - - parentForm.displayTaskAndLog("Stopping adapter - last periodic data read..."); - - // chiudo la connessione all'adapter... - tryDisconnect(); - dtStopAdp = DateTime.Now; - adpTryRestart = tryRestart; - adpRunning = false; - // chiudo! - parentForm.displayTaskAndLog("Adapter Stopped."); - parentForm.commPlcActive = false; - } - - /// - /// Metodo base connessione... - /// - public virtual void tryConnect() - { - dtAvvioAdp = DateTime.Now; - } - - /// - /// Metodo base disconnessione... - /// - public virtual void tryDisconnect() - { - } - - #endregion metodi adapter - - #region layer persistenza dati - - /// - /// Dizionario di persistenza per i valori da salvare da/su file - /// - public Dictionary persistenceLayer; - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) - /// - /// - /// - private string getStoredVal(string keyVal) - { - string value = ""; - try - { - if (persistenceLayer != null) - { - if (!persistenceLayer.TryGetValue(keyVal, out value)) - { - persistenceLayer.Add(keyVal, "0"); - } - } - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc)); - } - return value; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come double - /// - /// - /// - private double getStoredValDouble(string keyVal) - { - double answ = 0; - try - { - answ = Convert.ToDouble(getStoredVal(keyVal)); - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc)); - } - answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come INT - /// - /// - /// - private long getStoredValLong(string keyVal) - { - long answ = 0; - try - { - answ = Convert.ToInt64(getStoredVal(keyVal)); - } - catch - { } - // verifico che il valore sia minore di 9/10 del valore massimo... - answ = (answ < (long.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// recupera valore salvato in persistence layer (se non c'è crea...) come UINT - /// - /// - /// - private uint getStoredValUInt(string keyVal) - { - uint answ = 0; - try - { - answ = Convert.ToUInt32(getStoredVal(keyVal)); - } - catch (Exception exc) - { - lgError(string.Format("Eccezione in getStoredValUInt: {0}{1}", Environment.NewLine, exc)); - } - // verifico che il valore sia minore di 9/10 del valore massimo... - answ = (answ < (uint.MaxValue / 10 * 9)) ? answ : 0; - return answ; - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private double updateValDoubleByIncr(int i, double delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - double contAct = getStoredValDouble(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private long updateValLongByIncr(int i, long delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - long contAct = getStoredValLong(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - /// - /// Aggiorna un valore del dizionario in SOSTITUZIONE - /// - /// - /// - /// - /// Nuovo valore incrementato - private void updateValString(int i, string newVal, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // salvo in ram! - persistenceLayer[keyVal] = newVal; - } - - /// - /// Aggiorna un valore del dizionario in SOSTITUZIONE e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private void updateValUInt(int i, uint newVal, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // salvo in ram! - persistenceLayer[keyVal] = newVal.ToString(); - } - - /// - /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce - /// - /// - /// - /// - /// Nuovo valore incrementato - private uint updateValUIntByIncr(int i, uint delta, string searchString) - { - // stringa da cercare.. - string keyVal = string.Format(searchString, i + 1); - // recupero valore precedente... - uint contAct = getStoredValUInt(keyVal); - // nuovo valore... - contAct += delta; - // salvo in ram! - persistenceLayer[keyVal] = contAct.ToString(); - // rendo il valore! - return contAct; - } - - #endregion layer persistenza dati - - #region area lettura configurazioni - - /// - /// Decodifica file MAP (caso .bit) - /// - /// - /// - /// indirizzo Byte: indirizzo di partenza memoria - /// dimensione singolo slot in byte - /// indirizzo bit: numero riga x calcolo indice bit - /// - protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - int resto = 0; - Math.DivRem(BitNum, 8, out resto); - string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - - /// - /// Decodifica file MAP generico - /// - /// - /// - /// - /// - /// - /// - protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - - /// - /// Legge il file di conf di una MAP di informazioni da gestire con lettura set memoria - /// - /// nome vettore memoria - /// file origine - /// dimensione (in byte) della memoria - /// dimensione (in byte) della memoria - protected void loadConfFile(ref otherData[] vettoreConf, string nomeFile, int memSize, ref int numVett) - { - otherData lastData = new otherData(); - int totRighe = 0; - string linea; - totRighe = File.ReadLines(nomeFile).Count(); - // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco... - vettoreConf = new otherData[File.ReadLines(nomeFile).Count()]; - // carica da file... - StreamReader file = new StreamReader(nomeFile); - // leggo 1 linea alla volta... - int numRiga = 0; - int bitNum = 0; - int byteNum = 0; - while ((linea = file.ReadLine()) != null) - { - // SE non è un commento... - if (linea.Substring(0, 1) != "#") - { - // se finisce per BIT allora processo bit-a-bit... - if (linea.EndsWith("BOOL")) - { - try - { - string[] memIdx = linea.Split(utils.CRC("testCharSep"))[0].Split('.'); - // calcolo bit e byte number... - int.TryParse(memIdx[0], out byteNum); - if (memIdx.Length > 1) - { - int.TryParse(memIdx[1], out bitNum); - } - else - { - bitNum = 0; - } - } - catch - { - byteNum = 0; - bitNum = 0; - } - lastData = decodeBitData(linea, utils.CRC("testCharSep"), byteNum, 1, bitNum); - vettoreConf[numRiga] = lastData; - } - else - { - lastData = decodeOtherData(linea, utils.CRC("testCharSep"), "", 1, memSize); - vettoreConf[numRiga] = lastData; - } - numRiga++; - } - } - // salvo lunghezza file... - try - { - numVett = Convert.ToInt32(lastData.memAddr) + 1; - } - catch - { - numVett = numRiga + 1; - } - // chiudo file - file.Close(); - // ora trimmo vettore al solo numero VERO dei valori caricati... - Array.Resize(ref vettoreConf, numRiga); - - if (isVerboseLog) - { - lgInfo(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile)); - } - } - - /// - /// Cerca parametri opzionali in modalità "like" del nome - /// - /// - /// - public Dictionary findOptPar(string keyStartSearch = "") - { - Dictionary answ = new Dictionary(); - // controllo SE keySearch !="" - if (!string.IsNullOrWhiteSpace(keyStartSearch)) - { - if (cIobConf.optPar.Count > 0) - { - // ciclo su tutti e cerco occorrenze che INIZINO... - foreach (var item in cIobConf.optPar) - { - if (item.Key.StartsWith(keyStartSearch)) - { - answ.Add(item.Key, item.Value); - } - } - } - } - return answ; - } - - /// - /// Cerca se esiste il parametro opzionale e lo restituisce - /// - /// - /// - public string getOptPar(string key) - { - string answ = ""; - if (cIobConf.optPar.Count > 0) - { - // controllo SE HO il parametro - if (cIobConf.optPar.ContainsKey(key)) - { - answ = cIobConf.optPar[key]; - } - } - return answ; - } - - #endregion area lettura configurazioni - - #region IOB METHODS - - /// - /// contatore x simulazione valori input - /// - public int countSim = 0; - - /// - /// DateTime Ultimo valore simulazione generato - /// - public DateTime lastSim; - - /// - /// Verifica se la IOB sia ENABLED (da server o Demo) - /// - private bool checkIobEnabled - { - get - { - bool answ = false; - // controllo se ho veto al check... - if (dtVetoCheckIOB < DateTime.Now) - { - if (DemoOut) - { - answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); - } - else - { - try - { - // chiamo URL, se restituisce "OK" è enabled! - string callResp = callUrl(urlIobEnabled, true); - answ = (callResp == "OK"); - // attesa casuale se necessario - var rand = new Random(); - // primi 2 test - int maxTry = 2; - while (maxTry > 0 && !answ) - { - Thread.Sleep(rand.Next(250, 500)); - callResp = callUrl(urlIobEnabled, true); - answ = (callResp == "OK"); - maxTry--; - } - // se NON OK riprovo ANCORA 1 volta... - if (!answ) - { - resetWebClients(); - Thread.Sleep(rand.Next(250, 1000)); - callResp = callUrl(urlIobEnabled, false); - answ = (callResp == "OK"); - } - // altri 2 - maxTry = 2; - while (maxTry > 0 && !answ) - { - Thread.Sleep(rand.Next(250, 500)); - callResp = callUrl(urlIobEnabled, false); - answ = (callResp == "OK"); - maxTry--; - } - // salvo status... - IobOnline = answ; - // se online imposto veto check a 5 x tempo reinvio... - if (answ) - { - lastIobOnline = DateTime.Now; - } - dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); + sb.Append("-------------------------------"); + currDispData.currBitmap = sb.ToString(); } catch { } - } - // verifico SE è variato stato online/offline... - if (IobOnline != answ) - { - // se ORA sono online riporto... - if (answ) + } + + /// + /// Salva valori indicati in prod data + /// + /// Item KVP di cui salvare i dati in currProdData come chiave/valore + /// + public void saveProdData(KeyValuePair item) + { + // imposto i valori... + if (currProdData.ContainsKey(item.Key)) { - lgInfo("IOB ONLINE for server MP/IO"); + currProdData[item.Key] = item.Value; } else { - lgInfo("IOB OFFLINE for server MP/IO"); + currProdData.Add(item.Key, item.Value); } - } - // fix colore - if (answ) - { - parentForm.commSrvActive = 2; - } - else - { - parentForm.commSrvActive = 1; - } } - else - { - // altrimenti passo ultimo valore noto - answ = IobOnline; - } - return answ; - } - } - /// - /// test ping all'indirizzo impostato nei parametri - /// - /// - private IPStatus testPingServer - { - get - { - IPStatus answ = IPStatus.Unknown; ; - IPAddress address; - PingReply reply; - using (Ping pingSender = new Ping()) + #endregion variabili ed oggetti base + + #region Protected Fields + + /// + /// ultimo tentativo connessione... + /// + protected DateTime lastConnectTry; + + /// + /// indica se serva refresh parametri e quindi PLC... + /// + protected bool needRefresh = true; + + /// + /// Form chiamante + /// + protected AdapterForm parentForm; + + #endregion Protected Fields + + /// + /// Conf adapter corrente + /// + public IobConfiguration cIobConf; + + /// + /// Struttura memoria PLC x lettura/scrittura da JSON file + /// + public plcMemMap memMap; + + /// + /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto + /// + /// + /// + public IobGeneric(AdapterForm caller, IobConfiguration IOBConf) { - address = IPAddress.Loopback; - int maxRetry = maxPingRetry + 1; - int numRetry = 1; ; - string ipAdrr = cIobConf.serverData.MPIP.Replace("http://", "").Replace("https://", ""); - IPAddress.TryParse(ipAdrr, out address); - reply = pingSender.Send(address, pingServerMsTimeout); - // se ho timeout riprovo... - while (reply.Status != IPStatus.Success && numRetry < maxRetry) - { - lgInfo($"Ping KO | reply: {reply.Status} --> retry"); - reply = pingSender.Send(address, pingServerMsTimeout * numRetry / 2); - numRetry++; - if (reply.Status == IPStatus.Success) + if (IOBConf != null) { - lgInfo("PING OK!"); - break; - } - } - } - answ = reply.Status; - return answ; - } - } + // init oggetto redis... + redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB); - /// - /// Valore del num max invii consecutivi da coda... - /// - protected static int nMaxSend - { - get - { - int answ = 5; - try - { - answ = utils.CRI("nMaxSend"); - } - catch - { } - return answ; - } - } + // initi oggetto TCMan + tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz); - /// - /// 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 = false; - } - else - { - IPStatus pingStatus = testPingServer; - // se passa il ping faccio il resto... - if (pingStatus == IPStatus.Success) - { - string callResp = ""; - try - { - // chiamo URL, se restituisce "OK" è alive! - 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); - } - else - { - lgInfo("SERVER OFFLINE in checkServerAlive"); - parentForm.commSrvActive = 0; - } - // salvo nuovo status... - MPOnline = answ; - } - else - { - // allungo periodo controllo... - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); - } + // salvo il form chiamante + parentForm = caller; + // configurazione... + cIobConf = IOBConf; + + lastConnectTry = DateTime.Now; + + // aggiungo nel logger IDX Macchina + lg = LogManager.GetCurrentClassLogger(); + + lgInfo("Avvio preliminare AdapterGeneric"); + // aggiungo altri defaults + setDefaults(true); + + setParamPlc(); + + // checkLogDir x shrink! + checkShrinkDir(); + + // concluso! + lgInfo("Istanziata classe preliminare IOBGeneric"); } else { - lgInfo($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})"); - MPOnline = false; - // imposto veto a 10 volte reinvio dati standard... - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); - utils.dtVetoSend = dtVetoPing; + lgError("Error: IobCOnf is null!"); } - } } - else - { - // altrimenti passo ultimo valore noto... - answ = MPOnline; - } - return answ; - } - } - /// - /// Verifica se sia machcina multi = DoppioPallet da CONF - /// - public bool isMulti - { - get - { - bool answ = false; - if (cIobConf.optPar.Count > 0) - { - // cerco con chaive reale IS_MULTI - string keyName = "IS_MULTI"; - if (!cIobConf.optPar.ContainsKey(keyName)) - { - // legacy: accetto anche SIM_MULTI... - keyName = "SIM_MULTI"; - } - // vera verifica su chaive... - if (cIobConf.optPar.ContainsKey(keyName)) - { - string SIM_MULTI = getOptPar(keyName); - answ = SIM_MULTI == "1"; - } - } - return answ; - } - } + /// + /// Valore limite MASSIMO di invio di dati come array Json + /// + protected int maxJsonData { get; set; } = utils.CRI("maxJsonData"); - /// - /// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)... - /// - public string urlAddPzCount - { - get - { - string answ = ""; - try - { - answ = string.Format(@"http://{0}{1}{2}/savePzCountInc/{3}?qty=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlAddPzCount"); - } - return answ; - } - } + /// + /// Valore limite MASSIMO di invio di dati come array Json x EVENTI + /// + protected int maxJsonDataEv { get; set; } = utils.CRI("maxJsonDataEv"); - /// - /// URL per check alive... - /// - public string urlAlive - { - get - { - return string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE); - } - } + /// + /// Valore MINIMO limite x decidere invio di dati come array Json + /// + protected int minJsonData { get; set; } = utils.CRI("minJsonData"); - /// - /// URL per forzare split ODL... - /// - public string urlForceSplit - { - get - { - return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_FORCLE_SPLIT_ODL, cIobConf.codIOB); - } - } + /// + /// Secondi standard x veto check status e log + /// + protected int vetoSeconds { get; set; } = utils.CRI("vetoSeconds"); - /// - /// URL per salvataggio contapezzi... - /// - public string urlGetCurrODL - { - get - { - string answ = ""; - try + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgError(string message, bool sendToForm = true) { - answ = string.Format(@"http://{0}{1}{2}/getCurrODL/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlGetCurrODL"); - } - return answ; - } - } - - /// - /// URL per richiamo parametri da scrivere... - /// - public string urlGetParams2Write - { - get - { - string answ = ""; - try - { - answ = string.Format(@"http://{0}{1}{2}/getObjItems2Write/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, 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 = $@"http://{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 = $@"http://{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 = string.Format(@"http://{0}{1}{2}/getTask2Exe/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlGetTask2Exe"); - } - return answ; - } - } - - /// - /// URL per recupero idle time IOB... - /// - public string urlIdleTime - { - get - { - return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_IDLE_TIME, cIobConf.codIOB); - } - } - - /// - /// URL per recupero inizio ODL... - /// - public string urlInizioOdlIob - { - get - { - return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_ODL_STARTED, cIobConf.codIOB); - } - } - - /// - /// URL per check se abilitato... - /// - public string urlIobEnabled - { - get - { - return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDENABLED, cIobConf.codIOB); - } - } - - /// - /// URL per segnalazione reboot... - /// - public string urlReboot - { - get - { - string answ = ""; - try - { - answ = string.Format(@"http://{0}{1}{2}{3}&mac={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDREBO, cIobConf.codIOB, GetMACAddress()); - } - catch - { - answ = string.Format(@"http://{0}{1}{2}{3}", 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 = string.Format(@"http://{0}{1}{2}/remTask2Exe/{3}?taskName=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); - } - 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; - string apiCall = "setObjItems"; - answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, 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 = string.Format(@"http://{0}{1}{2}/saveConf/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlSaveMemConf"); - } - return answ; - } - } - - /// - /// URL per salvataggio dati associazione Machine 2 IOB... - /// - public string urlSetM2IOB - { - get - { - string answ = ""; - try - { - string machineName = Environment.MachineName; - answ = string.Format(@"http://{0}{1}{2}/setM2IOB/{3}?IOB_name={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB, 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 = string.Format(@"http://{0}{1}{2}/addOptPar/{3}?", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, 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 = $@"http://{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 = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/MAC_{cIobConf.codIOB}?counter="; - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlSetPzCountMAC"); - } - return answ; - } - } - - /// - /// URL per salvataggio in UPSERT dei PARAMETRI IOB scritti... - /// - public string urlUpdateWriteParams - { - get - { - string answ = ""; - try - { - string machineName = Environment.MachineName; - string apiCall = "upsertObjItems"; - answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, cIobConf.codIOB); - } - catch (Exception exc) - { - lgError(exc, "Errore in composizione urlSaveMemConf"); - } - return answ; - } - } - - /// - /// Esegue filtraggio dati x bit blinking!!! - /// - private void filterData() - { - // effettuo filtraggio dei valori letti... inizializzo OUT! - B_output = 0; - // in primis verifico SE ci siano bit blinkng... se non ci sono OUT=IN... - if (cIobConf.BLINK_FILT == 0) - { - B_output = B_input; - } - else - { - // incomincio con i valori NON blinking: questi "passano invariati", inizio a sommare nel valore OUT... - B_output = B_input & ~cIobConf.BLINK_FILT; - // calcolo il valore dei BIT che "passano la maschera" - int iBlink = B_input & cIobConf.BLINK_FILT; - // ...aggiungo i "bit che passano" - B_output += iBlink; - - // calcolo QUALI valori (tra quelli blink) siano PASSATI da 0 a 1 --> init counters... - BitArray bBlinkStart = new BitArray(new byte[] { Convert.ToByte(iBlink) }); - int[] bitsUp = bBlinkStart.Cast().Select(bit => bit ? 1 : 0).ToArray(); - for (int i = 0; i < bitsUp.Length; i++) - { - // SE 1... impostiamo contatori al MAX - if (bitsUp[i] == 1) - { - // se era zero indico START blink... - if (i_counters[i] == 0) + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(message); + if (sendToForm) { - lgInfo("START BLINK: B{0}", i); + sendToLogWatch("ERROR", message); } - // 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++) + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgError(string message, params object[] args) { - // se era a zero (invertito...) - if (bitsDown[i] == 1) - { - // SE è in corso il conteggio... - if (i_counters[i] > 0) + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(message, args); + sendToLogWatch("ERROR", message, args); + } + + /// + /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + /// + protected void lgError(Exception exception, string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Error(exception, message, args); + sendToLogWatch("ERROR", message, exception, args); + } + + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgFatal(string message, bool sendToForm = true) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(message); + if (sendToForm) { - // decremento! - i_counters[i] -= 1; - // se è zero NON faccio nulla, altrimenti SOMMO... - if (i_counters[i] > 0) - { - B_output += 1 << i; - } - else - { - lgInfo("END BLINK: B{0}", i); - } + sendToLogWatch("FATAL", message); } - } } - } - } - /// - /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... - /// - private void processAllMemory() - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // in primis SALVO valori previous/precedenti - B_previous = B_output; - // poi faccio lettura NUOVI valori - readAllData(ref currDispData); - // eseguo il filtering dei valori (per i bit "blinking") - filterData(); - // effettuo confronto valori vecchi/nuovi... SE trovo variazione OPPURE se è passato + di un timeout di controllo... - if (B_output != B_previous) - { - accodaSigIN(ref currDispData); - } - raiseRefresh(currDispData); - } - - /// - /// Effettua gestioen programma: legge e mostra su display... - /// - private void processProgram() - { - string currPrgName = ""; - // se abilitata lettura prgName - if (enablePrgName) - { - if (connectionOk) + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgFatal(string message, params object[] args) { - currPrgName = getPrgName(); + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(message, args); + sendToLogWatch("FATAL", message, args); } - 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) + /// + /// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + /// + protected void lgFatal(Exception exception, string message, params object[] args) { - currSysInfo = getSysInfo(); + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Fatal(exception, message, args); + sendToLogWatch("FATAL", message, exception, args); } - else - { - lgError("Errore connessione mancante x getSysInfo"); - } - // verifico SE sia cambiato il programma... - if (lastSysInfo != currSysInfo["SYSINFO"]) - { - // salvo! - lastSysInfo = currSysInfo["SYSINFO"]; - // per ogni valore del dizionario mostro ed accodo! - string sVal = ""; - foreach (var item in currSysInfo) - { - sVal = string.Format("[SYSINFO]{0}|{1}", item.Key, item.Value); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); - } - } - } - } - /// - /// Processo la coda FLog... - /// - private void svuotaCodaFLog() - { - //controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! - if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) - { - string wdStatus = "elapsed"; - string sVal = string.Format("[WDST]{0}", wdStatus); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); - lastWatchDog = DateTime.Now; - } - // verifico SE la coda abbia dei valori... - if (QueueFLog.Count > 0) - { - // invio pacchetto di dati (max da conf) - for (int i = 0; i < nMaxSend; i++) + /// + /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + protected void lgInfo(string message, bool sendToForm = true) { - // SE ho qualcosa in coda... - if (QueueFLog.Count > 0) - { - string currVal = ""; - if (MPOnline) + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Info(message); + if (sendToForm) { - if (IobOnline) - { - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > 1) + sendToLogWatch("INFO", message); + } + } + + /// + /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere... + /// + /// + /// + protected void lgInfo(string message, params object[] args) + { + lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB; + lg.Info(message, args); + sendToLogWatch("INFO", message, args); + } + + /// + /// Lettura memorie conf speciali (json) + /// ...SE inserito in [OPTPAR] come PARAM_CONF=nome.json + /// + protected virtual void loadMemConf() + { + lgInfo("BEGIN loadMemConf"); + // variabili x gestione send contapezzi in blocco + string currPar = getOptPar("ENABLE_SEND_PZC_BLOCK"); + if (!string.IsNullOrEmpty(currPar)) + { + bool.TryParse(currPar, out enableSendPzCountBlock); + // se abilitato leggo num pezzi da reinviare in blocco + if (enableSendPzCountBlock) { - 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++) + int.TryParse(getOptPar("MAX_SEND_PZC_BLOCK"), out maxSendPzCountBlock); + int.TryParse(getOptPar("MIN_SEND_PZC_BLOCK"), out minSendPzCountBlock); + } + } + else + { + lgError("loadMemConf: parametro ENABLE_SEND_PZC_BLOCK non trovato, verificare anche MAX_SEND_PZC_BLOCK e MIN_SEND_PZC_BLOCK"); + } + // inizializzo LUT decodifica + string jsonConf = getOptPar("PARAM_CONF"); + if (!string.IsNullOrEmpty(jsonConf)) + { + string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonConf}"; + lgInfo($"Apertura file {jsonFileName}"); + StreamReader reader = new StreamReader(jsonFileName); + string jsonData = reader.ReadToEnd(); + if (!string.IsNullOrEmpty(jsonData)) + { + lgInfo($"File json composto da {jsonData.Length} caratteri"); + try { - QueueFLog.TryDequeue(out currVal); - listaValori.Add(currVal); + memMap = JsonConvert.DeserializeObject(jsonData); + lgInfo($"Decodifica aree memMap: trovati {memMap.mMapRead.Count} valori TSVC"); + lgInfo($"Decodifica aree memMap: trovati {memMap.mMapWrite.Count} parametri "); + // se ho variabili read --> genero dati TSVC... + if (memMap.mMapRead.Count > 0) + { + TSVC_Data.Clear(); + LastTSVC.Clear(); + VCData currConf; + int periodo = 0; + VC_func funz = VC_func.POINT; + // accodo nella conf... + foreach (var item in memMap.mMapRead) + { + funz = item.Value.func; + periodo = item.Value.period; + currConf = new VCData() + { + Funzione = funz, + Period = periodo, + DTStart = DateTime.Now.AddHours(-1), + dataArray = new List() + }; + TSVC_Data.Add(item.Key, currConf); + } + // documento... + foreach (var item in TSVC_Data) + { + lgInfo($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}"); + // salvo i valori PREC... + LastTSVC.Add(item.Key, 0); + } + } + // infine se obj memoria valido salvo in MP-IO x sue applicazioni + if (memMap != null) + { + // invio su cloud conf memoria... + string rawData = JsonConvert.SerializeObject(memMap); + utils.callUrlNow($"{urlSaveMemMap}", rawData); + // salvo ANCHE come parametri i valori... + objItem currItem = new objItem(); + List allParam = new List(); + // valori WRITE + foreach (var item in memMap.mMapWrite) + { + currItem = new objItem() + { + uid = item.Value.name, + name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, + writable = true + }; + allParam.Add(currItem); + } + // valori READ + foreach (var item in memMap.mMapRead) + { + currItem = new objItem() + { + uid = item.Value.name, + name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name, + writable = false + }; + allParam.Add(currItem); + } + // invio su cloud parametri! + rawData = JsonConvert.SerializeObject(allParam); + utils.callUrl($"{urlSaveAllParams}", rawData); + } + } + catch (Exception exc) + { + lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}"); } - sendDataBlock(urlType.FLog, listaValori); - } - else - { - // invio in blocco - listaValori = QueueFLog.ToList(); - // invio - sendDataBlock(urlType.FLog, listaValori); - // svuoto! - QueueFLog = new ConcurrentQueue(); - } } else { - // INVIO SINGOLO...!!! - QueueFLog.TryDequeue(out currVal); - sendToMoonPro(urlType.FLog, currVal); + lgError("Errore in loadMemConf: file json vuoto!"); } - } - else - { - break; - } + reader.Dispose(); } else { - break; + lgInfo("loadMemConf: non trovata opzione PARAM_CONF in file INI"); } - } - else - { - break; - } + // loggo + lgInfo("DONE loadMemConf"); } - } - } - /// - /// Effettua chiamata URL e restituisce risultato - /// - /// - /// invio in modalità async (NON GARANTITO ordine...) - /// - public static string callUrl(string URL, bool doAsync) - { - string answ = ""; - // Chiamata ASINCRONA - if (doAsync) - { - //Task resp = utils.callUrlAsync(URL); - //answ = resp.Result; - answ = utils.callUrlAsync(URL); - } - // chiamata SOLO NORMALE SINCRONA... - else - { - answ = utils.callUrl(URL); - } - return answ; - } - - /// - /// Effettua chiamata URL e restituisce risultato - /// - /// - /// - /// invio in modalità async (NON GARANTITO ordine...) - /// - public static string callUrlWithPayload(string URL, string payload, bool doAsync) - { - string answ = ""; - // Chiamata ASINCRONA - if (doAsync) - { - answ = utils.callUrlAsync(URL, payload); - } - // chiamata SOLO NORMALE SINCRONA... - else - { - answ = utils.callUrl(URL, payload); - } - return answ; - } - - public static string GetMACAddress() - { - NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); - String sMacAddress = string.Empty; - foreach (NetworkInterface adapter in nics) - { - if (string.IsNullOrEmpty(sMacAddress))// only return MAC Address from first card + /// + /// Invia messaggio a logWatcher + /// + /// + /// + protected void sendToLogWatch(string messType, string message) { - IPInterfaceProperties properties = adapter.GetIPProperties(); - //sMacAddress = adapter.GetPhysicalAddress().ToString(); - sMacAddress = string.Join(":", (from z in adapter.GetPhysicalAddress().GetAddressBytes() select z.ToString("X2")).ToArray()); + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; + parentForm.updateFormDisplay(currDispData); } - } - return sMacAddress; - } - /// - /// Reset dei webclients - /// - public static void resetWebClients() - { - utils.resetWebClients(); - } - - /// - /// Effettua chaimata x split ODL - /// - /// - public bool forceSplitOdl() - { - bool fatto = false; - if (vetoSplit < DateTime.Now) - { - // imposto veto x 1 minuto ad altre chiamate... - vetoSplit = DateTime.Now.AddMinutes(1); - // eseguo SOLO SE sono online... - if (MPOnline && IobOnline) + /// + /// Invia messaggio a logWatcher + /// + /// + /// + /// + protected void sendToLogWatch(string messType, string message, params object[] args) { - string fullUrl = ""; - string rawSplit = ""; - string IOB_MULTI_CNAME = ""; - string[] elencoMulti = null; - - try - { - /*************************************************** - * Descrizione procedura (OK X SIMULATORI...) - * - * - chiamata su MP/IO - * - verifica che su DB sia abilitato AUTO ODL - * - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW - * - viene duplicato e chiuso ODL corrente - * - viene fatto partire ODL nuovo ADESSO - * - num pezzi come ODL precedente (o da media 3 ODL precedenti) - * - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL) - * - reset contapezzi PLC locale... - * - * - * - * DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE - * - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI) - * - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!) - * - viene fatto reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...) - * - viene fatto reset + nuovo ODL (con stessi articoli e quantità) su follower, SENZA avere gestione x ODL di un codice esterno (quindi registra TUTTO ma NON RITORNERA' dati non avendo link verso esterno) - * - serve NUOVA TABELLA delle macchine LEADER | FOLLOWER (1:n) e gestione da MP/IO - * - * ***************************************************/ - - if (isMulti) + try { - // devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali... - IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME"); - elencoMulti = IOB_MULTI_CNAME.Split(','); + string expString = string.Format(message, args); + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}"; + parentForm.updateFormDisplay(currDispData); } - // se normale splitto! - if (!isMulti) + catch + { } + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + /// + /// + protected void sendToLogWatch(string messType, string message, Exception exception, params object[] args) + { + try { - // invio chiamata URL x reset ODL su macchina - rawSplit = callUrl(urlForceSplit, false); - fatto = (rawSplit == "OK") ? true : false; + string expString = string.Format(message, args); + newDisplayData currDispData = new newDisplayData(); + currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}{Environment.NewLine}{exception}"; + parentForm.updateFormDisplay(currDispData); } - // se multi gestisco il bit delle tavole... - else + catch + { } + } + + /// + /// Invia messaggio a logWatcher + /// + /// + /// + protected void sendToTaskWatch(string messType, string message) + { + parentForm.taskWatcher = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}"; + } + + /// + /// Impostazioni parametri PLC + /// + protected virtual void setParamPlc() + { + loadMemConf(); + fixDefaultPar(); + } + + /// + /// Imposta eventuali altri valori default + /// + private void fixDefaultPar() + { + // parametro max tentativi PING... + string s_maxPingRetry = getOptPar("MAX_PING_RETRY"); + if (!string.IsNullOrEmpty(s_maxPingRetry)) { - 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}|{item}"; - rawSplit = callUrl(fullUrl, false); - } - fatto = (rawSplit == "OK") ? true : false; + int numRetry = 5; + int.TryParse(s_maxPingRetry, out numRetry); + maxPingRetry = numRetry; } - } - catch (Exception exc) - { - lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}"); - } - // se fatto --> resetto contapezzi!!! - if (fatto) - { - contapezziPLC = 0; - contapezziIOB = 0; - } } - else + + /// + /// Imposto alcuni valori di default + /// + /// indica se sia richeisto di SVUOTARE le code delel info + private void setDefaults(bool resetQueue) { - lgError("Richiesto forceSplitOdl ma MP/IOB offline --> NON eseguito"); - } - } - else - { - lgError("Richiesto forceSplitOdl ma veto attivo --> NON eseguito"); - } - return fatto; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// Restituisce un payload in formato json della lista di valori ricevuta - /// - /// Tipo di URL (eventi / FLog) - /// elenco di valori da coda string salvata - /// - public string jsonPayload(urlType tipoUrl, List elencoValori) - { - string answ = ""; - if (elencoValori != null) - { - if (tipoUrl == urlType.FLog) - { - flogData currData = new flogData(); - flogJsonPayload fullObj = new flogJsonPayload(); - fullObj.fluxData = new List(); - string[] valori; - int counter = 0; - DateTime dtEve = DateTime.Now; - // inizio processando ogni valore - foreach (var item in elencoValori) - { - valori = qDecodeIN(item); - //DateTime.TryParse(valori[0], out dtEve); - CultureInfo provider = CultureInfo.InvariantCulture; - DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); - int.TryParse(valori[3], out counter); - currData = new flogData() + numSim = utils.CRI("numSim"); + lastPrgName = ""; + nReadIN = 0; + nReadFilt = 0; + nSendOut = 0; + currMode = 0; + lastAlarm = ""; + doStartMemDump = utils.CRB("doStartMemDump"); + doSampleMemory = utils.CRB("doSampleMemory"); + // svuoto code se richiesto + if (resetQueue) { - flux = valori[1], - valore = valori[2], - dtEve = dtEve, - dtCurr = DateTime.Now, - cnt = counter - }; - fullObj.fluxData.Add(currData); - } - // conversione finale - try - { - answ = JsonConvert.SerializeObject(fullObj); - } - catch (Exception exc) - { - lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); - } + QueueIN = new ConcurrentQueue(); + QueueFLog = new ConcurrentQueue(); + QueueAlarm = new ConcurrentQueue(); + QueueMessages = new ConcurrentQueue(); + } + // imposto contatori blink a zero... + i_counters = new int[32]; + lastPeriodicLog = DateTime.Now; + // fix parametri generali... + enablePrgName = true; } - else - { - evData currData = new evData(); - evJsonPayload fullObj = new evJsonPayload(); - fullObj.eventList = new List(); - string[] valori; - int counter = 0; - DateTime dtEve = DateTime.Now; - // inizio processando ogni valore - foreach (var item in elencoValori) - { - valori = qDecodeIN(item); - //DateTime.TryParse(valori[0], out dtEve); - CultureInfo provider = CultureInfo.InvariantCulture; - DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); - int.TryParse(valori[2], out counter); - currData = new evData() - { - valore = valori[1], - dtEve = dtEve, - dtCurr = DateTime.Now, - cnt = counter - }; - fullObj.eventList.Add(currData); - } - // conversione finale - try - { - answ = JsonConvert.SerializeObject(fullObj); - } - catch (Exception exc) - { - lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); - } - } - } - return answ; - } - /// - /// Verifica e processing x gestione ODL automatica - /// - public void processAutoOdl() - { - bool fatto = false; - if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL"))) - { - string IOB_MULTI_CNAME = ""; - string[] elencoMulti = null; - string fullUrl = ""; - if (isMulti) + #region metodi adapter + + protected bool _connOk = false; + + /// + /// Salva verifica stato connessione OK + /// + /// + public virtual bool connectionOk { - // 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) - { - // carico i parametri di configurazione x reset ODL... - string CHANGE_ODL_HOURS = getOptPar("CHANGE_ODL_IDLE_MIN"); - 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) + get { - // 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) + return _connOk || DemoIn; + } + set + { + _connOk = value; + } + } + + /// + /// indica se ping disabilitato da optPar + /// + public bool pingDisabled + { + get + { + bool answ = false; + bool.TryParse(getOptPar("NO_PING"), out answ); + return answ; + } + } + + /// + /// Max tentativi ping permessi (default: 5) + /// + protected int maxPingRetry { get; set; } = 5; + + /// + /// test ping all'indirizzo PLC/CNC impostato nei parametri + /// + /// + protected IPStatus testPingMachine + { + get + { + IPStatus answ = IPStatus.Unknown; + // se disabilitato salto... + if (pingDisabled) { - fullUrl = $"{urlInizioOdlIob}|{item}"; - rawDataInizio = callUrl(fullUrl, false); - DateTime.TryParse(rawDataInizio, out tmpData); - inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl; + answ = IPStatus.Success; } - } - // verifico se sia scaduto... - if (inizioOdl.AddHours(minOdlDurHours) < adesso) - { - string rawIdle = ""; - int idlePeriod = 0; + else + { + IPAddress address; + PingReply reply; + using (Ping pingSender = new Ping()) + { + address = IPAddress.Loopback; + IPAddress.TryParse(cIobConf.cncIpAddr, out address); + int pingMsTimeout = cIobConf.pingMsTimeout; + reply = pingSender.Send(address, pingMsTimeout); + answ = reply.Status; + } + } + return answ; + } + } + + /// + /// processa dataLayer e se necessario salva/mostra + /// + public static void checkSavePersDataLayer() + { + } + + public static void resetDebugConsole() + { + } + + /// + /// Esecuzione dei task richiesti e pulizia coda richieste eseguite + /// + /// + public virtual Dictionary executeTasks(Dictionary task2exe) + { + // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti... + Dictionary taskDone = new Dictionary(); + if (task2exe != null) + { + bool taskOk = false; + string taskVal = ""; + // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4 + foreach (var item in task2exe) + { + taskOk = false; + taskVal = ""; + // converto richiesta in enum... + taskType tName = taskType.nihil; + Enum.TryParse(item.Key, out tName); + // controllo sulla KEY... + switch (tName) + { + case taskType.nihil: + case taskType.fixStopSetup: + case taskType.forceSetPzCount: + case taskType.sendWatchDogMes2Plc: + taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; + lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.setArt: + case taskType.setComm: + case taskType.setProg: + case taskType.setPzComm: + // recupero dati da memMap... + if (memMap.mMapWrite.ContainsKey(item.Key)) + { + dataConf currMem = memMap.mMapWrite[item.Key]; + string addr = currMem.memAddr; + taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte"; + // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà + memMap.mMapWrite[item.Key].value = item.Value; + } + else + { + taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}"; + } + break; + + case taskType.forceResetPzCount: + // reset contapezzi inizio setup + taskOk = resetcontapezziPLC(); + taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.startSetup: + // reset contapezzi inizio setup + taskOk = resetcontapezziPLC(); + taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.stopSetup: + // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO + if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE") + { + taskOk = resetcontapezziPLC(); + } + taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC"; + lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + + case taskType.setParameter: + // richiedo da URL i parametri WRITE da popolare + lgInfo("Chiamata processMemWriteRequests"); + taskVal = processMemWriteRequests(); + // se restituiscce "" faccio altra prova... + if (string.IsNullOrEmpty(taskVal)) + { + // i parametri me li aspetto come stringa composta paramName|paramvalue + if (item.Value.Contains("|")) + { + string[] paramsJob = item.Value.Split('|'); + taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; + } + else + { + taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; + } + } + break; + + default: + taskVal = "SKIPPED | NO EXEC"; + lgInfo($"Chiamata default senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); + break; + } + // aggiungo task! + taskDone.Add(item.Key, taskVal); + } + } + return taskDone; + } + + /// + /// effettua recupero dati ed invio valori modificati... + /// + /// + public void getAndSend(gatherCycle ciclo) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!! + try + { + trySendValues(); + } + catch (Exception exc) + { + lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria"); + currDispData.semOut = Semaforo.SR; + } + // controllo connessione/connettività + if (connectionOk) + { + // controllo non sia già in esecuzione... + if (!adpCommAct) + { + // provo ad avviare + try + { + // imposto flag adapter running.. + adpCommAct = true; + adpStartRun = DateTime.Now; + } + catch (Exception exc) + { + string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}"; + adpCommAct = false; + adpStartRun = DateTime.Now; + currDispData.newLiveLogData = errore; + } + if (adpCommAct) + { + // try / catch generale altrimenti segno che è disconnesso... + try + { + bool showDebugData = false; + if (ciclo == gatherCycle.VHF) + { + processVHF(); + } + // processing dati memoria (lettura, filtraggio, enqueque) + else if (ciclo == gatherCycle.HF) + { + processWhatchDog(); + processAllMemory(); + processMode(); + } + else if (ciclo == gatherCycle.MF) + { + processServerRequests(); + processOverride(); + processContapezzi(); + processCncAlarms(); + processDynData(); + } + else if (ciclo == gatherCycle.LF) + { + processOtherCounters(); + processProgram(); + // verifico se devo gestire cambio ODL in modo automatico + processAutoOdl(); + } + else if (ciclo == gatherCycle.VLF) + { + if (utils.CRB("enableContapezzi")) + { + // rilettura contapezzi da server... + lgInfo("Ciclo VLF: pzCntReload(true)"); + if (!isMulti) + { + pzCntReload(true); + } + // refresh associazione Macchina - IOB + sendM2IOB(); + } + // recupero dati SETUP (sysinfo) e li invio/mostro se variati... + processSysInfo(); + // checkLogDir x shrink! + checkShrinkDir(); + // eventuale log! + if (utils.CRB("recTime")) + { + logTimeResults(); + } + } + // mostra eventuali altri dati di processo... + reportDataProc(); + if (showDebugData) + { + // verifica se debba salvare e mostrare dati + checkSavePersDataLayer(); + } + } + catch (Exception exc) + { + // segnalo eccezione e indico disconnesso... + lgError(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc)); + parentForm.fermaAdapter(true, false, true); + } + // tolgo flag running + adpCommAct = false; + } + else + { + if (periodicLog) + { + lgInfo("ADP not running..."); + } + } + } + else + { + // log ADP running + lgError("Non eseguo chiamata: ADP ancora in running"); + // se è bloccato da oltre maxSec lo sblocco... + if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) + { + // tolgo flag running + adpCommAct = false; + adpStartRun = DateTime.Now; + } + } + } + else + { + // provo a riconnettere SE abilitato tryRestart... + if (adpTryRestart && !connectionOk) + { + // controllo se sia scaduto periodi di veto al tryConnect... + int waitRecMSec = utils.CRI("waitRecMSec") * 2; + DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); + if (DateTime.Now > dtVeto) + { + lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect"); + lastConnectTry = DateTime.Now; + tryConnect(); + } + } + currDispData.semIn = Semaforo.SR; + } + raiseRefresh(currDispData); + } + + /// + /// riporta il log di tutti i dati di results temporali registrati + /// + public void logTimeResults() + { + if (TimingData.results.Count > 0) + { + lgInfo("{0}--------------- START TIMING DATA ---------------", Environment.NewLine); + int globNumCall = 0; + TimeSpan globAvgMsec = new TimeSpan(0); + foreach (TimeRec item in TimingData.results) + { + // loggo SOLO se del mio IOB corrente... + if (item.classCall == cIobConf.codIOB) + { + lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + globNumCall += item.numCall; + globAvgMsec += item.totMsec; + } + } + // riporto conteggio medio al secondo... + lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine); + // mostro in form statistiche globali! + parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds)); + } + } + + /// + /// Effettua ciclo controllo richieste server + /// + public void processServerRequests() + { + Dictionary task2exe = new Dictionary(); + Dictionary taskDone = new Dictionary(); + // recupero elenco delle cose da fare + string resp = getTask2exe(); + if (!string.IsNullOrEmpty(resp)) + { + try + { + task2exe = JsonConvert.DeserializeObject>(resp); + // se ho da fare chiamo esecuzione.. + if (task2exe.Count > 0) + { + taskDone = processTask(task2exe); + } + } + catch (Exception exc) + { + lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); + } + } + } + + public Dictionary processTask(Dictionary task2exe) + { + Dictionary taskDone = new Dictionary(); + if (task2exe != null) + { + lgInfo($"Task2Exe: trovati {task2exe.Count} task da eseguire, procedo"); + // chiamo procedura esecutiva (diversa x ogni IOB) + taskDone = executeTasks(task2exe); + // loggo tutti i task done... + foreach (var item in taskDone) + { + sendToTaskWatch(item.Key, item.Value); + } + // ora chiamo la cancellazione dei task eseguiti... + foreach (var item in taskDone) + { + remTask2exe(item.Key, item.Value); + } + } + return taskDone; + } + + /// + /// Effettua rilettura del contapezzi dal server MP/IO + /// + /// Forza rilettura da DB tempi ciclo rilevati + public void pzCntReload(bool forceCountRec) + { + // legge da IO server ULTIMO valore CONTPEZZI al riavvio... + string currServerCount = ""; + string lastIdxODL = ""; + if (checkServerAlive) + { + // leggo PRIMA ODL .... + lastIdxODL = utils.callUrl(urlGetCurrODL); + lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL); + // se ho valori in coda da trasmettere uso dati REDIS + if (forceCountRec) + { + // uso dati da TCiclo registrati... + currServerCount = utils.callUrl(urlGetPzCountRec); + lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount); + } + else + { + // uso il contapezzi dichiarato dall'IOB stesso + currServerCount = utils.callUrl(urlGetPzCount); + lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount); + } + // controllo: SE NON HO ODL... + if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0") + { + // NON AGGIORNO + contapezziIOB = contapezziPLC; + lgInfo($"Errore lettura ODL (vuoto) resta tutto invariato contapezzi: {contapezziIOB} | contapezziPLC {contapezziPLC}"); + } + else + { + if (!string.IsNullOrEmpty(currServerCount)) + { + // se "-1" resto a ultimo... + if (currServerCount != "-1") + { + int newVal = -1; + Int32.TryParse(currServerCount, out newVal); + contapezziIOB = newVal > -1 ? newVal : contapezziIOB; + lgInfo("Ricevuta conferma da server di {0} pezzi registrati per ODL", currServerCount); + } + else + { + // NON AGGIORNO + contapezziIOB = contapezziPLC; + lgInfo($"Errore lettura contapezzi (-1) - uso contapezziPLC --> {contapezziPLC}"); + } + } + else + { + // registro che ho UN NUOVO ODL + lgInfo($"Lettura ODL in pzCntReload, currIdxODL {currIdxODL} --> lastIdxODL {lastIdxODL}"); + // se ODL differente e NUOVO è zero --> resetto! + if (currIdxODL.ToString() != lastIdxODL && lastIdxODL == "0") + { + // cambiato ODL quindi reset... + contapezziIOB = 0; + lgInfo("Nuovo ODL==0, RESET contapezzi (-->ZERO)"); + } + // provo a salvare nuovo ODL + int.TryParse(lastIdxODL, out currIdxODL); + } + } + } + else + { + // se server NON pronto... + contapezziIOB = contapezziPLC; + lgError("Errore server NON pronto in pzCntReload"); + } + } + + /// + /// Metodo generico di reset contapezzi... + /// + /// + public virtual bool resetcontapezziPLC() + { + return false; + } + + /// + /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi... + /// + /// Pezzi richiesti + /// + public virtual bool setcontapezziPLC(int newPzCount) + { + return false; + } + + /// + /// Avvia l'adapter sulla porta richiesta + /// + /// indica se sia richeisto di SVUOTARE le code delel info + public virtual void startAdapter(bool resetQueue) + { + lgInfo("Starting adapter..."); + maxJsonData = utils.CRI("maxJsonData"); + maxJsonDataEv = utils.CRI("maxJsonDataEv"); + parentForm.commPlcActive = false; + adpRunning = true; + dtAvvioAdp = DateTime.Now; + lastWatchDog = dtAvvioAdp; + lastPING = dtAvvioAdp; + lastReadPLC = dtAvvioAdp.AddMinutes(-1); + lastDisconnCheck = dtAvvioAdp; + TimingData.resetData(); + // aggiungo altri defaults + setDefaults(resetQueue); + adpTryRestart = true; + parentForm.displayTaskAndLog("Adapter Started!"); + } + + /// + /// ferma l'adapter... + /// + /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato in automatico) + /// indica se sia richeisto di SVUOTARE le code delel info + public virtual void stopAdapter(bool tryRestart, bool forceDequeue) + { + if (forceDequeue) + { + // svuoto le code dei valori letti e non ancora trasmessi... + parentForm.displayTaskAndLog("Svuotamento FORZATO coda segnali..."); + while (QueueIN.Count > 0) + { + // INVIO COMUNQUE...!!! + string valore = ""; + QueueIN.TryDequeue(out valore); + sendToMoonPro(urlType.SignIN, valore); + } + parentForm.displayTaskAndLog("Svuotamento FORZATO coda FluxLOG..."); + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueFLog.Count > minJsonData) + { + while (QueueFLog.Count > 0) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueFLog.Count > maxJsonData) + { + string currVal = ""; + // prendoi primi maxJsonDataValori + for (int i = 0; i < maxJsonData; i++) + { + QueueFLog.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.FLog, listaValori); + } + else + { + // invio in blocco + listaValori = QueueFLog.ToList(); + // invio + sendDataBlock(urlType.FLog, listaValori); + // svuoto! + QueueFLog = new ConcurrentQueue(); + } + } + // HO FINITO invio di FLog... + } + else + { + string currVal = ""; + while (QueueFLog.Count > 0) + { + // INVIO COMUNQUE...!!! + QueueFLog.TryDequeue(out currVal); + sendToMoonPro(urlType.FLog, currVal); + } + } + } + parentForm.displayTaskAndLog("Stopping adapter..."); + adpTryRestart = false; + + parentForm.displayTaskAndLog("Stopping adapter - last periodic data read..."); + + // chiudo la connessione all'adapter... + tryDisconnect(); + dtStopAdp = DateTime.Now; + adpTryRestart = tryRestart; + adpRunning = false; + // chiudo! + parentForm.displayTaskAndLog("Adapter Stopped."); + parentForm.commPlcActive = false; + } + + /// + /// Metodo base connessione... + /// + public virtual void tryConnect() + { + dtAvvioAdp = DateTime.Now; + } + + /// + /// Metodo base disconnessione... + /// + public virtual void tryDisconnect() + { + } + + /// + /// Stringa raw dei parametri da scrivere... + /// + /// + protected string getParams2write() + { + string answ = ""; + string url2call = $"{urlGetParams2Write}"; + if (verboseLog) + { + lgInfo("chiamata URL " + url2call); + } + answ = utils.callUrlNow(url2call); + // se vuoto faccio seconda prova... + if (string.IsNullOrEmpty(answ)) + { + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Chiede elenco dei task da eseguire + /// - formato Json + /// - array di KVP / Dictionary + /// - formato definito da API x MP/IO/: + /// - KEY: task + /// - VALUE: array JSon KVP + /// + protected string getTask2exe() + { + string answ = ""; + if (checkServerAlive) + { + string url2call = $"{urlGetTask2Exe}"; + if (verboseLog) + { + lgInfo("chiamata URL " + url2call); + } + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Metodo da overridare x scrivere DAVVERO i aprametri sul PLC + /// + /// + protected virtual void plcWriteParams(List updatedPar) + { + // non faccio nulla di base... + } + + /// + /// Processa le richieste di scrittura memoria + /// + /// + protected string processMemWriteRequests() + { + string answ = ""; + // li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC) + List writeList = new List(); + List updatedPar = new List(); + // recupero elenco delle cose da fare + string resp = getParams2write(); + if (!string.IsNullOrEmpty(resp)) + { + try + { + writeList = JsonConvert.DeserializeObject>(resp); + // se ho da fare chiamo esecuzione.. + if (writeList.Count > 0) + { + foreach (var item in writeList) + { + // scrivo in memoria + if (memMap.mMapWrite.ContainsKey(item.uid)) + { + memMap.mMapWrite[item.uid].value = item.reqValue; + // accodo in stringa taskVal... + answ += $" | Parameter {item.uid} --> {item.reqValue}"; + // sistemo valori + item.value = item.reqValue; + lgInfo($"Effettuato update parametro: actVal = {item.value} | reqVal = {item.reqValue}"); + item.reqValue = ""; + // salvo in lista da ritrasmettere + updatedPar.Add(item); + } + else + { + answ += $" | Error: parameter {item.uid} not found"; + } + } + // richiamo scrittura parametri su PLC + plcWriteParams(updatedPar); + // invio su cloud parametri! + string rawData = JsonConvert.SerializeObject(updatedPar); + utils.callUrl($"{urlUpdateWriteParams}", rawData); + } + } + catch (Exception exc) + { + lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}"); + } + } + else + { + lgError("Non è stata ricevuta risposta x task da eseguire"); + } + return answ; + } + + protected void raiseRefresh(newDisplayData currDispData) + { + if (currDispData.hasData) + { + // segnalo refresh! + if (eh_refreshed != null) + { + eh_refreshed(this, new iobRefreshedEventArgs(currDispData)); + } + } + } + + /// + /// Cancella dal server i task eseguiti + /// + /// + /// + /// + protected string remTask2exe(string taskName, string esitoTask) + { + string answ = ""; + if (checkServerAlive) + { + string url2call = $"{urlRemTask2Exe}{taskName}"; + lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}"); + answ = utils.callUrlNow(url2call); + } + return answ; + } + + /// + /// Invia informazioni associazione IOB 2 machine + /// + protected void sendM2IOB() + { + if (checkServerAlive) + { + lgInfo("chiamata URL " + urlSetM2IOB); + utils.callUrlNow(urlSetM2IOB); + } + } + + /// + /// Invia al server IO i valori dei parametri opzionali (es counters) + /// + /// Nome parametro + /// Valore parametro + protected void sendOptVal(string paramName, string paramValue) + { + if (checkServerAlive) + { + string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}"; + lgInfo("chiamata URL " + url2call); + utils.callUrlNow(url2call); + } + } + + /// + /// Invia al server IO i valori dei parametri opzionali (es counters) + /// + /// Nome parametro + /// Valore parametro INT + protected void sendOptVal(string paramName, int paramValueInt) + { + // override! + sendOptVal(paramName, paramValueInt.ToString()); + } + + /// + /// Verifica e se necessario comprime directory log... + /// + private void checkShrinkDir() + { + string path = string.Format("{0}logs\\{1}", AppDomain.CurrentDomain.BaseDirectory, cIobConf.codIOB); + baseUtils.shrinkDir(path); + } + + private void reportDataProc() + { + // update valori visualizzazione... + parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut); + } + + private void svuotaCodaContapezzi() + { + // permetto al max 2 tentativi infruttuosi... + int maxTry = 2; + int oldContapezzi = contapezziIOB; + // se ho contapezzi OLTRE limite... + while ((MPOnline) && (contapezziPLC > contapezziIOB + minSendPzCountBlock)) + { + lgInfo($"Ciclo svuotaCodaContapezzi --> contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}"); if (!isMulti) { - // controllo SE sono fermo (spento o in manuale) per il periodo minimo richiesto... - rawIdle = callUrl(urlIdleTime, false); - int.TryParse(rawIdle, out idlePeriod); + pzCntReload(true); + } + // provo invio + trySendPzCountBlock(); + // verifica per evitare loop infinito invio fallito + if (oldContapezzi == contapezziIOB) + { + maxTry--; } 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; - } + maxTry = 2; + oldContapezzi = contapezziIOB; } - if (idlePeriod >= minPlcIdelMin) + // verifico maxTry: se li ho esauriti esco! + if (maxTry <= 0) { + return; + } + // aspetto x dare tempo calcolo + Thread.Sleep(400); + } + } + + /// + /// Cerca di inviare su un altro thread i vari dati accumulati... + /// + private void trySendValues() + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + try + { + // verifico se risponde il server... + if (checkServerAlive) + { + bool iobOk = false; + if (utils.CRB("sendDataByThread")) + { + Task taskCheck = TaskEx.Run(() => iobOk = checkIobEnabled); + } + else + { + iobOk = checkIobEnabled; + } + // verifico SE posso inviare dati + if (iobOk) + { + currDispData.semOut = Semaforo.SV; + // verificare come gestire il task secondario senza interferenza (chiamate update su FORM da thread secondari danno errori) + if (utils.CRB("sendDataByThread")) + { + // invio con thread separato... + Task taskSigIN = TaskEx.Run(() => svuotaCodaSignIN()); + Task taskFlog = TaskEx.Run(() => svuotaCodaFLog()); + } + else + { + // gestione queue SignalIN (invio, display) + svuotaCodaSignIN(); + currDispData.counter = contapezziIOB; + raiseRefresh(currDispData); + // provo a svuotare coda contapezzi + svuotaCodaContapezzi(); + currDispData.counter = contapezziIOB; + raiseRefresh(currDispData); + // gestione queue FluxLog (invio, display) + svuotaCodaFLog(); + raiseRefresh(currDispData); + } + } + else + { + // mostro VETO-SEND x invio... GIALLO + currDispData.semOut = Semaforo.SG; + if (periodicLog) + { + lgInfo("IOB - VETO SEND"); + } + } + } + else + { + // mostro SERVER KO x invio... ROSSO + currDispData.semOut = Semaforo.SR; + if (periodicLog) + { + lgInfo("IOB - SERVER NOT READY"); + } + } + } + catch (Exception exc) + { + lgError($"Errore in fase trySendValues{Environment.NewLine}{exc}"); + currDispData.semOut = Semaforo.SR; + } + raiseRefresh(currDispData); + } + + #endregion metodi adapter + + #region layer persistenza dati + + /// + /// Dizionario di persistenza per i valori da salvare da/su file + /// + public Dictionary persistenceLayer; + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) + /// + /// + /// + private string getStoredVal(string keyVal) + { + string value = ""; + try + { + if (persistenceLayer != null) + { + if (!persistenceLayer.TryGetValue(keyVal, out value)) + { + persistenceLayer.Add(keyVal, "0"); + } + } + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc)); + } + return value; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come double + /// + /// + /// + private double getStoredValDouble(string keyVal) + { + double answ = 0; + try + { + answ = Convert.ToDouble(getStoredVal(keyVal)); + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc)); + } + answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come INT + /// + /// + /// + private long getStoredValLong(string keyVal) + { + long answ = 0; + try + { + answ = Convert.ToInt64(getStoredVal(keyVal)); + } + catch + { } + // verifico che il valore sia minore di 9/10 del valore massimo... + answ = (answ < (long.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// recupera valore salvato in persistence layer (se non c'è crea...) come UINT + /// + /// + /// + private uint getStoredValUInt(string keyVal) + { + uint answ = 0; + try + { + answ = Convert.ToUInt32(getStoredVal(keyVal)); + } + catch (Exception exc) + { + lgError(string.Format("Eccezione in getStoredValUInt: {0}{1}", Environment.NewLine, exc)); + } + // verifico che il valore sia minore di 9/10 del valore massimo... + answ = (answ < (uint.MaxValue / 10 * 9)) ? answ : 0; + return answ; + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private double updateValDoubleByIncr(int i, double delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + double contAct = getStoredValDouble(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private long updateValLongByIncr(int i, long delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + long contAct = getStoredValLong(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + /// + /// Aggiorna un valore del dizionario in SOSTITUZIONE + /// + /// + /// + /// + /// Nuovo valore incrementato + private void updateValString(int i, string newVal, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // salvo in ram! + persistenceLayer[keyVal] = newVal; + } + + /// + /// Aggiorna un valore del dizionario in SOSTITUZIONE e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private void updateValUInt(int i, uint newVal, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // salvo in ram! + persistenceLayer[keyVal] = newVal.ToString(); + } + + /// + /// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce + /// + /// + /// + /// + /// Nuovo valore incrementato + private uint updateValUIntByIncr(int i, uint delta, string searchString) + { + // stringa da cercare.. + string keyVal = string.Format(searchString, i + 1); + // recupero valore precedente... + uint contAct = getStoredValUInt(keyVal); + // nuovo valore... + contAct += delta; + // salvo in ram! + persistenceLayer[keyVal] = contAct.ToString(); + // rendo il valore! + return contAct; + } + + #endregion layer persistenza dati + + #region area lettura configurazioni + + /// + /// Cerca parametri opzionali in modalità "like" del nome + /// + /// + /// + public Dictionary findOptPar(string keyStartSearch = "") + { + Dictionary answ = new Dictionary(); + // controllo SE keySearch !="" + if (!string.IsNullOrWhiteSpace(keyStartSearch)) + { + if (cIobConf.optPar.Count > 0) + { + // ciclo su tutti e cerco occorrenze che INIZINO... + foreach (var item in cIobConf.optPar) + { + if (item.Key.StartsWith(keyStartSearch)) + { + answ.Add(item.Key, item.Value); + } + } + } + } + return answ; + } + + /// + /// Cerca se esiste il parametro opzionale e lo restituisce + /// + /// + /// + public string getOptPar(string key) + { + string answ = ""; + if (cIobConf.optPar.Count > 0) + { + // controllo SE HO il parametro + if (cIobConf.optPar.ContainsKey(key)) + { + answ = cIobConf.optPar[key]; + } + } + return answ; + } + + /// + /// Decodifica file MAP (caso .bit) + /// + /// + /// + /// indirizzo Byte: indirizzo di partenza memoria + /// dimensione singolo slot in byte + /// indirizzo bit: numero riga x calcolo indice bit + /// + protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + int resto = 0; + Math.DivRem(BitNum, 8, out resto); + string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + /// + /// Decodifica file MAP generico + /// + /// + /// + /// + /// + /// + /// + protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + /// + /// Legge il file di conf di una MAP di informazioni da gestire con lettura set memoria + /// + /// nome vettore memoria + /// file origine + /// dimensione (in byte) della memoria + /// dimensione (in byte) della memoria + protected void loadConfFile(ref otherData[] vettoreConf, string nomeFile, int memSize, ref int numVett) + { + otherData lastData = new otherData(); + int totRighe = 0; + string linea; + totRighe = File.ReadLines(nomeFile).Count(); + // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco... + vettoreConf = new otherData[File.ReadLines(nomeFile).Count()]; + // carica da file... + StreamReader file = new StreamReader(nomeFile); + // leggo 1 linea alla volta... + int numRiga = 0; + int bitNum = 0; + int byteNum = 0; + while ((linea = file.ReadLine()) != null) + { + // SE non è un commento... + if (linea.Substring(0, 1) != "#") + { + // se finisce per BIT allora processo bit-a-bit... + if (linea.EndsWith("BOOL")) + { + try + { + string[] memIdx = linea.Split(utils.CRC("testCharSep"))[0].Split('.'); + // calcolo bit e byte number... + int.TryParse(memIdx[0], out byteNum); + if (memIdx.Length > 1) + { + int.TryParse(memIdx[1], out bitNum); + } + else + { + bitNum = 0; + } + } + catch + { + byteNum = 0; + bitNum = 0; + } + lastData = decodeBitData(linea, utils.CRC("testCharSep"), byteNum, 1, bitNum); + vettoreConf[numRiga] = lastData; + } + else + { + lastData = decodeOtherData(linea, utils.CRC("testCharSep"), "", 1, memSize); + vettoreConf[numRiga] = lastData; + } + numRiga++; + } + } + // salvo lunghezza file... + try + { + numVett = Convert.ToInt32(lastData.memAddr) + 1; + } + catch + { + numVett = numRiga + 1; + } + // chiudo file + file.Close(); + // ora trimmo vettore al solo numero VERO dei valori caricati... + Array.Resize(ref vettoreConf, numRiga); + + if (isVerboseLog) + { + lgInfo(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile)); + } + } + + #endregion area lettura configurazioni + + #region IOB METHODS + + /// + /// contatore x simulazione valori input + /// + public int countSim = 0; + + /// + /// DateTime Ultimo valore simulazione generato + /// + public DateTime lastSim; + + /// + /// Verifica se il server sia ALIVE (tramite PING) + /// + public bool checkServerAlive + { + get + { + bool answ = false; + // controllo se ho un VETO all'invio... + if (dtVetoPing < DateTime.Now) + { + if (DemoOut) + { + answ = false; + } + else + { + IPStatus pingStatus = testPingServer; + // se passa il ping faccio il resto... + if (pingStatus == IPStatus.Success) + { + string callResp = ""; + try + { + // chiamo URL, se restituisce "OK" è alive! + 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); + } + else + { + lgInfo("SERVER OFFLINE in checkServerAlive"); + parentForm.commSrvActive = 0; + } + // salvo nuovo status... + MPOnline = answ; + } + else + { + // allungo periodo controllo... + dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); + } + } + else + { + lgInfo($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})"); + MPOnline = false; + // imposto veto a 10 volte reinvio dati standard... + dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); + utils.dtVetoSend = dtVetoPing; + } + } + } + else + { + // altrimenti passo ultimo valore noto... + answ = MPOnline; + } + return answ; + } + } + + /// + /// Verifica se sia machcina multi = DoppioPallet da CONF + /// + public bool isMulti + { + get + { + bool answ = false; + if (cIobConf.optPar.Count > 0) + { + // cerco con chaive reale IS_MULTI + string keyName = "IS_MULTI"; + if (!cIobConf.optPar.ContainsKey(keyName)) + { + // legacy: accetto anche SIM_MULTI... + keyName = "SIM_MULTI"; + } + // vera verifica su chaive... + if (cIobConf.optPar.ContainsKey(keyName)) + { + string SIM_MULTI = getOptPar(keyName); + answ = SIM_MULTI == "1"; + } + } + return answ; + } + } + + /// + /// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)... + /// + public string urlAddPzCount + { + get + { + string answ = ""; + try + { + answ = string.Format(@"http://{0}{1}{2}/savePzCountInc/{3}?qty=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlAddPzCount"); + } + return answ; + } + } + + /// + /// URL per check alive... + /// + public string urlAlive + { + get + { + return string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE); + } + } + + /// + /// URL per forzare split ODL... + /// + public string urlForceSplit + { + get + { + return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_FORCLE_SPLIT_ODL, cIobConf.codIOB); + } + } + + /// + /// URL per salvataggio contapezzi... + /// + public string urlGetCurrODL + { + get + { + string answ = ""; + try + { + answ = string.Format(@"http://{0}{1}{2}/getCurrODL/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlGetCurrODL"); + } + return answ; + } + } + + /// + /// URL per richiamo parametri da scrivere... + /// + public string urlGetParams2Write + { + get + { + string answ = ""; + try + { + answ = string.Format(@"http://{0}{1}{2}/getObjItems2Write/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, 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 = $@"http://{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 = $@"http://{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 = string.Format(@"http://{0}{1}{2}/getTask2Exe/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlGetTask2Exe"); + } + return answ; + } + } + + /// + /// URL per recupero idle time IOB... + /// + public string urlIdleTime + { + get + { + return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_IDLE_TIME, cIobConf.codIOB); + } + } + + /// + /// URL per recupero inizio ODL... + /// + public string urlInizioOdlIob + { + get + { + return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_ODL_STARTED, cIobConf.codIOB); + } + } + + /// + /// URL per check se abilitato... + /// + public string urlIobEnabled + { + get + { + return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDENABLED, cIobConf.codIOB); + } + } + + /// + /// URL per segnalazione reboot... + /// + public string urlReboot + { + get + { + string answ = ""; + try + { + answ = string.Format(@"http://{0}{1}{2}{3}&mac={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDREBO, cIobConf.codIOB, GetMACAddress()); + } + catch + { + answ = string.Format(@"http://{0}{1}{2}{3}", 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 = string.Format(@"http://{0}{1}{2}/remTask2Exe/{3}?taskName=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); + } + 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; + string apiCall = "setObjItems"; + answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, 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 = string.Format(@"http://{0}{1}{2}/saveConf/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB); + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlSaveMemConf"); + } + return answ; + } + } + + /// + /// URL per salvataggio dati associazione Machine 2 IOB... + /// + public string urlSetM2IOB + { + get + { + string answ = ""; + try + { + string machineName = Environment.MachineName; + answ = string.Format(@"http://{0}{1}{2}/setM2IOB/{3}?IOB_name={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB, 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 = string.Format(@"http://{0}{1}{2}/addOptPar/{3}?", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, 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 = $@"http://{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 = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/MAC_{cIobConf.codIOB}?counter="; + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlSetPzCountMAC"); + } + return answ; + } + } + + /// + /// URL per salvataggio in UPSERT dei PARAMETRI IOB scritti... + /// + public string urlUpdateWriteParams + { + get + { + string answ = ""; + try + { + string machineName = Environment.MachineName; + string apiCall = "upsertObjItems"; + answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, cIobConf.codIOB); + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlSaveMemConf"); + } + return answ; + } + } + + /// + /// Valore del num max invii consecutivi da coda... + /// + protected static int nMaxSend + { + get + { + int answ = 5; + try + { + answ = utils.CRI("nMaxSend"); + } + catch + { } + return answ; + } + } + + /// + /// Verifica se la IOB sia ENABLED (da server o Demo) + /// + private bool checkIobEnabled + { + get + { + bool answ = false; + // controllo se ho veto al check... + if (dtVetoCheckIOB < DateTime.Now) + { + if (DemoOut) + { + answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); + } + else + { + try + { + // chiamo URL, se restituisce "OK" è enabled! + string callResp = callUrl(urlIobEnabled, true); + answ = (callResp == "OK"); + // attesa casuale se necessario + var rand = new Random(); + // primi 2 test + int maxTry = 2; + while (maxTry > 0 && !answ) + { + Thread.Sleep(rand.Next(250, 500)); + callResp = callUrl(urlIobEnabled, true); + answ = (callResp == "OK"); + maxTry--; + } + // se NON OK riprovo ANCORA 1 volta... + if (!answ) + { + resetWebClients(); + Thread.Sleep(rand.Next(250, 1000)); + callResp = callUrl(urlIobEnabled, false); + answ = (callResp == "OK"); + } + // altri 2 + maxTry = 2; + while (maxTry > 0 && !answ) + { + Thread.Sleep(rand.Next(250, 500)); + callResp = callUrl(urlIobEnabled, false); + answ = (callResp == "OK"); + maxTry--; + } + // salvo status... + IobOnline = answ; + // se online imposto veto check a 5 x tempo reinvio... + if (answ) + { + lastIobOnline = DateTime.Now; + } + dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); + } + catch + { } + } + // verifico SE è variato stato online/offline... + if (IobOnline != answ) + { + // se ORA sono online riporto... + if (answ) + { + lgInfo("IOB ONLINE for server MP/IO"); + } + else + { + lgInfo("IOB OFFLINE for server MP/IO"); + } + } + // fix colore + if (answ) + { + parentForm.commSrvActive = 2; + } + else + { + parentForm.commSrvActive = 1; + } + } + else + { + // altrimenti passo ultimo valore noto + answ = IobOnline; + } + return answ; + } + } + + /// + /// test ping all'indirizzo impostato nei parametri + /// + /// + private IPStatus testPingServer + { + get + { + IPStatus answ = IPStatus.Unknown; ; + IPAddress address; + PingReply reply; + using (Ping pingSender = new Ping()) + { + address = IPAddress.Loopback; + int maxRetry = maxPingRetry + 1; + int numRetry = 1; ; + string ipAdrr = cIobConf.serverData.MPIP.Replace("http://", "").Replace("https://", ""); + IPAddress.TryParse(ipAdrr, out address); + reply = pingSender.Send(address, pingServerMsTimeout); + // se ho timeout riprovo... + while (reply.Status != IPStatus.Success && numRetry < maxRetry) + { + lgInfo($"Ping KO | reply: {reply.Status} --> retry"); + reply = pingSender.Send(address, pingServerMsTimeout * numRetry / 2); + numRetry++; + if (reply.Status == IPStatus.Success) + { + lgInfo("PING OK!"); + break; + } + } + } + answ = reply.Status; + return answ; + } + } + + /// + /// Effettua chiamata URL e restituisce risultato + /// + /// + /// invio in modalità async (NON GARANTITO ordine...) + /// + public static string callUrl(string URL, bool doAsync) + { + string answ = ""; + // Chiamata ASINCRONA + if (doAsync) + { + //Task resp = utils.callUrlAsync(URL); + //answ = resp.Result; + answ = utils.callUrlAsync(URL); + } + // chiamata SOLO NORMALE SINCRONA... + else + { + answ = utils.callUrl(URL); + } + return answ; + } + + /// + /// Effettua chiamata URL e restituisce risultato + /// + /// + /// + /// invio in modalità async (NON GARANTITO ordine...) + /// + public static string callUrlWithPayload(string URL, string payload, bool doAsync) + { + string answ = ""; + // Chiamata ASINCRONA + if (doAsync) + { + answ = utils.callUrlAsync(URL, payload); + } + // chiamata SOLO NORMALE SINCRONA... + else + { + answ = utils.callUrl(URL, payload); + } + return answ; + } + + public static string GetMACAddress() + { + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + String sMacAddress = string.Empty; + foreach (NetworkInterface adapter in nics) + { + if (string.IsNullOrEmpty(sMacAddress))// only return MAC Address from first card + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + //sMacAddress = adapter.GetPhysicalAddress().ToString(); + sMacAddress = string.Join(":", (from z in adapter.GetPhysicalAddress().GetAddressBytes() select z.ToString("X2")).ToArray()); + } + } + return sMacAddress; + } + + /// + /// Reset dei webclients + /// + public static void resetWebClients() + { + utils.resetWebClients(); + } + + /// + /// Effettua chaimata x split ODL + /// + /// + public bool forceSplitOdl() + { + bool fatto = false; + if (vetoSplit < DateTime.Now) + { + // imposto veto x 1 minuto ad altre chiamate... + vetoSplit = DateTime.Now.AddMinutes(1); + // eseguo SOLO SE sono online... + if (MPOnline && IobOnline) + { + string fullUrl = ""; + string rawSplit = ""; + string IOB_MULTI_CNAME = ""; + string[] elencoMulti = null; + + try + { + /*************************************************** + * Descrizione procedura (OK X SIMULATORI...) + * + * - chiamata su MP/IO + * - verifica che su DB sia abilitato AUTO ODL + * - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW + * - viene duplicato e chiuso ODL corrente + * - viene fatto partire ODL nuovo ADESSO + * - num pezzi come ODL precedente (o da media 3 ODL precedenti) + * - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL) + * - reset contapezzi PLC locale... + * + * + * + * DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE + * - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI) + * - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!) + * - viene fatto reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...) + * - viene fatto reset + nuovo ODL (con stessi articoli e quantità) su follower, SENZA avere gestione x ODL di un codice esterno (quindi registra TUTTO ma NON RITORNERA' dati non avendo link verso esterno) + * - serve NUOVA TABELLA delle macchine LEADER | FOLLOWER (1:n) e gestione da MP/IO + * + * ***************************************************/ + + if (isMulti) + { + // devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali... + IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME"); + elencoMulti = IOB_MULTI_CNAME.Split(','); + } + // se normale splitto! + if (!isMulti) + { + // invio chiamata URL x reset ODL su macchina + rawSplit = callUrl(urlForceSplit, false); + fatto = (rawSplit == "OK") ? 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}|{item}"; + rawSplit = callUrl(fullUrl, false); + } + fatto = (rawSplit == "OK") ? true : false; + } + } + catch (Exception exc) + { + lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}"); + } + // se fatto --> resetto contapezzi!!! + if (fatto) + { + contapezziPLC = 0; + contapezziIOB = 0; + } + } + else + { + lgError("Richiesto forceSplitOdl ma MP/IOB offline --> NON eseguito"); + } + } + else + { + lgError("Richiesto forceSplitOdl ma veto attivo --> NON eseguito"); + } + return fatto; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// Restituisce un payload in formato json della lista di valori ricevuta + /// + /// Tipo di URL (eventi / FLog) + /// elenco di valori da coda string salvata + /// + public string jsonPayload(urlType tipoUrl, List elencoValori) + { + string answ = ""; + if (elencoValori != null) + { + if (tipoUrl == urlType.FLog) + { + flogData currData = new flogData(); + flogJsonPayload fullObj = new flogJsonPayload(); + fullObj.fluxData = new List(); + string[] valori; + int counter = 0; + DateTime dtEve = DateTime.Now; + // inizio processando ogni valore + foreach (var item in elencoValori) + { + valori = qDecodeIN(item); + //DateTime.TryParse(valori[0], out dtEve); + CultureInfo provider = CultureInfo.InvariantCulture; + DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); + int.TryParse(valori[3], out counter); + currData = new flogData() + { + flux = valori[1], + valore = valori[2], + dtEve = dtEve, + dtCurr = DateTime.Now, + cnt = counter + }; + fullObj.fluxData.Add(currData); + } + // conversione finale + try + { + answ = JsonConvert.SerializeObject(fullObj); + } + catch (Exception exc) + { + lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); + } + } + else + { + evData currData = new evData(); + evJsonPayload fullObj = new evJsonPayload(); + fullObj.eventList = new List(); + string[] valori; + int counter = 0; + DateTime dtEve = DateTime.Now; + // inizio processando ogni valore + foreach (var item in elencoValori) + { + valori = qDecodeIN(item); + //DateTime.TryParse(valori[0], out dtEve); + CultureInfo provider = CultureInfo.InvariantCulture; + DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve); + int.TryParse(valori[2], out counter); + currData = new evData() + { + valore = valori[1], + dtEve = dtEve, + dtCurr = DateTime.Now, + cnt = counter + }; + fullObj.eventList.Add(currData); + } + // conversione finale + try + { + answ = JsonConvert.SerializeObject(fullObj); + } + catch (Exception exc) + { + lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}"); + } + } + } + return answ; + } + + /// + /// Verifica e processing x gestione ODL automatica + /// + public void processAutoOdl() + { + bool fatto = false; + if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL"))) + { + 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) + { + // carico i parametri di configurazione x reset ODL... + string CHANGE_ODL_HOURS = getOptPar("CHANGE_ODL_IDLE_MIN"); + 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) + { #if false /*************************************************** * Descrizione procedura (OK X SIMULATORI...) @@ -3656,1044 +3421,1279 @@ namespace IOB_WIN fatto = (rawSplit == "OK") ? true : false; } #endif - fatto = forceSplitOdl(); - lgInfo("Chiamata: processAutoOdl --> forceSplitODL"); + fatto = forceSplitOdl(); + lgInfo("Chiamata: processAutoOdl --> forceSplitODL"); + } + } + } + } } - } } - } - } - } - // loggo se fatto - if (fatto) - { - lgInfo($"Effettuato processAutoOdl"); - } - } - - /// - /// Effettua processing degli allarmi CNC SE disponibili - /// - public void processCncAlarms() - { - if (utils.CRB("enableAlarms")) - { - Dictionary currAlarms = new Dictionary(); - if (connectionOk) - { - currAlarms = getCncAlarms(); - } - else - { - lgError("Errore connessione mancante x getCncAlarms"); - } - // verifico SE sia cambiato il programma... - if (currAlarms.Count > 0) - { - try - { - if (lastAlarm != currAlarms["CNC_ALARM"]) + // loggo se fatto + if (fatto) { - // salvo! - lastAlarm = currAlarms["CNC_ALARM"]; - // per ogni valore del dizionario mostro ed accodo! - string sVal = ""; - 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)); - } + lgInfo($"Effettuato processAutoOdl"); } - } - 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 del recupero delle speed (RPM, feedrate) degli assi - /// - public void processDynData() - { - bool enableByApp = utils.CRB("enableDynData"); - bool enableByIob = (getOptPar("ENABLE_DYN_DATA") == "TRUE"); - bool forceSendByIob = (getOptPar("FORCE_DYN_DATA") == "TRUE"); - Dictionary currDynData = new Dictionary(); - - if (enableByApp || enableByIob) - { - lgInfo("Inizio processDynData"); - if (connectionOk) + /// + /// Effettua processing degli allarmi CNC SE disponibili + /// + public void processCncAlarms() { - currDynData = getDynData(); - } - else - { - lgError("Errore connessione mancante x getDynData"); - } - try - { - string sVal = ""; - // se richiesto send diretto... - if (forceSendByIob) - { - // per ogni valore del dizionario mostro ed accodo! - foreach (var item in currDynData) + if (utils.CRB("enableAlarms")) { - sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); + 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 + { + if (lastAlarm != currAlarms["CNC_ALARM"]) + { + // salvo! + lastAlarm = currAlarms["CNC_ALARM"]; + // per ogni valore del dizionario mostro ed accodo! + string sVal = ""; + 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)); + } + } + } + catch (Exception exc) + { + lgError("Eccezione in processCncAlarms{0}{1}", Environment.NewLine, exc); + } + } } - } - // 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) + } + + /// + /// Effettua processing contapezzi (ed eventualmente alza il bit di contapezzo...) + /// + public virtual void processContapezzi() + { } + + /// + /// Effettua processing del recupero delle speed (RPM, feedrate) degli assi + /// + 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) { - sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); - // chiamo accodamento... - accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); + lgInfo("Inizio processDynData"); + if (connectionOk) + { + currDynData = getDynData(); + } + else + { + lgError("Errore connessione mancante x getDynData"); + } + try + { + string sVal = ""; + // se richiesto send diretto... + if (forceSendByIob) + { + // per ogni valore del dizionario mostro ed accodo! + foreach (var item in currDynData) + { + sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); + } + } + // altrimenti verifico SE sia cambiato il valore dei DynData... + else if (lastDynDataCtrlVal != currDynData["DYNDATA"]) + { + // salvo! + lastDynDataCtrlVal = currDynData["DYNDATA"]; + // per ogni valore del dizionario mostro ed accodo! + foreach (var item in currDynData) + { + sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value); + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog(item.Key, item.Value)); + } + } + // salvo array... + lastDynData = currDynData; + } + catch (Exception exc) + { + lgError(exc, "Eccezione in processDynData"); + } } - } - // salvo array... - lastDynData = currDynData; - } - catch (Exception exc) - { - lgError(exc, "Eccezione in processDynData"); - } - } - } - - /// - /// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC... - /// - public void processMemoryDiscon() - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // controllo contatore invio "keepalive"... invio solo a scadenza - if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec")) - { - // resetto tutti i vlaori BYTE IN/PREV/OUT... così invio macchina spenta... - B_input = 0; - B_output = 0; - B_previous = 0; - accodaSigIN(ref currDispData); - // update controllo - lastDisconnCheck = DateTime.Now; - } - raiseRefresh(currDispData); - } - - /// - /// Effettua processing mode/status (EDIT/MDI/...) - /// - public virtual void processMode() - { } - - /// - /// Effettua processing ALTRI contatori/parametri (ed invia ad IO) - /// - public virtual void processOtherCounters() - { } - - /// - /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid) - /// - public virtual void processOverride() - { - bool enableByApp = utils.CRB("enableOverrides"); - bool enableByIob = (getOptPar("ENABLE_OVERRIDES") == "TRUE"); - Dictionary currOverride = new Dictionary(); - if (enableByApp || enableByIob) - { - lgInfo("Inizio processOverride"); - if (connectionOk) - { - currOverride = getOverrides(); - } - else - { - lgError("Errore connessione mancante x getOverrides"); } - // SE sono connesso... - if (connectionOk) + /// + /// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC... + /// + public void processMemoryDiscon() { - // se HO dei valori override... - if (currOverride.Count > 0) - { - // verifico SE sia cambiato il programma... - if (lastOverrideFS != currOverride["FEED_OVER"] || lastOverrideRapid != currOverride["RAPID_OVER"]) + // init obj display + newDisplayData currDispData = new newDisplayData(); + // controllo contatore invio "keepalive"... invio solo a scadenza + if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec")) { - // 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)); - } + // resetto tutti i vlaori BYTE IN/PREV/OUT... così invio macchina spenta... + B_input = 0; + B_output = 0; + B_previous = 0; + accodaSigIN(ref currDispData); + // update controllo + lastDisconnCheck = DateTime.Now; } - } - } - } - } - - /// - /// 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 lettura dati - /// Parametri da aggiornare x display in form - /// - public virtual void readAllData(ref newDisplayData currDispData) - { - if (currDispData == null) - { - currDispData = new newDisplayData(); - } - try - { - if (DemoIn) - { - // segnalo che sono in Demo - currDispData.semIn = Semaforo.SV; - } - if (connectionOk) - { - readSemafori(ref currDispData); - lastReadPLC = DateTime.Now; - } - else - { - lgError("Errore connessione mancante x readSemafori"); + raiseRefresh(currDispData); } - nReadIN++; - // aggiorno valore mostrato... - displayRawData(ref currDispData); - } - catch - { - currDispData.semIn = Semaforo.SR; - } - raiseRefresh(currDispData); - } + /// + /// Effettua processing mode/status (EDIT/MDI/...) + /// + public virtual void processMode() + { } - /// - /// Effettua lettura semafori principale - /// Parametri da aggiornare x display in form - /// - public virtual void readSemafori(ref newDisplayData currDispData) - { - lastReadPLC = DateTime.Now; - } + /// + /// Effettua processing ALTRI contatori/parametri (ed invia ad IO) + /// + public virtual void processOtherCounters() + { } - /// - /// metodo dummy x salvataggio aree memoria conf x CN - /// - /// tipo di DUMP - public virtual void saveMemDump(dumpType tipo) - { - } - - /// - /// 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++) + /// + /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid) + /// + public virtual void processOverride() { - if (QueueIN.Count > 0) - { - string currVal = ""; - // se online provo + 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)); + } + } + } + } + } + } + + /// + /// 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 lettura dati + /// Parametri da aggiornare x display in form + /// + public virtual void readAllData(ref newDisplayData currDispData) + { + if (currDispData == null) + { + currDispData = new newDisplayData(); + } + try + { + if (DemoIn) + { + // segnalo che sono in Demo + currDispData.semIn = Semaforo.SV; + } + if (connectionOk) + { + readSemafori(ref currDispData); + lastReadPLC = DateTime.Now; + } + 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; + } + + /// + /// metodo dummy x salvataggio aree memoria conf x CN + /// + /// tipo di DUMP + public virtual void saveMemDump(dumpType tipo) + { + } + + /// + /// Processo la coda SignalIN... + /// + public void svuotaCodaSignIN() + { + // verifico SE la coda abbia dei valori... + if (QueueIN.Count > 0) + { + // invio pacchetto di dati (max da conf) + for (int i = 0; i < nMaxSend; i++) + { + if (QueueIN.Count > 0) + { + string currVal = ""; + // se online provo + if (MPOnline) + { + if (IobOnline) + { + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueIN.Count > 1) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueIN.Count > maxJsonDataEv) + { + // prendoi primi maxJsonDataValori + for (int j = 0; j < maxJsonDataEv; j++) + { + QueueIN.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.SignIN, listaValori); + } + else + { + // invio in blocco + listaValori = QueueIN.ToList(); + // invio + sendDataBlock(urlType.SignIN, listaValori); + // svuoto! + QueueIN = new ConcurrentQueue(); + } + } + else + { + // INVIO SINGOLO...!!! + QueueIN.TryDequeue(out currVal); + sendToMoonPro(urlType.SignIN, currVal); + } + // salvo come last signal in il currVal ultimo... SE !="" + if (!string.IsNullOrEmpty(currVal)) + { + lastSignInVal = currVal; + } + } + else + { + break; + } + } + else + { + break; + } + } + else + { + break; + } + } + } + } + + /// + /// Effettua verifica se abilitato invio pezzi in blocco e nel caso + /// - invio in blocco pezzi + /// - aggiornamento del contapezzi (passato come ref) x nuovo valore post invio + /// + public void trySendPzCountBlock() + { + // in primis HA SENSO procedere SOLO SE server MP è Online... if (MPOnline) { - if (IobOnline) - { - // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueIN.Count > 1) + // SOLO SE online la macchina... + if (IobOnline) { - 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++) + int numIncr = 0; + int qtyAdded = 0; + // verifico se la funzione SIA abilitata + if (enableSendPzCountBlock) { - QueueIN.TryDequeue(out currVal); - listaValori.Add(currVal); + int delta = contapezziPLC - contapezziIOB; + // se è abilitata verifico differenza: se ho DELTA > minSendPzCountBlock --> invio un blocco <= maxSendPzCountBlock + if (delta > minSendPzCountBlock) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // resta indietro di ALMENO minSendPzCountBlock pezzi x recuperare 1:1... + numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock; + // invio il num max di pezzi ammesso in blocco! + lastUrl = $"{urlAddPzCount}{numIncr}"; + string resp = utils.callUrlNow(lastUrl); + if (!string.IsNullOrEmpty(resp)) + { + // dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti) + int.TryParse(resp, out qtyAdded); + if (qtyAdded > 0) + { + // aggiorno IL MIO contapezzi... + contapezziIOB += qtyAdded; + lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}"); + // invio conferma contapezzi.. + string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); + // verifica se tutto OK + if (retVal != contapezziIOB.ToString()) + { + // errore salvataggio contapezzi + lgInfo($"trySendPzCountBlock: errore salvataggio contapezzi: contapezziIOB {contapezziIOB} | risposta: {retVal}"); + } + } + else + { + lgError($"Richiesto incremento {numIncr} ma NON registrato su server MP-IO"); + } + } + currDispData.newUrlCallData = lastUrl; + currDispData.counter = contapezziIOB; + currDispData.semOut = Semaforo.SV; + raiseRefresh(currDispData); + } } - sendDataBlock(urlType.SignIN, listaValori); - } - else - { - // invio in blocco - listaValori = QueueIN.ToList(); - // invio - sendDataBlock(urlType.SignIN, listaValori); - // svuoto! - QueueIN = new ConcurrentQueue(); - } } else { - // INVIO SINGOLO...!!! - QueueIN.TryDequeue(out currVal); - sendToMoonPro(urlType.SignIN, currVal); + lgInfo("Impossibile trySendPzCountBlock: IobOnline è false"); } - // salvo come last signal in il currVal ultimo... SE !="" - if (!string.IsNullOrEmpty(currVal)) - { - lastSignInVal = currVal; - } - } - else - { - break; - } } else { - break; + lgInfo("Impossibile trySendPzCountBlock: MPOnline è false"); } - } - else - { - break; - } } - } - } - /// - /// Effettua verifica se abilitato invio pezzi in blocco e nel caso - /// - invio in blocco pezzi - /// - aggiornamento del contapezzi (passato come ref) x nuovo valore post invio - /// - public void trySendPzCountBlock() - { - // in primis HA SENSO procedere SOLO SE server MP è Online... - if (MPOnline) - { - // SOLO SE online la macchina... - if (IobOnline) + /// + /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi + /// + /// + /// + public string urlDataBlock(urlType tipoUrl) { - 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) + // verifico la parte di link "tipoComando" + string tipoComando = tipoUrl == urlType.FLog ? cIobConf.serverData.CMDFLOG_JSON : cIobConf.serverData.CMDBASE_JSON; + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, tipoComando, cIobConf.codIOB); + return answ; + } + + /// + /// Fornisce URL di tipo FluxLog + /// + /// valore salvato in coda nel formato dtEve#flux#valore#counter + /// + public string urlFLog(string queueVal) + { + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDFLOG); + // decodifica valore! + string[] valori = qDecodeIN(queueVal); + // aggiungo macchina e valore... + answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]); + // aggiondo dataOra evento e corrente + contatore... + answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]); + return answ; + } + + /// + /// Fornisce URL INPUT per i parametri richiesti + /// + /// valore salvato in coda formato dtEve#valore#counter + /// + public string urlInput(string queueVal) + { + // URL base x input + string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDBASE); + // decodifica valore! + string[] valori = qDecodeIN(queueVal); + // aggiungo macchina e valore... + answ += string.Format(@"{0}?valore={1}", cIobConf.codIOB, valori[1]); + // aggiondo dataOra evento e corrente + contatore... + answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[2]); + return answ; + } + + /// + /// Esegue filtraggio dati x bit blinking!!! + /// + private void filterData() + { + // effettuo filtraggio dei valori letti... inizializzo OUT! + B_output = 0; + // in primis verifico SE ci siano bit blinkng... se non ci sono OUT=IN... + if (cIobConf.BLINK_FILT == 0) { - // init obj display - newDisplayData currDispData = new newDisplayData(); - // resta indietro di ALMENO minSendPzCountBlock pezzi x recuperare 1:1... - numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock; - // invio il num max di pezzi ammesso in blocco! - lastUrl = $"{urlAddPzCount}{numIncr}"; - string resp = utils.callUrlNow(lastUrl); - if (!string.IsNullOrEmpty(resp)) - { - // dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti) - int.TryParse(resp, out qtyAdded); - if (qtyAdded > 0) + 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++) { - // aggiorno IL MIO contapezzi... - contapezziIOB += qtyAdded; - lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}"); - // invio conferma contapezzi.. - string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); - // verifica se tutto OK - if (retVal != contapezziIOB.ToString()) - { - // errore salvataggio contapezzi - lgInfo($"trySendPzCountBlock: errore salvataggio contapezzi: contapezziIOB {contapezziIOB} | risposta: {retVal}"); - } + // SE 1... impostiamo contatori al MAX + if (bitsUp[i] == 1) + { + // se era zero indico START blink... + if (i_counters[i] == 0) + { + lgInfo("START BLINK: B{0}", i); + } + // imposto comunque contatore al cambio fronte... + i_counters[i] = cIobConf.MAX_COUNTER_BLINK; + } + } + + // quelli che sono zero... LI RECUPERO E LI PROCESSO... + int iZero = ~B_input & cIobConf.BLINK_FILT; + BitArray bBlinkEnd = new BitArray(new byte[] { Convert.ToByte(iZero) }); + int[] bitsDown = bBlinkEnd.Cast().Select(bit => bit ? 1 : 0).ToArray(); + for (int i = 0; i < bitsDown.Length; i++) + { + // se era a zero (invertito...) + if (bitsDown[i] == 1) + { + // SE è in corso il conteggio... + if (i_counters[i] > 0) + { + // decremento! + i_counters[i] -= 1; + // se è zero NON faccio nulla, altrimenti SOMMO... + if (i_counters[i] > 0) + { + B_output += 1 << i; + } + else + { + lgInfo("END BLINK: B{0}", i); + } + } + } + } + } + } + + /// + /// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo... + /// + private void processAllMemory() + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + // in primis SALVO valori previous/precedenti + B_previous = B_output; + // poi faccio lettura NUOVI valori + readAllData(ref currDispData); + // eseguo il filtering dei valori (per i bit "blinking") + filterData(); + // effettuo confronto valori vecchi/nuovi... SE trovo variazione OPPURE se è passato + di un timeout di controllo... + if (B_output != B_previous) + { + accodaSigIN(ref currDispData); + } + raiseRefresh(currDispData); + } + + /// + /// Effettua gestioen programma: legge e mostra su display... + /// + private void processProgram() + { + string currPrgName = ""; + // se abilitata lettura prgName + if (enablePrgName) + { + if (connectionOk) + { + currPrgName = getPrgName(); } else { - lgError($"Richiesto incremento {numIncr} ma NON registrato su server MP-IO"); + lgError("Errore connessione mancante x getPrgName"); } - } - currDispData.newUrlCallData = lastUrl; - currDispData.counter = contapezziIOB; - currDispData.semOut = Semaforo.SV; - raiseRefresh(currDispData); } - } - } - else - { - lgInfo("Impossibile trySendPzCountBlock: IobOnline è false"); - } - } - else - { - lgInfo("Impossibile trySendPzCountBlock: MPOnline è false"); - } - } - - /// - /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi - /// - /// - /// - public string urlDataBlock(urlType tipoUrl) - { - // verifico la parte di link "tipoComando" - string tipoComando = tipoUrl == urlType.FLog ? cIobConf.serverData.CMDFLOG_JSON : cIobConf.serverData.CMDBASE_JSON; - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, tipoComando, cIobConf.codIOB); - return answ; - } - - /// - /// Fornisce URL di tipo FluxLog - /// - /// valore salvato in coda nel formato dtEve#flux#valore#counter - /// - public string urlFLog(string queueVal) - { - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDFLOG); - // decodifica valore! - string[] valori = qDecodeIN(queueVal); - // aggiungo macchina e valore... - answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]); - // aggiondo dataOra evento e corrente + contatore... - answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]); - return answ; - } - - /// - /// Fornisce URL INPUT per i parametri richiesti - /// - /// valore salvato in coda formato dtEve#valore#counter - /// - public string urlInput(string queueVal) - { - // URL base x input - string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDBASE); - // decodifica valore! - string[] valori = qDecodeIN(queueVal); - // aggiungo macchina e valore... - answ += string.Format(@"{0}?valore={1}", cIobConf.codIOB, valori[1]); - // aggiondo dataOra evento e corrente + contatore... - answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[2]); - return answ; - } - - #endregion IOB METHODS - - #region gestione VC e postprocessing - - /// - /// Recupera la VC x TS, svuotando lista e resettando periodo partenza - /// - /// Nome della VC - /// Reimposta e resetta array VC - /// - public double getVal_TSVC(string VCName, bool doReset) - { - double answ = 0; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - // !!!FARE!!! vero calcolo... x ora FIX a MAX... - foreach (var item in TSVC_Data[VCName].dataArray) - { - answ = item > answ ? item : answ; - } - // ora resetto... SE richiesto... - if (doReset) - { - TSVC_Data[VCName].dataArray = new List(); - TSVC_Data[VCName].DTStart = DateTime.Now; - } - } - return answ; - } - - /// - /// Recupera la VC x TS, svuotando lista e resettando periodo partenza - /// - /// Nome della VC - /// Reimposta e resetta array VC - /// - public int getVal_TSVC_int(string VCName, bool doReset) - { - int answ = 0; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - // !!!FARE!!! vero calcolo... x ora FIX a MAX... - foreach (var item in TSVC_Data[VCName].dataArray) - { - answ = (int)item > answ ? (int)item : answ; - } - // ora resetto... SE richiesto.. - if (doReset) - { - TSVC_Data[VCName].dataArray = new List(); - TSVC_Data[VCName].DTStart = DateTime.Now; - } - } - return answ; - } - - /// - /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza) - /// - /// - /// - /// - public bool monItem2Send(string newVal, DynDataItem item) - { - bool answ = false; - if (item != null) - { - // controllo in base al tipo di function... - switch (item.func) - { - case "SAMPLE": - // controllo se scaduto sample period... - if (item.DTScad < DateTime.Now) + else { - answ = true; + currPrgName = lastPrgName; } - break; - - case "CHANGE": - default: - // controllo se scaduto o se variato... - if (newVal != item.actVal || item.DTScad < DateTime.Now) + // verifico SE sia cambiato il programma... + if (lastPrgName != currPrgName) { - // aggiorno scadenza e che vada inviato - answ = true; + // salvo! + lastPrgName = currPrgName; + string sVal = string.Format("[PROG]{0}", currPrgName); + + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog("PROG", currPrgName)); } - break; } - } - return answ; - } - /// - /// - /// - /// - /// - /// - /// - public void saveValue(ref Dictionary outVal, double valore, string chiave) - { - //check obj preliminare - if (outVal == null) - { - outVal = new Dictionary(); - } - bool scaduto = stackVal_TSVC(chiave, valore); - // recupero VC - valore = getVal_TSVC(chiave, scaduto); - if (scaduto) - { - outVal.Add(chiave, $"{valore}"); - } - LastTSVC[chiave] = valore; - } - - /// - /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e restituisce bool val se SCADUTO periodo controllo - /// - /// Nome della VC - /// Valore (nuovo) delal VC - /// - public bool stackVal_TSVC(string VCName, double VCVal) - { - bool answ = false; - // cerco VC... - if (TSVC_Data.ContainsKey(VCName)) - { - TSVC_Data[VCName].dataArray.Add(VCVal); - // ora verifico scadenza... - if (TSVC_Data[VCName].DTStart.AddSeconds(TSVC_Data[VCName].Period) < DateTime.Now) + /// + /// Processo lettura dati sysinfo + /// + private void processSysInfo() { - answ = true; - } - } - return answ; - } - - #endregion gestione VC e postprocessing - - #region gestione code (accumulo, invio) - - /// - /// Fornisce il valore letto da BITMAP in formato valido x messa in coda nel formato dtEve#value#cont - /// - protected string qEncodeIN - { - get - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN); - } - catch - { } - return answ; - } - } - - /// - /// Decodifica valore della coda IN nel formato - /// answ[0]=dtEve - /// answ[1]=valore - /// answ[2]=counter - /// - /// dtEve + '#' + value + '#' + cont - /// - protected static string[] qDecodeIN(string queueVal) - { - string[] answ = null; - if (!string.IsNullOrEmpty(queueVal)) - { - try - { - answ = queueVal.Split('#'); - } - catch - { } - } - return answ; - } - - /// - /// Accumula in coda i valori ALARM e logga... - /// - /// VALORE RAW (x display) - /// VALORE già processato con qEncodeFLog(...) - public void accodaAlarmLog(string val, string encodedVal) - { - // mostro dati variati letti... - displayOtherData(val); - // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); - // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati... - - // loggo! - lgInfo(string.Format("[QUEUE-ALARM-LOG] {0}", encodedVal)); - counterFLog++; - if (counterFLog > 9999) - { - counterFLog = 0; - } - } - - /// - /// Accumula in coda i valori Flux Log e logga... - /// - /// VALORE RAW (x display) - /// VALORE già processato con qEncodeFLog(...) - public void accodaFLog(string val, string encodedVal) - { - // mostro dati variati letti... - displayOtherData(val); - // --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); - // se abilitato controllo coda FLog (superiore a 0...) - if (maxQueueFLog > 0) - { - // se ho una coda superiore a max ammesso - if (QueueFLog.Count > maxQueueFLog) - { - // elimino valori iniziali fino a tornare al max ammesso... - while (QueueFLog.Count > maxQueueFLog) - { - string currVal = ""; - QueueFLog.TryDequeue(out currVal); - lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}"); - } - } - } - // loggo! - lgInfo(string.Format("[QUEUE-FLOG] {0}", encodedVal)); - counterFLog++; - if (counterFLog > 9999) - { - counterFLog = 0; - } - } - - /// - /// Accumula in coda i valori Signal IN e logga... - /// Parametri da aggiornare x display in form - /// - public void accodaSigIN(ref newDisplayData currDispData) - { - // mostro dati variati letti... - displayInData(ref currDispData); - // --> accodo (valore già formattato)! - QueueIN.Enqueue(qEncodeIN); - // loggo! - lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN)); - // aggiorno counters ed eventuale reset - nReadFilt++; - if (nReadFilt > int.MaxValue - 1) - { - nReadFilt = 0; // per evitare buffer overflow... - } - - counterSigIN++; - if (counterSigIN > 9999) - { - counterSigIN = 0; - } - } - - /// - /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont - /// Flusso dati - /// Valore da salvare - /// - public string qEncodeFLog(string flusso, string valore) - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", DateTime.Now, flusso, valore, counterFLog); - } - catch - { } - return answ; - } - - /// - /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont - /// DataOra evento registrato - /// Flusso dati - /// Valore da salvare - /// - public string qEncodeFLog(DateTime eventDT, string flusso, string valore) - { - string answ = ""; - try - { - answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", eventDT, flusso, valore, counterFLog); - } - catch - { } - return answ; - } - - /// - /// Invia una LISTA di valori - /// - /// - /// - public void sendDataBlock(urlType tipoUrl, List listQueueVal) - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - if (listQueueVal != null) - { - try - { - // recupero e formatto URL dati da coda... - lastUrl = urlDataBlock(tipoUrl); - // in base al tipo di dato compongo il payload Json da inviare - string payload = jsonPayload(tipoUrl, listQueueVal); - // async a true SE FLog - bool doAsync = tipoUrl == urlType.FLog ? true : false; - // se NON sono in demo effettuo invio! - if (!DemoOut) - { - // SE server alive... - if (checkServerAlive) + if (utils.CRB("enableSysInfo")) { - // chiamo URL! - string answ = callUrlWithPayload(lastUrl, payload, doAsync); - // loggo! - lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}"); - // se "OK" verde, altrimenti errore --> ROSSO - if (answ.Contains("OK")) - { - currDispData.semOut = Semaforo.SV; - // se oltre 1 min NON era online --> check pezzi! - if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + Dictionary currSysInfo = new Dictionary(); + + if (connectionOk) { - lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); - pzCntReload(true); + 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)); + } } - lastIobOnline = DateTime.Now; - } - else - { - currDispData.semOut = Semaforo.SR; - } } - else + } + + /// + /// Processo la coda FLog... + /// + private void svuotaCodaFLog() + { + //controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! + if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) { - lgInfo($"[SERVER KO] {listQueueVal.Count}"); + string wdStatus = "elapsed"; + string sVal = string.Format("[WDST]{0}", wdStatus); + // chiamo accodamento... + accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); + lastWatchDog = DateTime.Now; } - } - else - { - currDispData.semOut = Semaforo.SV; + // verifico SE la coda abbia dei valori... + if (QueueFLog.Count > 0) + { + // invio pacchetto di dati (max da conf) + for (int i = 0; i < nMaxSend; i++) + { + // SE ho qualcosa in coda... + if (QueueFLog.Count > 0) + { + string currVal = ""; + if (MPOnline) + { + if (IobOnline) + { + // se ho + di 2 elementi in coda --> uso invio JSON in blocco... + if (QueueFLog.Count > 1) + { + List listaValori = new List(); + // se ho + di maxJsonData elementi --> invio un set di dati alla volta + if (QueueFLog.Count > maxJsonData) + { + // prendoi primi maxJsonDataValori + for (int j = 0; j < maxJsonData; j++) + { + QueueFLog.TryDequeue(out currVal); + listaValori.Add(currVal); + } + sendDataBlock(urlType.FLog, listaValori); + } + else + { + // invio in blocco + listaValori = QueueFLog.ToList(); + // invio + sendDataBlock(urlType.FLog, listaValori); + // svuoto! + QueueFLog = new ConcurrentQueue(); + } + } + else + { + // INVIO SINGOLO...!!! + QueueFLog.TryDequeue(out currVal); + sendToMoonPro(urlType.FLog, currVal); + } + } + else + { + break; + } + } + else + { + break; + } + } + else + { + break; + } + } + } + } + + #endregion IOB METHODS + + #region gestione VC e postprocessing + + /// + /// Recupera la VC x TS, svuotando lista e resettando periodo partenza + /// + /// Nome della VC + /// Reimposta e resetta array VC + /// + public double getVal_TSVC(string VCName, bool doReset) + { + double answ = 0; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) + { + // !!!FARE!!! vero calcolo... x ora FIX a MAX... + foreach (var item in TSVC_Data[VCName].dataArray) + { + answ = item > answ ? item : answ; + } + // ora resetto... SE richiesto... + if (doReset) + { + TSVC_Data[VCName].dataArray = new List(); + TSVC_Data[VCName].DTStart = DateTime.Now; + } + } + return answ; + } + + /// + /// Recupera la VC x TS, svuotando lista e resettando periodo partenza + /// + /// Nome della VC + /// Reimposta e resetta array VC + /// + public int getVal_TSVC_int(string VCName, bool doReset) + { + int answ = 0; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) + { + // !!!FARE!!! vero calcolo... x ora FIX a MAX... + foreach (var item in TSVC_Data[VCName].dataArray) + { + answ = (int)item > answ ? (int)item : answ; + } + // ora resetto... SE richiesto.. + if (doReset) + { + TSVC_Data[VCName].dataArray = new List(); + TSVC_Data[VCName].DTStart = DateTime.Now; + } + } + return answ; + } + + /// + /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza) + /// + /// + /// + /// + public bool monItem2Send(string newVal, DynDataItem item) + { + bool answ = false; + if (item != null) + { + // controllo in base al tipo di function... + switch (item.func) + { + case "SAMPLE": + // controllo se scaduto sample period... + if (item.DTScad < DateTime.Now) + { + answ = true; + } + break; + + case "CHANGE": + default: + // controllo se scaduto o se variato... + if (newVal != item.actVal || item.DTScad < DateTime.Now) + { + // aggiorno scadenza e che vada inviato + answ = true; + } + break; + } + } + return answ; + } + + /// + /// + /// + /// + /// + /// + /// + public void saveValue(ref Dictionary outVal, double valore, string chiave) + { + //check obj preliminare + if (outVal == null) + { + outVal = new Dictionary(); + } + bool scaduto = stackVal_TSVC(chiave, valore); + // recupero VC + valore = getVal_TSVC(chiave, scaduto); + if (scaduto) + { + outVal.Add(chiave, $"{valore}"); + } + LastTSVC[chiave] = valore; + } + + /// + /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e restituisce bool val se SCADUTO periodo controllo + /// + /// Nome della VC + /// Valore (nuovo) delal VC + /// + public bool stackVal_TSVC(string VCName, double VCVal) + { + bool answ = false; + // cerco VC... + if (TSVC_Data.ContainsKey(VCName)) + { + TSVC_Data[VCName].dataArray.Add(VCVal); + // ora verifico scadenza... + if (TSVC_Data[VCName].DTStart.AddSeconds(TSVC_Data[VCName].Period) < DateTime.Now) + { + answ = true; + } + } + return answ; + } + + #endregion gestione VC e postprocessing + + #region gestione code (accumulo, invio) + + /// + /// Fornisce il valore letto da BITMAP in formato valido x messa in coda nel formato dtEve#value#cont + /// + protected string qEncodeIN + { + get + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN); + } + catch + { } + return answ; + } + } + + /// + /// Accumula in coda i valori ALARM e logga... + /// + /// VALORE RAW (x display) + /// VALORE già processato con qEncodeFLog(...) + public void accodaAlarmLog(string val, string encodedVal) + { + // mostro dati variati letti... + displayOtherData(val); + // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! + QueueFLog.Enqueue(encodedVal); + // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati... + // loggo! - lgInfo($"{listQueueVal.Count} records --> [SIM]"); - } - nSendOut += listQueueVal.Count; - // riporto cosa inviato - currDispData.newUrlCallData = lastUrl; - // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; + lgInfo(string.Format("[QUEUE-ALARM-LOG] {0}", encodedVal)); + counterFLog++; + if (counterFLog > 9999) + { + counterFLog = 0; + } } - catch - { - currDispData.semOut = Semaforo.SR; - } - } - raiseRefresh(currDispData); - } - /// - /// Effettua invio a MoonPro del valore richiesto - /// - /// - /// Valore da trasmettere: es - /// INPUT: lo status rilevato in HEX - /// FLog: il valore da trasmettere per il flusso indicato - public void sendToMoonPro(urlType tipoUrl, string queueVal) - { - // init obj display - newDisplayData currDispData = new newDisplayData(); - try - { - // recupero e formatto URL dati da coda... - switch (tipoUrl) + /// + /// 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) { - 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); + // mostro dati variati letti... + displayOtherData(val); + // --> accodo (valore già formattato)! + QueueFLog.Enqueue(encodedVal); + // se abilitato controllo coda FLog (superiore a 0...) + if (maxQueueFLog > 0) + { + // se ho una coda superiore a max ammesso + if (QueueFLog.Count > maxQueueFLog) + { + // elimino valori iniziali fino a tornare al max ammesso... + while (QueueFLog.Count > maxQueueFLog) + { + string currVal = ""; + QueueFLog.TryDequeue(out currVal); + lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}"); + } + } + } // loggo! - lgInfo(string.Format("[SEND] {0} -> {1}", queueVal, answ)); - // se oltre 1 min NON era online --> check pezzi! - if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + lgInfo(string.Format("[QUEUE-FLOG] {0}", encodedVal)); + counterFLog++; + if (counterFLog > 9999) { - lgInfo($"sendToMoonPro --> offline timeaout ({lastIobOnline}) --> pzCntReload(true)"); - pzCntReload(true); + counterFLog = 0; } - // se richiesto effettuo refresh contapezzi - if (needRefreshPzCount && !isMulti) - { - pzCntReload(true); - needRefreshPzCount = false; - } - lastIobOnline = DateTime.Now; - // se "OK" verde, altrimenti errore --> ROSSO - if (answ == "OK") - { - currDispData.semOut = Semaforo.SV; - } - else - { - currDispData.semOut = Semaforo.SR; - } - } - else - { - lgInfo(string.Format("[SERVER KO] {0}", queueVal)); - } } - else + + /// + /// Accumula in coda i valori Signal IN e logga... + /// Parametri da aggiornare x display in form + /// + public void accodaSigIN(ref newDisplayData currDispData) { - currDispData.semOut = Semaforo.SV; - // loggo! - lgInfo(string.Format("{0} -> [SIM]", queueVal)); + // mostro dati variati letti... + displayInData(ref currDispData); + // --> accodo (valore già formattato)! + QueueIN.Enqueue(qEncodeIN); + // loggo! + lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN)); + // aggiorno counters ed eventuale reset + nReadFilt++; + if (nReadFilt > int.MaxValue - 1) + { + nReadFilt = 0; // per evitare buffer overflow... + } + + counterSigIN++; + if (counterSigIN > 9999) + { + counterSigIN = 0; + } } - nSendOut++; - // riporto cosa inviato + + /// + /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont + /// Flusso dati + /// Valore da salvare + /// + public string qEncodeFLog(string flusso, string valore) + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", DateTime.Now, flusso, valore, counterFLog); + } + catch + { } + return answ; + } + + /// + /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont + /// DataOra evento registrato + /// Flusso dati + /// Valore da salvare + /// + public string qEncodeFLog(DateTime eventDT, string flusso, string valore) + { + string answ = ""; + try + { + answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", eventDT, flusso, valore, counterFLog); + } + catch + { } + return answ; + } + + /// + /// Invia una LISTA di valori + /// + /// + /// + public void sendDataBlock(urlType tipoUrl, List listQueueVal) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + if (listQueueVal != null) + { + try + { + // recupero e formatto URL dati da coda... + lastUrl = urlDataBlock(tipoUrl); + // in base al tipo di dato compongo il payload Json da inviare + string payload = jsonPayload(tipoUrl, listQueueVal); + // async a true SE FLog + bool doAsync = tipoUrl == urlType.FLog ? true : false; + // se NON sono in demo effettuo invio! + if (!DemoOut) + { + // SE server alive... + if (checkServerAlive) + { + // chiamo URL! + string answ = callUrlWithPayload(lastUrl, payload, doAsync); + // loggo! + lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}"); + // se "OK" verde, altrimenti errore --> ROSSO + if (answ.Contains("OK")) + { + currDispData.semOut = Semaforo.SV; + // se oltre 1 min NON era online --> check pezzi! + if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + { + lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); + pzCntReload(true); + } + lastIobOnline = DateTime.Now; + } + else + { + currDispData.semOut = Semaforo.SR; + } + } + else + { + lgInfo($"[SERVER KO] {listQueueVal.Count}"); + } + } + else + { + currDispData.semOut = Semaforo.SV; + // loggo! + lgInfo($"{listQueueVal.Count} records --> [SIM]"); + } + nSendOut += listQueueVal.Count; + // riporto cosa inviato + currDispData.newUrlCallData = lastUrl; + // aggiorno data ultimo watchdog... + lastWatchDog = DateTime.Now; + } + catch + { + currDispData.semOut = Semaforo.SR; + } + } + raiseRefresh(currDispData); + } + + /// + /// Effettua invio a MoonPro del valore richiesto + /// + /// + /// Valore da trasmettere: es + /// INPUT: lo status rilevato in HEX + /// FLog: il valore da trasmettere per il flusso indicato + public void sendToMoonPro(urlType tipoUrl, string queueVal) + { + // init obj display + newDisplayData currDispData = new newDisplayData(); + try + { + // recupero e formatto URL dati da coda... + switch (tipoUrl) + { + case urlType.FLog: + lastUrl = urlFLog(queueVal); + break; + + case urlType.SignIN: + lastUrl = urlInput(queueVal); + break; + + default: + lastUrl = ""; + break; + } + // se NON sono in demo effettuo invio! + if (!DemoOut) + { + // SE server alive... + if (checkServerAlive) + { + // chiamo URL! + string answ = callUrl(lastUrl, false); + // loggo! + lgInfo(string.Format("[SEND] {0} -> {1}", queueVal, answ)); + // se oltre 1 min NON era online --> check pezzi! + if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + { + lgInfo($"sendToMoonPro --> offline timeaout ({lastIobOnline}) --> pzCntReload(true)"); + pzCntReload(true); + } + // se richiesto effettuo refresh contapezzi + if (needRefreshPzCount && !isMulti) + { + pzCntReload(true); + needRefreshPzCount = false; + } + lastIobOnline = DateTime.Now; + // se "OK" verde, altrimenti errore --> ROSSO + if (answ == "OK") + { + currDispData.semOut = Semaforo.SV; + } + else + { + currDispData.semOut = Semaforo.SR; + } + } + else + { + lgInfo(string.Format("[SERVER KO] {0}", queueVal)); + } + } + else + { + currDispData.semOut = Semaforo.SV; + // loggo! + lgInfo(string.Format("{0} -> [SIM]", queueVal)); + } + nSendOut++; + // riporto cosa inviato #if false displayOutData(); #endif - currDispData.newUrlCallData = lastUrl; - // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; - } - catch - { - currDispData.semOut = Semaforo.SR; - } - raiseRefresh(currDispData); - } + currDispData.newUrlCallData = lastUrl; + // aggiorno data ultimo watchdog... + lastWatchDog = DateTime.Now; + } + catch + { + currDispData.semOut = Semaforo.SR; + } + raiseRefresh(currDispData); + } - #endregion gestione code (accumulo, invio) + /// + /// Decodifica valore della coda IN nel formato + /// answ[0]=dtEve + /// answ[1]=valore + /// answ[2]=counter + /// + /// dtEve + '#' + value + '#' + cont + /// + protected static string[] qDecodeIN(string queueVal) + { + string[] answ = null; + if (!string.IsNullOrEmpty(queueVal)) + { + try + { + answ = queueVal.Split('#'); + } + catch + { } + } + return answ; + } - #region gestione dataMonitor (update visualizzazione valori) + #endregion gestione code (accumulo, invio) - /// - /// 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; + #region gestione dataMonitor (update visualizzazione valori) + + /// + /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA + /// + /// + public void accodaOtherData(string newLine) + { + // inserisco in cima allo stack, trimmo e aggiorno display + string strOtherData = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3)); + //parentForm.dataMonitor_3 = strOtherData; + parentForm.WriteTextSafe(strOtherData); + } + + /// + /// Update visualizzaizone BIT in ingresso + /// Parametri da aggiornare x display in form + /// + public void displayInData(ref newDisplayData currDispData) + { + if (currDispData != null) + { + // mostro update... + string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr(B_output, 8)); + currDispData.newSignalData = newString; + } + } + + /// + /// Mostra cosa ha/avrebbe inviato + /// + /// + public void displayOtherData(string newData) + { + // mostro update... + accodaOtherData(newData); + } + + /// + /// 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; + } + + /// + /// Mostra i dati grezzi letti in esadecimale + /// Parametri da aggiornare x display in form + /// + private void displayRawData(ref newDisplayData currDispData) + { + // mostro update... + string newString = string.Format("{0:X}", B_input); + currDispData.newInData = newString; #if false // salvo coda debug... QueueDebug.Enqueue(B_input); #endif + } + + #endregion gestione dataMonitor (update visualizzazione valori) } /// - /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA + /// Evento per incapsulare dati x refresh pagina /// - /// - public void accodaOtherData(string newLine) + public class iobRefreshedEventArgs : EventArgs { - // inserisco in cima allo stack, trimmo e aggiorno display - string strOtherData = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3)); - //parentForm.dataMonitor_3 = strOtherData; - parentForm.WriteTextSafe(strOtherData); + #region Private Fields + + /// + /// classe obj privata + /// + private readonly newDisplayData _newDisplayData; + + #endregion Private Fields + + #region Public Constructors + + /// + /// salvataggio obj + /// + /// + public iobRefreshedEventArgs(newDisplayData newObject) + { + _newDisplayData = newObject; + } + + #endregion Public Constructors + + #region Public Properties + + /// + /// Proprietà lettura displayData aggiornato + /// + public newDisplayData DisplayDataObject + { + get { return _newDisplayData; } + } + + #endregion Public Properties } - - /// - /// Update visualizzaizone BIT in ingresso - /// Parametri da aggiornare x display in form - /// - public void displayInData(ref newDisplayData currDispData) - { - if (currDispData != null) - { - // mostro update... - string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr(B_output, 8)); - currDispData.newSignalData = newString; - } - } - - /// - /// Mostra cosa ha/avrebbe inviato - /// - /// - public void displayOtherData(string newData) - { - // mostro update... - accodaOtherData(newData); - } - - /// - /// 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; - } - - #endregion gestione dataMonitor (update visualizzazione valori) - } - - /// - /// Evento per incapsulare dati x refresh pagina - /// - public class iobRefreshedEventArgs : EventArgs - { - #region Private Fields - - /// - /// classe obj privata - /// - private readonly newDisplayData _newDisplayData; - - #endregion Private Fields - - #region Public Constructors - - /// - /// salvataggio obj - /// - /// - public iobRefreshedEventArgs(newDisplayData newObject) - { - _newDisplayData = newObject; - } - - #endregion Public Constructors - - #region Public Properties - - /// - /// Proprietà lettura displayData aggiornato - /// - public newDisplayData DisplayDataObject - { - get { return _newDisplayData; } - } - - #endregion Public Properties - } } \ No newline at end of file diff --git a/IOB-WIN/IobSimula.cs b/IOB-WIN/IobSimula.cs index 5e3f0fbc..e589e7bd 100644 --- a/IOB-WIN/IobSimula.cs +++ b/IOB-WIN/IobSimula.cs @@ -338,8 +338,6 @@ namespace IOB_WIN // di base macchina in RUN B_input = 3; - // di base NON emergenza - B_input += (1 << 7); /*---------------------------------------- * Simulazione segnali con priorità: @@ -429,6 +427,9 @@ namespace IOB_WIN // questa parte la processo SOLO SE sono in run --> B_input == 3 if (B_input == 3) { + // di base NON emergenza + B_input += (1 << 7); + // ora controllo il resto... if (bit3.wait <= 0) { // segnalo BIT diff --git a/Jenkinsfile b/Jenkinsfile index 1b8f592d..ffe10d30 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { steps { /* calcolo numero versione... diverso x branch MASTER/DEVELOP */ script { - withEnv(['NEXT_BUILD_NUMBER=703']) { + withEnv(['NEXT_BUILD_NUMBER=704']) { // env.versionNumber = VersionNumber(versionNumberString : '3.2.${BUILD_DATE_FORMATTED, "yyMM"}.${BUILDS_ALL_TIME}', projectStartDate : '2006-01-01', skipFailedBuilds: true) env.versionNumber = VersionNumber(versionNumberString : '3.2.${BUILD_DATE_FORMATTED, "yyMM"}.${BUILDS_ALL_TIME}', projectStartDate : '2006-01-01', skipFailedBuilds: true, overrideBuildsAllTime: '${NEXT_BUILD_NUMBER}') env.APP_NAME = 'MAPO-IOB-WIN'