Files
Mapo-IOB-WIN/IOB-WIN-NEXT/Iob/MTConn.cs
T
Samuele Locatelli 8bb0f158b5 SPLIT PROGETTO!!!
- proj di base con le 2 form da ereditare
- progetto globale che contiene TUTTI gli adapter (pronto a venire spezzettato
- gettate le basi x "portare fuori" i vari componenti oppure fare compilazione condizonale
2024-12-20 10:16:32 +01:00

1710 lines
67 KiB
C#

using IOB_UT_NEXT;
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.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
namespace IOB_WIN_NEXT.Iob
{
public class MTConn : Iob.GenericNext
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget TrackHound https://github.com/TrakHound/MTConnect.NET
/// </summary>
/// <param name="caller"></param>
/// <param name="adpConf"></param>
public MTConn(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// gestione invio ritardato contapezzi
pzCountDelay = utils.CRI("pzCountDelay");
// gestione data filtering...
if (!string.IsNullOrEmpty(getOptPar("ENABLE_DATA_FILTER")))
{
bool.TryParse(getOptPar("ENABLE_DATA_FILTER"), out enableDataFilter);
}
// 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 override contapezzi
enablePzCountByApp = utils.CRB("enableContapezzi");
enablePzCountByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE");
disablePzCountByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE");
// init datetime counters
DateTime adesso = DateTime.Now;
lastPzCountSend = adesso;
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);
}
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti document li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Recupera uno specifico dataItem
/// </summary>
/// <param name="diKey"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Recupero dati dinamici...
/// </summary>
public override Dictionary<string, string> getDynData()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
return outVal;
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if ((enablePzCountByApp || enablePzCountByIob) && !(disablePzCountByIob))
{
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} | enablePzCountByIob: {enablePzCountByIob} | disablePzCountByIob: {disablePzCountByIob}");
}
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
DateTime adesso = DateTime.Now;
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);
}
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();
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE per MTC (read only)
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = true;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE per MTC (read only)
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = true;
return answ;
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
if (!connectionOk)
{
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (verboseLog || periodicLog)
{
lgInfo("MTC: ConnKO - tryConnect");
}
// in primis salvo data ping...
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();
//queueInEnabCurr = true;
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 {cIobConf.cncPingAddr}");
}
}
}
}
else
{
needRefresh = true;
}
// se non è ancora connesso faccio procesisng memoria caso disconnesso...
if (!connectionOk)
{
// processo semafori ed invio...
processMemoryDiscon();
}
}
/// <summary>
/// Override disconnessione
/// </summary>
public override void tryDisconnect()
{
queueInEnabCurr = false;
// 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
{
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
/// <summary>
/// Gestione filtraggio dati
/// </summary>
protected bool enableDataFilter = false;
/// <summary>
/// Abilitazione restart (da opt par...)
/// </summary>
protected bool enableMtcRestart = false;
/// <summary>
/// Determina se ha effettuata lettura items in memoria x confronto...
/// </summary>
protected bool hasReadItems = false;
/// <summary>
/// Ultimo current received x gestione update periodico...
/// </summary>
protected DateTime lastCurrent = DateTime.Now;
/// <summary>
/// Oggetto MAIN x connessione MTC
/// </summary>
protected MTConnectHttpClient MTC_ref;
/// <summary>
/// Gestione valori unavailable come POWEROFF (es trevisan)
/// </summary>
protected bool unavailPoweroff = false;
/// <summary>
/// Veto controllos tatus x log...
/// </summary>
protected DateTime vetoCheckStatus = DateTime.Now;
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Verifico se abbia ALMENO un errore...
/// </summary>
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;
}
}
/// <summary>
/// Indica se abbia emergenza premuta
/// </summary>
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;
}
}
/// <summary>
/// Verifico se abbia unavailable x le condizioni principali condPowerOn keyRunMode
/// </summary>
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;
/// <summary>
/// Parametri specifici MTC
/// </summary>
protected MtcParamConf mtcParams { get; set; }
protected DateTime startConnecting { get; set; } = DateTime.Today;
/// <summary>
/// URL x salvataggio elenco dataItems MTC
/// </summary>
protected string urlSaveDataItems
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveDataItems/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveDataItems");
}
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Verifica un DataItem document se il valore corrisponde a quello indicato come "true eValue"
/// restituisce true
/// </summary>
/// <param name="itemName"></param>
/// <param name="trueVal"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Verifica / Salva valore condition
/// </summary>
/// <param name="observ"></param>
/// <param name="cValue">Messaggio completo (livello + testo)</param>
/// <returns></returns>
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 oggetot in memoria...
List<machDataItem> elencoDataItems = new List<machDataItem>();
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;
}
/// <summary>
/// Verifica / Salva valore generico (NON SAMPLE)
/// </summary>
/// <param name="observ"></param>
/// <param name="iValue"></param>
/// <returns></returns>
protected bool checkSaveItem(IObservation observ, string iValue)
{
bool answ = !enableDataFilter;
if (observ != null)
{
if (isVerboseLog)
{
lgInfo($"Richiesta checkSaveItem per {observ} | id: {observ.DataItemId} | eValue: {iValue}");
}
// verifico in memoria se ho l'oggetto condition ed il suo valore..
if (dataItemMem.ContainsKey(observ.DataItemId))
{
// verifico SE sia cambiato...
if (dataItemMem[observ.DataItemId].value != iValue)
{
dataItemMem[observ.DataItemId].value = iValue;
answ = true;
}
dataItemMem[observ.DataItemId].valueTimestamp = observ.Timestamp;
}
else
{
// registro non trovato da aggiungere...
lgInfo($"DataItem non trovato in checkSaveItem: {observ.DataItemId}");
try
{
// provo a creare oggetot in memoria...
List<machDataItem> elencoDataItems = new List<machDataItem>();
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 checkSaveItem{Environment.NewLine}{exc}");
}
}
}
else
{
lgError("Attenzione: checkSaveItem con observ null!");
}
return answ;
}
/// <summary>
/// Verifica / Salva valore SAMPLE document restitusice SE sia variato (document quindi da inviare...)
/// </summary>
/// <param name="observ"></param>
/// <returns></returns>
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<machDataItem> elencoDataItems = new List<machDataItem>();
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: checkSaveItem con observ null!");
}
return answ;
}
/// <summary>
/// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non
/// trovo uso key
/// </summary>
/// <param name="tipo"></param>
/// <param name="id"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Effettua log di un elenco componenti
/// </summary>
/// <param name="elencoComponenti"></param>
protected void logComponentsList(List<MTConnect.Devices.IComponent> elencoComponenti)
{
if (elencoComponenti != null)
{
foreach (var item in elencoComponenti)
{
lgInfo($"Component data | ID: {item.Id} | Name: {item.Name} | Type: {item.Type} | # items: {item.DataItems.Count()}");
// se ho sottocomponenti richiamo...
if (item.Components != null)
{
if (item.Components.Count() > 0)
{
logComponentsList(item.GetComponents().ToList());
}
}
if (item.DataItems.Count() > 0)
{
logDataItemList(item.GetDataItems().ToList());
}
}
}
}
/// <summary>
/// Effettua log di un elenco composizioni
/// </summary>
/// <param name="elencoComposizioni"></param>
protected void logCompositionList(List<MTConnect.Devices.IComposition> elencoComposizioni)
{
if (elencoComposizioni != null)
{
foreach (var item in elencoComposizioni)
{
lgInfo($"Composition data | ID: {item.Id} | Name: {item.Name} | Type: {item.Type} | # items: {item.DataItems.Count()} | path: {item.IdPath}");
}
}
}
/// <summary>
/// Log elenco DataItems
/// </summary>
/// <param name="elencoItems"></param>
protected void logDataItemList(List<MTConnect.Devices.IDataItem> elencoItems)
{
if (elencoItems != null)
{
// loggo devices principali...
foreach (var item in elencoItems)
{
lgInfo($"Device data | ID: {item.Id} | Name: {item.Name} | Category: {item.Category} | # Type: {item.Type} | Path: {item.IdPath}");
}
}
}
/// <summary>
/// Effettua log di un devices (ed eventualmente dei sub-devices...
/// </summary>
/// <param name="listDevices"></param>
protected void logProbeDevices(List<MTConnect.Devices.IDevice> listDevices)
{
if (listDevices != null)
{
// loggo devices principali...
foreach (var device in listDevices)
{
lgInfo($"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)
{
logDataItemList(device.GetDataItems().ToList());
}
if (device.Components.Count() > 0)
{
logComponentsList(device.GetComponents().ToList());
}
// All Compositions (traverse the entire Device model)
if (device.Compositions.Count() > 0)
{
logCompositionList(device.GetCompositions().ToList());
}
}
}
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Struttura dove vengono memorizzati i dataitem ed i rispettivi valori x processing
/// </summary>
private Dictionary<string, MtcDataItemExt> dataItemMem = new Dictionary<string, MtcDataItemExt>();
#endregion Private Fields
#region Private Methods
/// <summary>
/// Verifica ed invia variazioni
/// </summary>
/// <param name="document"></param>
/// <param name="forceSend"></param>
private void checkAndSend(MTConnect.Streams.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");
}
}
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
private void decodeToBaseBitmap()
{
// init a zero...
B_input = 0;
if (queueInEnabCurr)
{
/* -----------------------------------------------------
* bitmap MAPO
* 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)
----------------------------------------------------- */
// 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);
}
// emergenza armata da riportare con bit True/ 1
if (mtcParams.emergencyArmedTrue)
{
//se NON premuta lazo il bit
if (!hasEStopTriggered)
{
B_input += (1 << 5);
}
}
// emergenza armata da riportare come False/0 (!mtcParams.emergencyArmedTrue)
else
{
// se premuta alzo il bit...
if (hasEStopTriggered)
{
B_input += (1 << 5);
}
}
DateTime adesso = DateTime.Now;
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(1000);
// 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}");
}
}
/// <summary>
/// Vera connessione ad MTC
/// </summary>
/// <returns></returns>
private void doConnect()
{
isConnecting = true;
startConnecting = DateTime.Now;
// reset memoria dataItem..
dataItemMem = new Dictionary<string, MtcDataItemExt>();
// predisposizione conf oggetto di comunicazione MTC
short port = 5000;
short.TryParse(cIobConf.cncPort, out port);
string callUrl = $"{cIobConf.cncIpAddr}:{port}";
// test probe!
try
{
lgInfo($"Test Probe MTC Client: {callUrl}");
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)
{
// anche se non del tutto vero...
connectionOk = true;
lgInfo($"Effettuata correttamente PROBE per device MTC all'URL {callUrl}");
lgInfo($"---------------- Elenco Devices ----------------");
// loggo devices principali...
logProbeDevices(prbDoc.Devices.ToList());
}
}
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(cIobConf.cncIpAddr, port);
//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.ProbeReceived += DevicesSuccessful;
MTC_ref.CurrentReceived += MTC_ref_CurrentReceived;
//MTC_ref.CurrentReceived += CurrentSuccessful;
// 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;
}
MTC_ref.Start();
//// attendo 2 sec...
//Thread.Sleep(2000);
// fix tempi!
DateTime adesso = DateTime.Now;
lastPzCountSend = adesso;
lastWarnODL = adesso;
lastCurrent = adesso;
}
catch
{ }
isConnecting = false;
}
/// <summary>
/// Formatta un dataitem da uno stream SAMPLE
/// </summary>
/// <param name="dSamplePeriod"></param>
/// <param name="threshDBand"></param>
/// <param name="uuid"></param>
/// <param name="observ"></param>
/// <param name="fullMsg"></param>
/// <returns></returns>
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...
MTConnect.Devices.DataItem newDataItem = new MTConnect.Devices.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);
}
/// <summary>
/// Formatta un dataitem x salvataggio in memoria locale
/// </summary>
/// <param name="dSamplePeriod"></param>
/// <param name="threshDBand"></param>
/// <param name="uuid"></param>
/// <param name="dataItem"></param>
/// <param name="value"></param>
/// <returns></returns>
private MtcDataItemExt formatDataItem(ref int dSamplePeriod, ref float threshDBand, ref string uuid, MTConnect.Devices.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}";
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;
}
/// <summary>
/// Formatta un dataitem x salvataggio in memoria locale
/// </summary>
/// <param name="dSamplePeriod"></param>
/// <param name="threshDBand"></param>
/// <param name="uuid"></param>
/// <param name="dataItem"></param>
/// <param name="value"></param>
/// <returns></returns>
private MtcDataItemExt formatDataItem(ref int dSamplePeriod, ref float threshDBand, ref string uuid, MTConnect.Devices.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;
}
/// <summary>
/// Effettua lettura file di conf specifico MTC da oggetto serializzato json <paramref
/// name="fileName">Nome file da cui leggere i parametri json</paramref>
/// </summary>
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<MtcParamConf>(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;
foreach (var asset in doc.Assets)
{
// Print AssetId to the Console
lgInfo($"Asset: {asset.AssetId}");
}
}
private void MTC_ref_ClientStarted(object sender, EventArgs e)
{
lgInfo($"Client Started!");
}
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;
}
/// <summary>
/// Effettuata discovery iniziale valori CURRENT
/// </summary>
/// <param name="sender"></param>
/// <param name="document"></param>
private void MTC_ref_CurrentReceived(object sender, MTConnect.Streams.IStreamsResponseDocument document)
{
lgInfo("CURRENT received");
queueInEnabCurr = true;
connectionOk = true;
if (document != null)
{
if (document.Streams != null)
{
lgInfo($"CurrentSuccessful: trovati {document.Streams.Count()} streams");
}
checkAndSend(document, true);
}
else
{
lgError("MTC_ref_CurrentReceived ERROR: document è null");
}
}
/// <summary>
/// Processing singola observation ricevuta
/// </summary>
/// <param name="sender"></param>
/// <param name="observ"></param>
private void MTC_ref_ObservationReceived(object sender, MTConnect.Observations.IObservation observ)
{
connectionOk = true;
processObservation(observ, false);
}
/// <summary>
/// Effettuata discovery iniziale valori PROBE
/// </summary>
/// <param name="sender"></param>
/// <param name="document"></param>
private void MTC_ref_ProbeReceived(object sender, MTConnect.Devices.IDevicesResponseDocument document)
{
queueInEnabCurr = true;
connectionOk = true;
lgInfo("STEP 01 DevicesSuccessful reached!");
MtcDataItemExt currDataItem = null;
machDataItem currMapoDataItem = null;
List<machDataItem> elencoDataItems = new List<machDataItem>();
int dSamplePeriod = 0;
float threshDBand = 0;
string uuid = "";
if (document != null)
{
foreach (var device in document.Devices)
{
List<MTConnect.Devices.IDataItem> dataItems = device.GetDataItems().ToList();
//List<MTConnect.Devices.DataItem> 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);
}
hasReadItems = true;
lgInfo($"STEP 04: memorizzati {dataItemMem.Count} oggetti in memoria");
}
else
{
lgError("STEP 05 error: document null!");
}
}
/// <summary>
/// Ricevuto SAMPLE stream da campionamenti ricevuti
/// </summary>
/// <param name="document"></param>
private void MTC_ref_SampleReceived(object sender, MTConnect.Streams.IStreamsResponseDocument document)
{
connectionOk = true;
lgTrace("SAMPLE received");
checkAndSend(document, false);
}
/// <summary>
/// Processing di una singola observ in un datastream
/// </summary>
/// <param name="observ">Osservazione da processare</param>
/// <param name="forceSend">invio forzato</param>
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 = "";
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)
{
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 verboso SEMPRE!
lgInfo(sVal);
DateTime tStamp = observ.Timestamp;
var time2 = tStamp.ToLocalTime();
// 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));
}
}
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 = checkSaveItem(observ, eValue);
if (changed || forceSend)
{
accodaFLog(sVal, qEncodeFLog(time2, 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)
{
accodaFLog(sVal, qEncodeFLog(time2, 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);
}
/// <summary>
/// Effettua invio a MP/IO dell'elenco serializzato dei dataItems
/// </summary>
/// <param name="dataItems"></param>
private void sendDataItemsList(List<machDataItem> dataItems)
{
string rawData = JsonConvert.SerializeObject(dataItems);
utils.callUrlNow($"{urlSaveDataItems}", rawData);
}
/// <summary>
/// Esegue log trace del valore che è filtrato in invio
/// </summary>
/// <param name="observation"></param>
private void traceObservation(IObservation observation)
{
string sMsg = $"VETO: - {observation.DataItemId} | {observation.Category}";
if (observation.Values != null && observation.Values.Count() > 0)
{
foreach (var item in observation.Values)
{
sMsg += $" | {item.Key}: {item.Value}";
}
}
lgTrace(sMsg);
}
#endregion Private Methods
}
}