using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MTConnect.Clients; using MTConnect.Devices; using MTConnect.Observations; using MTConnect.Streams; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Threading; using System.Windows.Forms; namespace IOB_WIN_MTC.Iob { public class MTConn : Iob.GenericNext { #region Public Constructors /// /// Estende l'init della classe base, impiegando il pacchetto Nuget TrackHound https://github.com/TrakHound/MTConnect.NET /// /// Form chiamante /// Configurazione (v 4.x) public MTConn(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { // gestione parametri da nuova IobConfFull enableDataFilter = IobConfFull.FluxLog.EnableFilt; MaxSecReload = IobConfFull.FluxLog.MaxSecReload; readErrorMax = IobConfFull.Device.ReadErrorMax; readErrorSleepTime = IobConfFull.Device.ReadErrorSleepTime; enableCliRestart = IobConfFull.Device.Connect.EnableRestart; // gestione data unavailable = poweroff... if (!string.IsNullOrEmpty(getOptPar("UNAVAIL_POWEROFF"))) { bool.TryParse(getOptPar("UNAVAIL_POWEROFF"), out unavailPoweroff); } // gestione restart MTC client... if (!string.IsNullOrEmpty(getOptPar("ENABLE_MTC_RESTART"))) { bool.TryParse(getOptPar("ENABLE_MTC_RESTART"), out enableMtcRestart); } // gestione parametri x sendDataItem if (!string.IsNullOrEmpty(getOptPar("ENABLE_SEND_DATAITEM"))) { bool.TryParse(getOptPar("ENABLE_SEND_DATAITEM"), out enableSendDataItem); } if (!string.IsNullOrEmpty(getOptPar("MIN_VETO_SEND_DATAITEM"))) { int.TryParse(getOptPar("MIN_VETO_SEND_DATAITEM"), out minVetoSendDataItem); } // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; DtHelp.lastWarnODL = adesso; lastCurrent = adesso; // ora leggo il file di conf specifico.... string jsonFileName = getOptPar("MTC_PARAM_CONF"); if (!string.IsNullOrEmpty(jsonFileName)) { // leggo il file... loadMtcConf(jsonFileName); } // verifico SE ho dei bit "extra" configurati allora NON uso la state machine 54 ma 60... if (mtcParams != null && mtcParams.bitSpecCond.Count > 0) { bitEmg = 7; } } #endregion Public Constructors #region Public Methods /// /// Processo i task richiesti document li elimino dalla coda 1:1 /// /// public override Dictionary executeTasks(Dictionary task2exe, string codTav) { // uso metodo base x ora return base.executeTasks(task2exe, codTav); } /// /// Recupera uno specifico dataItem /// /// /// public string getDataItemValue(string diKey) { string answ = ""; if (queueInEnabCurr) { try { if (dataItemMem.ContainsKey(diKey)) { var currDataItem = dataItemMem[diKey]; answ = currDataItem.value; } } catch (Exception exc) { Logging.Instance.Error($"Errore in getDataItemValue per {diKey}{Environment.NewLine}{exc}"); } } else { lgDebug($"[VETO getDataItemValue] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}"); } return answ; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { Dictionary outVal = new Dictionary(); return outVal; } /// /// Effettua vero processing contapezzi /// public override void processContapezzi() { if (enablePzCountByApp) { if (queueInEnabCurr) { // cerco parametro contapezzi... string currPzCount = getDataItemValue(mtcParams.keyPartCount); if (isVerboseLog) { lgInfo($"contapezzi: {mtcParams.keyPartCount} --> {currPzCount}"); } // se ho un contapezzi... processo... if (!string.IsNullOrEmpty(currPzCount)) { int newVal = -1; Int32.TryParse(currPzCount, out newVal); contapezziPLC = newVal > -1 ? newVal : contapezziPLC; } } else { lgDebug($"[VETO processContapezzi] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}"); } } else { lgTrace($"processContapezzi escluso: enablePzCountByApp = {enablePzCountByApp} | disablePzCountByIob: {disablePzCountByIob}"); } } /// /// Effettua lettura semafori principale di default da FamIngressi 54 ma opzionalmente si può cambiare (es 60) /// Parametri da /// aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { DateTime adesso = DateTime.Now; DtHelp.lastReadPLC = adesso; // verifico non sia in veto invio iniziale... if (queueInEnabCurr) { try { if (verboseLog) { lgInfo("inizio read semafori"); } currDispData.semIn = Semaforo.SV; // decodifica document gestione decodeToBaseBitmap(); reportRawInput(ref currDispData); // controllo read error ed eventuale disconnect if (readErrorCurr > readErrorMax) { lgError($"Effettuo disconnessione x superamento errori lettura UA_ref.ReadNodeString, sleetp 15 sec"); tryDisconnect(); Thread.Sleep(readErrorSleepTime); readErrorCurr = 0; } else if (readErrorCurr > 0) { readErrorCurr--; } } catch (Exception exc) { currDispData.semIn = Semaforo.SR; lgError($"Eccezione in readSemafori:{Environment.NewLine}{exc}"); } } else { lgDebug($"[VETO readSemafori] | veto attivo alle {adesso:yyyy.MM.dd HH:mm:ss}"); checkVetoQueueIn(); } } /// /// Effettua reset del contapezzi, NON POSSIBILE per MTC (read only) /// /// public override bool resetContapezziPLC(string codTav) { bool answ = true; return answ; } /// /// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE per MTC (read only) /// /// public override bool setcontapezziPLC(int newPzCount, string codTav) { bool answ = true; return answ; } /// /// Override connessione /// public override void tryConnect() { if (!connectionOk) { // disattivo eventuali sottoscrizioni DeactEvents(); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("MTC: ConnKO - tryConnect"); } // in primis salvo data ping... DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { string szStatusConnection = ""; try { // ora provo connessione... parentForm.commPlcActive = true; doConnect(); parentForm.commPlcActive = false; // refresh stato allarmi!!! if (connectionOk) { checkVetoQueueIn(); if (adpRunning) { lgInfo("Connessione OK"); } } else { lgError("Impossibile procedere, connessione mancante..."); } } catch (Exception exc) { lgFatal($"Errore nella connessione all'adapter MTC: {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; lgInfo($"Eccezione in TryConnect, Adapter MTC NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione"); } } else { // loggo no risposta ping ... connectionOk = false; if (verboseLog || periodicLog) { lgInfo($"Attenzione: MTC controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr}"); } } } } else { needRefresh = true; } // se non è ancora connesso faccio procesisng memoria caso disconnesso... if (!connectionOk) { // processo semafori ed invio... processMemoryDiscon(); } } /// /// Override disconnessione /// public override void tryDisconnect() { try { queueInEnabCurr = false; } catch (Exception exc) { lg.Error($"eccezione in tryDisconnect | queueInEnabCurr = false{Environment.NewLine}{exc}"); } // verifico che NON sia in fase di avvio MTC... if (isConnecting && DateTime.Now.Subtract(startConnecting).TotalSeconds < 60) { lgInfo("Disconnessione non effettuata: connessione ancora in corso"); } else { lgTrace("Richiesta tryDisconnect"); if (connectionOk) { string szStatusConnection = ""; try { // disattivo sottoscrizioni DeactEvents(); // stop componente MTC_ref.Stop(); connectionOk = false; isConnecting = false; lgInfo($"conn status: {szStatusConnection}"); lgInfo("Effettuata disconnessione adapter MTC!"); } catch (Exception exc) { lgFatal(exc, "Errore nella disconnessione dall'adapter MTC"); } } else { lgError("IMPOSSIBILE effettuare disconnessione MTC: Connessione non disponibile..."); } } queueInEnabCurr = false; needRefresh = true; } #endregion Public Methods #region Protected Fields /// /// Gestione filtraggio dati /// protected bool enableDataFilter = false; /// /// Abilitazione restart (da opt par...) /// protected bool enableMtcRestart = false; /// /// Determina se ha effettuata lettura items in memoria x confronto... /// protected bool hasReadItems = false; /// /// Ultimo current received x gestione update periodico... /// protected DateTime lastCurrent = DateTime.Now; /// /// Oggetto MAIN x connessione MTC /// protected MTConnectHttpClient MTC_ref; /// /// Gestione valori unavailable come POWEROFF (es trevisan) /// protected bool unavailPoweroff = false; /// /// Veto controllos tatus x log... /// protected DateTime vetoCheckStatus = DateTime.Now; #endregion Protected Fields #region Protected Properties /// /// Verifico se abbia ALMENO un errore... /// protected bool hasError { get { bool answ = false; // controllo TUTTE le conditions... foreach (var item in dataItemMem) { // se NON HO GIA' allarmi attivi... if (!answ) { // se è una condition... if (item.Value.Category == MTConnect.Devices.DataItemCategory.CONDITION) { // se ha valore !="" --> allarmi attivi if (!string.IsNullOrEmpty(item.Value.value)) { answ = true; } } } } return answ; } } /// /// Indica se abbia emergenza premuta /// protected bool hasEStopTriggered { get { bool answ = false; if (queueInEnabCurr) { try { if (!string.IsNullOrEmpty(mtcParams.keyEStop)) { string currEStop = getDataItemValue(mtcParams.keyEStop); answ = (currEStop == "TRIGGERED"); } } catch (Exception exc) { Logging.Instance.Error($"Errore in hasEStopTriggered{Environment.NewLine}{exc}"); } } else { lgDebug($"[VETO hasEStopTriggered] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}"); } return answ; } } /// /// Verifico se abbia unavailable x le condizioni principali condPowerOn keyRunMode /// protected bool hasUnavailableData { get { bool answ = false; if (queueInEnabCurr) { // verifico le condizioni: powerOn, run document work... string currRun = getDataItemValue(mtcParams.keyRunMode); bool unavRun = currRun.ToUpper() == "UNAVAILABLE"; string currPowerOn = getDataItemValue(mtcParams.condPowerOn.keyName); bool checkPowerOn = (currPowerOn == mtcParams.condPowerOn.targetValue); answ = unavRun && !checkPowerOn; } return answ; } } protected bool isConnecting { get; set; } = false; /// /// Parametri specifici MTC /// protected MtcParamConf mtcParams { get; set; } protected DateTime startConnecting { get; set; } = DateTime.Today; #endregion Protected Properties #region Protected Methods /// /// Verifica un DataItem document se il valore corrisponde a quello indicato come "true eValue" /// restituisce true /// /// /// /// protected bool checkDataItem(string itemName, string trueVal) { bool answ = false; MtcDataItemExt currValue = null; try { currValue = dataItemMem[itemName]; answ = (currValue.value.Equals(trueVal)); } catch { lgError($"Errore in decodifica valore per {itemName} rispetto a {trueVal} | recuperato {currValue} / {currValue.value}"); } return answ; } /// /// Verifica condizione "multipla" secondo setup json /// /// Set condizioni da validare /// protected bool checkMultiCondition(diCheckCondSetup reqCondition) { bool answ = false; int numCondTgt = 0; int numCond = 0; if (reqCondition.checkList != null && reqCondition.checkList.Count > 0) { numCond = reqCondition.checkList.Count; // cerco nell'elenco delle condizioni che indicano lavora se sono ok faccio +1 conteggio...... foreach (var item in reqCondition.checkList) { if (string.IsNullOrEmpty(item.keyName)) { lgError($"Attenzione: item vuoto in checkMultiCondition{Environment.NewLine}StackTrace: {Environment.StackTrace}"); } else { // versione semplificata MTC if (getDataItemValue(item.keyName) == item.targetValue) { numCondTgt++; } // versione completa OPC-UA #if false // verifico se ci sia richiesta x test bit... if (item.bitNum >= 0) { if (getDataItemValueBit(item.keyName, item.bitNum) == item.targetValue) { numCondTgt++; } } else { if (getDataItemValue(item.keyName) == item.targetValue) { numCondTgt++; } } #endif } } if (reqCondition.checkMode == boolCheckMode.AND) { answ = (numCond == numCondTgt); } else if (reqCondition.checkMode == boolCheckMode.OR) { answ = numCondTgt > 0; } } else { answ = true; } // verifico se devo negare il valore... answ = reqCondition.negateValue ? !answ : answ; // restituisco return answ; } /// /// Verifica / Salva valore condition /// /// /// Messaggio completo (livello + testo) /// protected bool checkSaveCondition(IObservation observ, string cValue) { bool answ = !enableDataFilter; if (observ != null) { if (isVerboseLog) { lgInfo($"Richiesta checkSaveCondition per {observ} | id: {observ.DataItemId} | message: {cValue}"); } // verifico in memoria se ho l'oggetto condition ed il suo valore.. if (dataItemMem.ContainsKey(observ.DataItemId)) { // check variazione if (dataItemMem[observ.DataItemId].value != cValue) { dataItemMem[observ.DataItemId].value = cValue; answ = true; } dataItemMem[observ.DataItemId].valueTimestamp = observ.Timestamp; } else { // registro non trovato da aggiungere... lgInfo($"DataItem non trovato in checkSaveCondition: {observ.DataItemId}"); try { // provo a creare oggetto in memoria... List elencoDataItems = new List(); int dSamplePeriod = 0; float threshDBand = 0; string uuid = ""; var currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, observ, cValue); // aggiungo dataItemMem.Add(observ.DataItemId, currDataItem); var thisCat = (MapoSDK.DataItemCategory)Enum.Parse(typeof(MapoSDK.DataItemCategory), $"{observ.Category}"); // salvo oggetto x registrazione su server MP-IO var currMapoDataItem = new machDataItem() { uuid = observ.DataItemId, Category = thisCat, Name = observ.Name, Type = observ.Type, SubType = observ.SubType, //Units = observ.Units }; // aggiungo elencoDataItems.Add(currMapoDataItem); // invio il dataItem serializzato... sendDataItemsList(elencoDataItems); } catch (Exception exc) { lgError($"Eccezione in checkSaveCondition{Environment.NewLine}{exc}"); } } } else { lgError("Attenzione: checkSaveCondition con observ null!"); } return answ; } /// /// Verifica / Salva valore EVENT generico (NON SAMPLE) /// /// /// /// protected bool checkSaveEvent(IObservation observ, string iValue) { bool answ = !enableDataFilter; if (observ != null) { if (isVerboseLog) { lgInfo($"Richiesta checkSaveEvent per {observ} | id: {observ.DataItemId} | eValue: {iValue}"); } // verifico in memoria se ho l'oggetto EVENT ed il suo valore.. if (dataItemMem.ContainsKey(observ.DataItemId)) { // verifico SE sia cambiato... if (dataItemMem[observ.DataItemId].value != iValue) { // verifico SE avessi una soglia deadBand... if (dataItemMem[observ.DataItemId].thresholdDeadBand > 0) { } else { dataItemMem[observ.DataItemId].value = iValue; answ = true; } } dataItemMem[observ.DataItemId].valueTimestamp = observ.Timestamp; } else { // registro non trovato da aggiungere... lgInfo($"DataItem non trovato in checkSaveEvent: {observ.DataItemId}"); try { // provo a creare oggetot in memoria... List elencoDataItems = new List(); int dSamplePeriod = 0; float threshDBand = 0; string uuid = ""; var currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, observ, iValue); // aggiungo dataItemMem.Add(observ.DataItemId, currDataItem); var thisCat = (MapoSDK.DataItemCategory)Enum.Parse(typeof(MapoSDK.DataItemCategory), $"{observ.Category}"); // salvo oggetto x registrazione su server MP-IO var currMapoDataItem = new machDataItem() { uuid = observ.DataItemId, Category = thisCat, Name = observ.Name, Type = observ.Type, SubType = observ.SubType, //Units = observ.Units }; // aggiungo elencoDataItems.Add(currMapoDataItem); // invio il dataItem serializzato... sendDataItemsList(elencoDataItems); } catch (Exception exc) { lgError($"Eccezione in checkSaveEvent{Environment.NewLine}{exc}"); } } } else { lgError("Attenzione: checkSaveEvent con observ null!"); } return answ; } /// /// Verifica / Salva valore SAMPLE document restitusice SE sia variato (document quindi da inviare...) /// /// /// protected bool checkSaveSample(IObservation observ, string sValue) { bool answ = !enableDataFilter; double oldVal = 0; double newVal = 0; if (observ != null) { // verifico in memoria se ho l'oggetto condition ed il suo valore.. if (dataItemMem.ContainsKey(observ.DataItemId)) { MtcDataItemExt currDataItemMem = dataItemMem[observ.DataItemId]; // controllo SE SIA scaduto il tempo massimo... if (Math.Abs(dataItemMem[observ.DataItemId].valueTimestamp.Subtract(observ.Timestamp).TotalSeconds) > currDataItemMem.samplePeriod) { answ = true; } else { // ALTRIMENTI controllo SE diverso if (dataItemMem[observ.DataItemId].value != sValue) { // controllo SE ho DeadBand... if (dataItemMem[observ.DataItemId].thresholdDeadBand > 0) { if (isVerboseLog) { lgInfo($"Test deadband: oldVal: {oldVal} | newVal: {newVal}"); } // recupero i valori document testo DeadBand... double.TryParse(dataItemMem[observ.DataItemId].value.Replace(".", ","), out oldVal); double.TryParse(sValue.Replace(".", ","), out newVal); // test deadband! if (Math.Abs(newVal - oldVal) > dataItemMem[observ.DataItemId].thresholdDeadBand) { // indico da salvare.. answ = true; } } } } if (answ) { // salvo! dataItemMem[observ.DataItemId].value = sValue; dataItemMem[observ.DataItemId].valueTimestamp = observ.Timestamp; } } else { // registro non trovato da aggiungere... lgInfo($"DataItem non trovato in checkSaveSample: {observ.DataItemId}"); // provo a creare oggetto in memoria... try { List elencoDataItems = new List(); int dSamplePeriod = 0; float threshDBand = 0; string uuid = ""; var currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, observ, sValue); // aggiungo dataItemMem.Add(observ.DataItemId, currDataItem); var thisCat = (MapoSDK.DataItemCategory)Enum.Parse(typeof(MapoSDK.DataItemCategory), $"{observ.Category}"); // salvo oggetto x registrazione su server MP-IO var currMapoDataItem = new machDataItem() { uuid = observ.DataItemId, Category = thisCat, Name = observ.Name, Type = observ.Type, SubType = observ.SubType, //Units = observ.Units }; // aggiungo elencoDataItems.Add(currMapoDataItem); // invio il dataItem serializzato... sendDataItemsList(elencoDataItems); } catch (Exception exc) { lgError($"Eccezione in checkSaveSample{Environment.NewLine}{exc}"); } } } else { lgError("Attenzione: checkSaveEvent con observ null!"); } return answ; } /// /// Indica se siano valide condizioni di bit speciali /// protected bool hasBitCondition(int bitNum) { bool answ = false; if (queueInEnabCurr) { if (mtcParams.bitSpecCond.ContainsKey(bitNum)) { answ = checkMultiCondition(mtcParams.bitSpecCond[bitNum]); } } else { lgDebug($"[VETO hasWarmUpConditions] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}"); } return answ; } /// /// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non /// trovo uso key /// /// /// /// protected override string itemTranslation(string tipo, string id) { string answ = ""; string lemma = $"{tipo}_{id}"; // cerco nel dizionario delle traduzioni SE esiste un valore document prendo quello, altrimenti // uso il lemma... if (mtcParams.itemTranslation.ContainsKey(lemma)) { answ = mtcParams.itemTranslation[lemma]; } else { answ = lemma; } return answ; } /// /// Effettua log di un elenco componenti /// /// protected int logComponentsList(List elencoComponenti) { int found = 0; if (elencoComponenti != null) { foreach (var item in elencoComponenti) { lgTrace($"Component data | ID: {item.Id} | Name: {item.Name} | Type: {item.Type} | # items: {item.DataItems.Count()}"); found++; // se ho sottocomponenti richiamo... if (item.Components != null) { if (item.Components.Count() > 0) { found += logComponentsList(item.GetComponents().ToList()); } } if (item.DataItems.Count() > 0) { found += logDataItemList(item.GetDataItems().ToList()); } } } return found; } /// /// Effettua log di un elenco composizioni /// /// protected int logCompositionList(List elencoComposizioni) { int found = 0; if (elencoComposizioni != null) { foreach (var item in elencoComposizioni) { lgTrace($"Composition data | ID: {item.Id} | Name: {item.Name} | Type: {item.Type} | # items: {item.DataItems.Count()} | path: {item.IdPath}"); found++; } } return found; } /// /// Log elenco DataItems /// /// protected int logDataItemList(List elencoItems) { int found = 0; if (elencoItems != null) { // loggo devices principali... foreach (var item in elencoItems) { lgTrace($"Device data | ID: {item.Id} | Name: {item.Name} | Category: {item.Category} | # Type: {item.Type} | Path: {item.IdPath}"); found++; } } return found; } /// /// Effettua log di un devices (ed eventualmente dei sub-devices... /// /// protected void logProbeDevices(List listDevices) { if (listDevices != null) { int numCom = 0; int numDev = 0; int numItm = 0; // loggo devices principali... foreach (var device in listDevices) { lgTrace($"Device data | ID: {device.Id} | Name: {device.Name} | UUID: {device.Uuid} | # items: {device.DataItems.Count()}"); // se ho subItems descrivo pure loro... if (device.DataItems.Count() > 0) { numItm += logDataItemList(device.GetDataItems().ToList()); } if (device.Components.Count() > 0) { numCom += logComponentsList(device.GetComponents().ToList()); } // All Compositions (traverse the entire Device model) if (device.Compositions.Count() > 0) { numCom += logCompositionList(device.GetCompositions().ToList()); } } lgInfo(lineSep); lgInfo($"Effettuata lettura | Components: {numCom} | Devices: {numDev} | DataItems: {numItm}"); lgInfo(lineSep); } } #endregion Protected Methods #region Private Fields /// /// Valore del bit usato x emergenza, default 5 x StateMach=54 , 7 se è StateMac=60 /// private int bitEmg = 5; /// /// Struttura dove vengono memorizzati i dataitem ed i rispettivi valori x processing /// private Dictionary dataItemMem = new Dictionary(); #endregion Private Fields #region Private Methods /// /// Verifica ed invia variazioni /// /// /// private void checkAndSend(IStreamsResponseDocument document, bool forceSend) { if (document != null) { foreach (var deviceStream in document.Streams) { // DeviceStream lgTrace($"Stream: {deviceStream.Name}"); // Component Streams foreach (var dataStream in deviceStream.ComponentStreams) { lgTrace($"Component {dataStream.Name} -->"); // DataItems (Samples, Events, and Conditions) foreach (var observation in dataStream.Observations) { // cerco se non sia un dato filtrato in FLUXLOG... bool isFiltered = mtcParams.fluxLogVeto.Where(x => x == observation.DataItemId).Count() > 0; if (isFiltered) { traceObservation(observation); } else { processObservation(observation, forceSend); } } } } } else { lgError("StreamsSuccessful ERROR: document è null"); } } /// /// Disattivazione eventi sottoscritti /// private void DeactEvents() { if (MTC_ref != null) { // disconnetto metodi sottoscritti MTC_ref.ClientStarted -= MTC_ref_ClientStarted; MTC_ref.ClientStopped -= MTC_ref_ClientStopped; MTC_ref.ConnectionError -= MTC_ref_ConnectionError; MTC_ref.AssetsReceived -= MTC_ref_AssetsReceived; MTC_ref.ProbeReceived -= MTC_ref_ProbeReceived; MTC_ref.CurrentReceived -= MTC_ref_CurrentReceived; // sample subscription if (mtcParams.doSubsSample) { MTC_ref.SampleReceived -= MTC_ref_SampleReceived; //MTC_ref.SampleReceived += StreamsSuccessful; } // observ subscription if (mtcParams.doSubsObserv) { MTC_ref.ObservationReceived -= MTC_ref_ObservationReceived; } } } /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO (std 54, opzioanlmente 60) /// private void decodeToBaseBitmap() { /* ----------------------------------------------------- * Macchine a stati ammesse: tipicamente 54 (STD) opure 60 (SE ho configurazione 6° bit) * * ----------------------------------- * bitmap MAPO, STD 54 (6 bit) * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: emergenza (dipende da state machine se riportare 1 = armata/premuta, vedere conf mtcParams.emergencyArmedTrue) * ----------------------------------- * * ----------------------------------- * bitmap MAPO, OPZ 60 (8 bit) * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: emergenza (dipende da state machine se riportare 1 = armata/premuta, vedere conf mtcParams.emergencyArmedTrue) ----------------------------------------------------- */ // init a zero... B_input = 0; if (queueInEnabCurr) { DateTime adesso = DateTime.Now; // controllo se non ho dati buoni da > MaxSecReload sec --> disconnetto if (adesso.Subtract(lastCurrent).TotalSeconds > MaxSecReload) { if (enableCliRestart) { if (checkAdapterAlive()) { lgInfo($"Mancanza innovazioni da oltre {MaxSecReload} sec | Alive Test Success | NON riavviamo, impostato lastCurrent"); lastCurrent = adesso; } else { lgInfo($"Timeout per mancata comunicazione da oltre {MaxSecReload} sec | Alive Test Failed | --> disconnessione adapter MTConnect!"); tryDisconnect(); } } else { lgError($"Attenzione: superato periodo di mancata comunicazione da oltre {MaxSecReload} sec ma enableCliRestart non permesso"); } } // Controllo booleano PING document POWERON... string currPowerOn = getDataItemValue(mtcParams.condPowerOn.keyName); // se valido il check ping lo eseguo... altrimenti lo do x buono bool isPingOk = mtcParams.pingAsPowerOn && (testPingMachine == IPStatus.Success); // verifico da target eValue richiesto... bool checkPowerOn = (currPowerOn == mtcParams.condPowerOn.targetValue); // bit 0 (poweron) imposto a 1 SE pingo o PowerOn=="ON"... B_input = (isPingOk || checkPowerOn) ? 1 : 0; // variabili RUN... string currRun = getDataItemValue(mtcParams.keyRunMode); // controllo RUN MODE preliminare... CABLATO - è GENERALE x MTC if (currRun == "AUTOMATIC" || currRun == "SEMI_AUTO" || currRun == "SEMI_AUTOMATIC") { int numCond = mtcParams.condWork.Count; int numCondOk = 0; // cerco nell'elenco delle condizioni che indicano lavora se sono ok faccio +1 conteggio...... foreach (var item in mtcParams.condWork) { if (getDataItemValue(item.keyName) == item.targetValue) { numCondOk++; } } // se tutte condizioni rispettate --> lavora! if (numCond == numCondOk) { // RUN = LAVORA! B_input += (1 << 1); } } // se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM! else if (hasError) { B_input += (1 << 3); } // 2024.01.90: gestione dati UNAVAILABLE che indicano poweroff... else if (hasUnavailableData && unavailPoweroff) { B_input = 0; } else { // se ho run mode != auto --> manual B_input += (1 << 4); } // gestione bit 5-6 se StateMach == 60... if (bitEmg > 5) { // verifico condizioni per 6° bit = WarmUp/CoolDown... if (hasBitCondition(6)) { // resetto a 1 il segnale di base (solo poweron)... B_input = 1; // ed aggiungo warmup B_input += (1 << 6); } // possibile miglioramento: gestione 5° bit slowTC come simula... } // emergenza armata da riportare con bit True/ 1 if (mtcParams.emergencyArmedTrue) { //se NON premuta lazo il bit if (!hasEStopTriggered) { B_input += (1 << bitEmg); } } // emergenza armata da riportare come False/0 (!mtcParams.emergencyArmedTrue) else { // se premuta alzo il bit... if (hasEStopTriggered) { B_input += (1 << bitEmg); } } int vFactor = 1; // controllo SE HO dati per fare verifiche... if (string.IsNullOrEmpty(currRun)) { // se ho parametro x gestione reset... if (enableMtcRestart) { // controllo se ho ricevuto il current da OLTRE 1 minuto... if (lastCurrent.AddMinutes(3) < adesso) { lastCurrent = adesso; // stop... lgInfo("Fermato MTC_ref per mancanza dati current"); MTC_ref.Stop(); Thread.Sleep(3000); // restart lgInfo("Riavviato MTC_ref per mancanza dati current"); MTC_ref.Start(); } } } else { vFactor = 6; } // solo se non ho veto check if (vetoCheckStatus < adesso) { lgInfo($"Stato variabili: currRun: {currRun}"); // imposto veto per vetoSeconds... vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor); } // log opzionale! if (verboseLog) { lgInfo($"Trasformazione B_input: {B_input} | currRun = {currRun}"); } } else { lgDebug($"[VETO getDataItemValue] | veto attivo alle {DateTime.Now:yyyy.MM.dd HH:mm:ss}"); } } /// /// Verifica stato Alive per adapter: /// - test ping /// - test probe /// /// private bool checkAdapterAlive() { bool isPingOk = testPingMachine == IPStatus.Success; // test probe! bool probeOk = TestProbe(mtcUrlCall, true); return isPingOk && probeOk; } /// /// URL completo x chiamate MTC /// private string mtcUrlCall { get { return $"{IOBConfFull.Device.Connect.IpAddr}:{mtcPort}"; } } private short mtcPort { get { short port = 5000; short.TryParse(IOBConfFull.Device.Connect.Port, out port); return port; } } /// /// Vera connessione ad MTC /// /// private void doConnect() { isConnecting = true; startConnecting = DateTime.Now; // reset memoria dataItem.. dataItemMem = new Dictionary(); // predisposizione conf oggetto di comunicazione MTC string callUrl = mtcUrlCall; // test probe! try { // anche se non del tutto vero... connectionOk = TestProbe(callUrl, true); } catch (Exception exc) { lgError($"Eccezione durante test probe:{Environment.NewLine}{exc}"); } // attendo 1 sec... Thread.Sleep(1000); // ora avvio try { lgInfo($"Chiamata apertura MTC Client: {callUrl}"); MTC_ref = new MTConnectHttpClient(IOBConfFull.Device.Connect.IpAddr, mtcPort); //MTC_ref = new MTConnectHttpClient(callUrl); // sample interval MTC_ref.Interval = mtcParams.clientSampleIntMs; // timeout x richieste MTC_ref.Timeout = mtcParams.reqTOutMs; // timeout riconnessione MTC_ref.ReconnectionInterval = mtcParams.reconnectIntMs; // Sottoscrizioni base MTC_ref.ClientStarted += MTC_ref_ClientStarted; MTC_ref.ClientStopped += MTC_ref_ClientStopped; MTC_ref.ConnectionError += MTC_ref_ConnectionError; // Sottoscrizioni dati ricevuti !!! MTC_ref.AssetsReceived += MTC_ref_AssetsReceived; MTC_ref.ProbeReceived += MTC_ref_ProbeReceived; MTC_ref.CurrentReceived += MTC_ref_CurrentReceived; // sample subscription if (mtcParams.doSubsSample) { MTC_ref.SampleReceived += MTC_ref_SampleReceived; } // observ subscription if (mtcParams.doSubsObserv) { MTC_ref.ObservationReceived += MTC_ref_ObservationReceived; } MTC_ref.Start(); // fix tempi! DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; DtHelp.lastWarnODL = adesso; lastCurrent = adesso; } catch { } isConnecting = false; } /// /// Test di chiamata metodo PROBE con log info /// /// private bool TestProbe(string callUrl, bool doLogDevices) { bool testOk = false; lgInfo($"Test Probe MTC Client: {callUrl}"); try { var prbClient = new MTConnectHttpProbeClient(callUrl); //var prbClient = new MTConnectHttpProbeClient(cIobConf.cncIpAddr, port); prbClient.Timeout = mtcParams.reqTOutMs; var prbDoc = prbClient.Get(); // se valido loggo! if (prbDoc != null) { testOk = true; lgInfo($"Effettuata correttamente PROBE per device MTC all'URL {callUrl}"); if (doLogDevices) { lgInfo($"---------------- Elenco Devices ----------------"); // loggo devices principali... logProbeDevices(prbDoc.Devices.ToList()); } } } catch (Exception exc) { lgError($"Eccezione durante TestProbe:{Environment.NewLine}{exc}"); } return testOk; } /// /// Formatta un dataitem da uno stream SAMPLE /// /// /// /// /// /// /// private MtcDataItemExt formatDataItem(ref int dSamplePeriod, ref float threshDBand, ref string uuid, IObservation observ, string fullMsg) { DataItemStatistic currStat = DataItemStatistic.AVERAGE; //cerco nei values... if (observ.Values.Count() > 0) { foreach (var item in observ.Values) { if (item.Key == "Statistic") { currStat = (DataItemStatistic)Enum.Parse(typeof(DataItemStatistic), item.Value); } } } // creo il nuovo dataitem da sample... DataItem newDataItem = new DataItem() { Category = observ.Category, Id = observ.DataItemId, Name = observ.Name, //SampleRate = , Statistic = currStat, SubType = observ.SubType, Type = observ.Type }; return formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, newDataItem, fullMsg); } /// /// Formatta un dataitem x salvataggio in memoria locale /// /// /// /// /// /// /// private MtcDataItemExt formatDataItem(ref int dSamplePeriod, ref float threshDBand, ref string uuid, DataItem dataItem, string value) { MtcDataItemExt currDataItem; // uuid document parametri secondo categoria... switch (dataItem.Category) { case MTConnect.Devices.DataItemCategory.CONDITION: uuid = $"C_{dataItem.Id}"; threshDBand = 0; dSamplePeriod = 0; break; case MTConnect.Devices.DataItemCategory.EVENT: uuid = $"E_{dataItem.Id}"; if (enableDataFilter) { threshDBand = 1; // controllo SE ho conf x deadband... if (mtcParams.paramsEndThresh.Count > 0) { // ciclo su tutti i parametri indicati... foreach (var item in mtcParams.paramsEndThresh) { if (dataItem.Id.EndsWith(item.Key)) { threshDBand = item.Value; } } } } else { threshDBand = 0; } dSamplePeriod = 0; break; case MTConnect.Devices.DataItemCategory.SAMPLE: uuid = $"S_{dataItem.Id}"; // SOLO SE è abilitato il datafiltering... if (enableDataFilter) { threshDBand = 1; // controllo SE ho conf x deadband... if (mtcParams.paramsEndThresh.Count > 0) { // ciclo su tutti i parametri indicati... foreach (var item in mtcParams.paramsEndThresh) { if (dataItem.Id.EndsWith(item.Key)) { threshDBand = item.Value; } } } } else { threshDBand = 0; } dSamplePeriod = 60; break; default: break; } // salvo oggetto x "uso interno" currDataItem = new MtcDataItemExt() { Id = dataItem.Id, Category = dataItem.Category, Constraints = dataItem.Constraints, CoordinateSystem = dataItem.CoordinateSystem, Name = dataItem.Name, NativeScale = dataItem.NativeScale, NativeUnits = dataItem.NativeUnits, SampleRate = dataItem.SampleRate, Representation = dataItem.Representation, SignificantDigits = dataItem.SignificantDigits, Source = dataItem.Source, Statistic = dataItem.Statistic, SubType = dataItem.SubType, Type = dataItem.Type, //TypePath = dataItem.TypePath, Units = dataItem.Units, //XPath = dataItem.XPath, uid = uuid, thresholdDeadBand = threshDBand, samplePeriod = dSamplePeriod, value = value }; // log x capire COME ho chiamato alcune cosette... if (dataItem.Id.Contains("EXE_MODE") || dataItem.Id.Contains("RUN_MODE") || dataItem.Id.Contains("POWER") || dataItem.Id.EndsWith("_Status")) { lgInfo($"DEBUG DATA | dataItem.Id : {dataItem.Id}"); } return currDataItem; } /// /// Formatta un dataitem x salvataggio in memoria locale /// /// /// /// /// /// /// private MtcDataItemExt formatDataItem(ref int dSamplePeriod, ref float threshDBand, ref string uuid, IDataItem dataItem, string value) { MtcDataItemExt currDataItem; // uuid document parametri secondo categoria... switch (dataItem.Category) { case MTConnect.Devices.DataItemCategory.CONDITION: uuid = $"C_{dataItem.Id}"; threshDBand = 0; dSamplePeriod = 0; break; case MTConnect.Devices.DataItemCategory.EVENT: uuid = $"E_{dataItem.Id}"; threshDBand = 0; dSamplePeriod = 0; break; case MTConnect.Devices.DataItemCategory.SAMPLE: uuid = $"S_{dataItem.Id}"; // SOLO SE è abilitato il datafiltering... if (enableDataFilter) { threshDBand = 1; // controllo SE ho conf x deadband... if (mtcParams.paramsEndThresh.Count > 0) { // ciclo su tutti i parametri indicati... foreach (var item in mtcParams.paramsEndThresh) { if (dataItem.Id.EndsWith(item.Key)) { threshDBand = item.Value; } } } } else { threshDBand = 0; } dSamplePeriod = 60; break; default: break; } // salvo oggetto x "uso interno" currDataItem = new MtcDataItemExt() { Id = dataItem.Id, Category = dataItem.Category, Constraints = dataItem.Constraints, CoordinateSystem = dataItem.CoordinateSystem, Name = dataItem.Name, NativeScale = dataItem.NativeScale, NativeUnits = dataItem.NativeUnits, SampleRate = dataItem.SampleRate, Representation = dataItem.Representation, SignificantDigits = dataItem.SignificantDigits, Source = dataItem.Source, Statistic = dataItem.Statistic, SubType = dataItem.SubType, Type = dataItem.Type, //TypePath = dataItem.TypePath, Units = dataItem.Units, //XPath = dataItem.XPath, uid = uuid, thresholdDeadBand = threshDBand, samplePeriod = dSamplePeriod, value = value }; // log x capire COME ho chiamato alcune cosette... if (dataItem.Id.Contains("EXE_MODE") || dataItem.Id.Contains("RUN_MODE") || dataItem.Id.Contains("POWER") || dataItem.Id.EndsWith("_Status")) { lgInfo($"DEBUG DATA | dataItem.Id : {dataItem.Id}"); } return currDataItem; } /// /// Effettua lettura file di conf specifico MTC da oggetto serializzato json Nome file da cui leggere i parametri json /// private void loadMtcConf(string fileName) { string jsonFullPath = $"{Application.StartupPath}/DATA/CONF/{fileName}"; lgInfo($"Apertura file {jsonFullPath}"); StreamReader reader = new StreamReader(jsonFullPath); string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", ""); if (!string.IsNullOrEmpty(jsonData)) { lgInfo($"File json composto da {jsonData.Length} caratteri"); try { mtcParams = JsonConvert.DeserializeObject(jsonData); lgInfo($"Decodifica aree MtcParamConf | condPowerOn: {mtcParams.condPowerOn.keyName}={mtcParams.condPowerOn.targetValue} | condWork: {mtcParams.condWork.Count} | keyPartCount: {mtcParams.keyPartCount} | itemTranslation: {mtcParams.itemTranslation.Count} | fluxLogVeto: {mtcParams.fluxLogVeto.Count} | paramsEndThresh: {mtcParams.paramsEndThresh.Count} "); } catch (Exception exc) { lgError($"Eccezione in decodifica conf json MTC:{Environment.NewLine}{exc}"); } } else { lgError("Errore in loadMtcConf: file json vuoto!"); } reader.Dispose(); } private void MTC_ref_AssetsReceived(object sender, MTConnect.Assets.IAssetsResponseDocument doc) { lgInfo("ASSET received"); queueInEnabCurr = true; connectionOk = true; lastCurrent = DateTime.Now; int docSize = 0; string rawVal = ""; foreach (var asset in doc.Assets) { // Print AssetId to the Console lgInfo($"Asset: {asset.AssetId}"); rawVal = JsonConvert.SerializeObject(asset); docSize += rawVal.Length; } // salvo dim caratteri ricevuti trackExchData(docSize, 4096); } private void MTC_ref_ClientStarted(object sender, EventArgs e) { lgInfo($"Client Started!"); lastCurrent = DateTime.Now; } private void MTC_ref_ClientStopped(object sender, EventArgs e) { lgInfo($"Client Stopped! | {e}"); queueInEnabCurr = false; connectionOk = false; } private void MTC_ref_ConnectionError(object sender, Exception exc) { lgError($"CONNECTION ERROR returned!{Environment.NewLine}{exc}"); queueInEnabCurr = false; connectionOk = false; } /// /// Effettuata discovery iniziale valori CURRENT /// /// /// private void MTC_ref_CurrentReceived(object sender, IStreamsResponseDocument document) { lgInfo("CURRENT received"); queueInEnabCurr = true; connectionOk = true; // se lastcurrent > MinRefreshPeriodSec sec --> forzo send... bool forceSend = DateTime.Now.Subtract(lastCurrent).TotalSeconds > IOBConfFull.Device.MinRefreshPeriodSec; lastCurrent = DateTime.Now; if (document != null) { if (document.Streams != null) { lgInfo($"CurrentSuccessful: trovati {document.Streams.Count()} streams"); } checkAndSend(document, forceSend); } else { lgError("MTC_ref_CurrentReceived ERROR: document è null"); } } /// /// Processing singola observation ricevuta /// /// /// private void MTC_ref_ObservationReceived(object sender, IObservation observ) { connectionOk = true; lastCurrent = DateTime.Now; processObservation(observ, false); } /// /// Effettuata discovery iniziale valori PROBE /// /// /// private void MTC_ref_ProbeReceived(object sender, IDevicesResponseDocument document) { queueInEnabCurr = true; connectionOk = true; lastCurrent = DateTime.Now; lgInfo("STEP 01 DevicesSuccessful reached!"); MtcDataItemExt currDataItem = null; machDataItem currMapoDataItem = null; List elencoDataItems = new List(); int dSamplePeriod = 0; float threshDBand = 0; string uuid = ""; if (document != null) { foreach (var device in document.Devices) { List dataItems = device.GetDataItems().ToList(); //List dataItems = device.GetDataItems(); lgInfo($"Inizio STEP 02 per caricare {dataItems.Count} dataItems"); foreach (var dataItem in dataItems) { try { currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, dataItem, ""); // aggiungo se non c'è... if (!dataItemMem.ContainsKey(dataItem.Id)) { dataItemMem.Add(dataItem.Id, currDataItem); } var thisCat = (MapoSDK.DataItemCategory)Enum.Parse(typeof(MapoSDK.DataItemCategory), $"{dataItem.Category}"); // salvo oggetto x registrazione su server MP-IO currMapoDataItem = new machDataItem() { uuid = dataItem.Id, Category = thisCat, Name = dataItem.Name, Type = dataItem.Type, SubType = dataItem.SubType, Units = dataItem.Units }; // aggiungo se non ci fosse if (!elencoDataItems.Contains(currMapoDataItem)) { elencoDataItems.Add(currMapoDataItem); } } catch (Exception exc) { lgError($"Eccezione in DevicesSuccessful / DataItem:{Environment.NewLine}{exc}"); } } } // invio IN BLOCCO il dataItem serializzati... if (elencoDataItems.Count > 0) { lgInfo($"STEP 03 invio dati di {elencoDataItems.Count} records"); sendDataItemsList(elencoDataItems); trackExchData(1024, 4096); } hasReadItems = true; lgInfo($"STEP 04: memorizzati {dataItemMem.Count} oggetti in memoria"); } else { lgError("STEP 05 error: document null!"); } } /// /// Ricevuto SAMPLE stream da campionamenti ricevuti /// /// private void MTC_ref_SampleReceived(object sender, IStreamsResponseDocument document) { connectionOk = true; lastCurrent = DateTime.Now; lgTrace("SAMPLE received"); checkAndSend(document, false); trackExchData(64, 4096); } /// /// Processing di una singola observ in un datastream /// /// Osservazione da processare /// invio forzato private void processObservation(IObservation observ, bool forceSend) { string sVal = ""; string descr = ""; DateTime locTStamp = DateTime.Now; // log msg string sMsg = $" - {observ.DataItemId} | {observ.Category}"; switch (observ.Category) { case MTConnect.Devices.DataItemCategory.CONDITION: string message = "OK"; string level = ""; string fullMsg = ""; bool doVeto = false; try { descr = itemTranslation("C", observ.DataItemId); locTStamp = observ.Timestamp.ToLocalTime(); sVal = $"CONDITION: {locTStamp.ToString()} | descr: {descr} | Id: {observ.DataItemId} | Name: {observ.Name}"; if (observ.Values.Count() > 0) { // se ho veto sui livelli di allarme li gestisco (e salto nel caso) if (ListVetoCond.Count != 0) { // recupero level... string cLevel = observ.Values.FirstOrDefault(x => x.Key.Equals("level", StringComparison.InvariantCultureIgnoreCase)).Value; // verificos e è vietato il log ricevuto... doVeto = ListVetoCond.Contains(cLevel); } foreach (var item in observ.Values) { sVal += $" | {item.Key}: {item.Value}"; if (item.Key == "Level") { level = item.Value; } else if (item.Key == "Message") { message = item.Value; } } fullMsg = $"{level} | {message}"; } // condition su debug lgDebug(sVal); DateTime tStamp = observ.Timestamp; var time2 = tStamp.ToLocalTime(); // verifico veto... if (!doVeto) { // verifico se sia variato valore e dataora da ultimo salvataggio redis... string redKey = $"{redKeyLastCondition}:{observ.DataItemId}"; string redisLastCond = redisMan.getRSV(redKey); string currCond = $"{observ.Timestamp}|{fullMsg}"; if (currCond.Equals(redisLastCond)) { lgInfo($"Verifca condition | nessuna variazione da ultima registata | {redisLastCond}"); } else { redisMan.setRSV(redKey, currCond); // verifico se salvare bool changed = checkSaveCondition(observ, fullMsg); if (changed || forceSend) { // accodare ed invia nella coda ALARMS (che POI salva in document // MongoDB anche ultimi x minuti di FluxLog...) accodaAlarmLog(sVal, qEncodeFLog(time2, descr, fullMsg)); } else { lgTrace(sVal); } } } else { lgTrace(sVal); } } catch (Exception exc) { lgError($"Eccezione in decodifica Conditions x StreamSuccesfull{Environment.NewLine}{exc}", false); } break; case MTConnect.Devices.DataItemCategory.EVENT: string eValue = ""; try { descr = itemTranslation("E", observ.DataItemId); locTStamp = observ.Timestamp.ToLocalTime(); sVal = $"EVENT: {locTStamp.ToString()} | descr: {descr} | Id: {observ.DataItemId} | Name: {observ.Name}"; if (observ.Values.Count() > 0) { foreach (var item in observ.Values) { sVal += $" | {item.Key}: {item.Value}"; if (item.Key == "Result") { eValue = item.Value; } } } if (isVerboseLog) { lgInfo(sVal); } DateTime tStamp = observ.Timestamp; var time2 = tStamp.ToLocalTime(); // verifico se salvare bool changed = checkSaveEvent(observ, eValue); if (changed || forceSend) { bool sent = accodaFLog(descr, sVal, qEncodeFLog(time2, descr, eValue)); if (sent) { // traccio valore DynData x analisi trackDynData(descr, eValue); } } } catch (Exception exc) { lgError($"Eccezione in decodifica Events x StreamSuccesfull{Environment.NewLine}{exc}", false); } break; case MTConnect.Devices.DataItemCategory.SAMPLE: string sValue = ""; try { descr = itemTranslation("S", observ.DataItemId); locTStamp = observ.Timestamp.ToLocalTime(); sVal = $"SAMPLE: {locTStamp.ToString()} | descr: {descr} | Id: {observ.DataItemId} | Name: {observ.Name}"; if (observ.Values.Count() > 0) { foreach (var item in observ.Values) { sVal += $" | {item.Key}: {item.Value}"; if (item.Key == "Result") { sValue = item.Value; } } } if (isVerboseLog) { lgInfo(sVal); } DateTime tStamp = observ.Timestamp; var time2 = tStamp.ToLocalTime(); // verifico se salvare bool changed = checkSaveSample(observ, sValue); if (changed || forceSend) { bool sent = accodaFLog(descr, sVal, qEncodeFLog(time2, descr, sValue)); if (sent) { // traccio valore DynData x analisi trackDynData(descr, sValue); } } else { if (isVerboseLog) { lgTrace($"NON ACCODATO sample per {observ.DataItemId} - verifica variazione SAMPLE ha dato esito negativo", false); } } } catch (Exception exc) { lgError($"Eccezione in decodifica Samples x StreamSuccesfull{Environment.NewLine}{exc}"); } break; default: break; } if (observ.Values != null && observ.Values.Count() > 0) { foreach (var item in observ.Values) { sMsg += $" | {item.Key}: {item.Value}"; } } lgTrace(sMsg); #if false trackExchDataRaw(sMsg.Length, 1024); #endif trackExchData(64, 4096); } /// /// Effettua invio a MP/IO dell'elenco serializzato dei dataItems /// /// private void sendDataItemsList(List dataItems) { // verifico abilitazione preliminare all'invio if (enableSendDataItem) { // verifico veto ad invio (ogni 60 min...) DateTime adesso = DateTime.Now; if (adesso > DtHelp.dtVetoSenDataItem) { string rawData = JsonConvert.SerializeObject(dataItems); var resp = HttpService.CallUrlPost($"{urlSaveDataItems}", rawData); // imposto nuovo veto DtHelp.dtVetoSenDataItem = adesso.AddMinutes(minVetoSendDataItem); } } } /// /// Esegue log trace del valore che è filtrato in invio /// /// private void traceObservation(IObservation observation) { #if DEBUG // SOLO in debug... trackExchData(64, 4096); #endif } #endregion Private Methods } }