582 lines
16 KiB
C#
582 lines
16 KiB
C#
using MTC;
|
|
using NLog;
|
|
using OPC_UA_REDIS;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
|
|
namespace SCMA.AdapterCom
|
|
{
|
|
|
|
/// <summary>
|
|
/// Gateway di comunicazione secondo diversi standard, partendo da oggetti BASE MTC
|
|
/// </summary>
|
|
public class Gateway
|
|
{
|
|
|
|
#region oggetti base
|
|
|
|
/// <summary>
|
|
/// Dictionary degli errori item ricorrenti
|
|
/// </summary>
|
|
protected Dictionary<string, int> itemErrors;
|
|
/// <summary>
|
|
/// wrapper di log
|
|
/// </summary>
|
|
public static Logger lg;
|
|
/// <summary>
|
|
/// Protocollo attivo per la comunicazione dell'adapter
|
|
/// </summary>
|
|
public gwProtocol protocollo;
|
|
/// <summary>
|
|
/// STOBE allarmi:
|
|
/// 1024 bit di strobe degli allarmi attivi (32 word da 4byte/32 bit di flags...)
|
|
/// </summary>
|
|
public byte[] AlarmFlags;
|
|
/// <summary>
|
|
/// Oggetto elenco allarmi
|
|
/// </summary>
|
|
public allarme[] elencoAllarmi;
|
|
/// <summary>
|
|
/// DataModel di riferimento
|
|
/// </summary>
|
|
public string DataModel;
|
|
/// <summary>
|
|
/// Dizionario conversione nome variabili con replace "like" (per gestione REDIS "_" --> ":")
|
|
/// </summary>
|
|
public Dictionary<string, replDict> nameRepRoles = new Dictionary<string, replDict>();
|
|
/// <summary>
|
|
/// Dizionario conversione nome variabiliin memoria REDIS
|
|
/// </summary>
|
|
public Dictionary<string, string> memCopyList = new Dictionary<string, string>();
|
|
/// <summary>
|
|
/// Porta comunicazione standard oggetto
|
|
/// </summary>
|
|
public int port;
|
|
/// <summary>
|
|
/// Stringa di configurazione globale
|
|
/// </summary>
|
|
public string connConfig;
|
|
/// <summary>
|
|
/// Flag per indicare se l'obj stia ancora girando
|
|
/// </summary>
|
|
private bool myRunning = false;
|
|
/// <summary>
|
|
/// Indicatore public di oggetto running running.
|
|
/// </summary>
|
|
public bool Running { get { return myRunning; } }
|
|
/// <summary>
|
|
/// la parte di "mark & sweep" (segnala ed invia) è iniziata e stiamo tracciando le conditions.
|
|
/// </summary>
|
|
bool myBegun = false;
|
|
/// <summary>
|
|
/// Elenco di TUTTI i NODI ITEMS gestiti dal gateway (item/variabile)...
|
|
/// </summary>
|
|
public Dictionary<string, itemNode> itemNodes = new Dictionary<string, itemNode>();
|
|
/// <summary>
|
|
/// Elenco di TUTTI i NODI CONDITIONS gestiti dal gateway (allarme/condizione)...
|
|
/// </summary>
|
|
public Dictionary<string, itemNode> conditionNodes = new Dictionary<string, itemNode>();
|
|
/// <summary>
|
|
/// Elenco ultimi allarmi riportati...
|
|
/// </summary>
|
|
protected Dictionary<string, string> lastAlarmList;
|
|
/// <summary>
|
|
/// Bool di allarmi presenti
|
|
/// </summary>
|
|
public bool alarmPresent = false;
|
|
|
|
#endregion
|
|
|
|
#region gestione globale oggetto
|
|
|
|
/// <summary>
|
|
/// init classe come output su LOGFILE
|
|
/// </summary>
|
|
public Gateway()
|
|
{
|
|
lg = LogManager.GetCurrentClassLogger();
|
|
itemErrors = new Dictionary<string, int>();
|
|
port = 0;
|
|
connConfig = "";
|
|
protocollo = gwProtocol.LOGFILE;
|
|
lastAlarmList = new Dictionary<string, string>();
|
|
}
|
|
/// <summary>
|
|
/// Inizia la raccolta dati per confronto modifica da precedente...
|
|
/// </summary>
|
|
public virtual void beginDataCollect()
|
|
{
|
|
myBegun = true;
|
|
foreach (object di in itemNodes)
|
|
{
|
|
// inizializza ogni oggetto (in particolare di tipo alarm/conditions x check variazione)
|
|
//di.Begin();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Fa la verifica di cosa sia cambiato ed invia
|
|
/// </summary>
|
|
public virtual void sendChanged()
|
|
{
|
|
if (myBegun)
|
|
{
|
|
foreach (object di in itemNodes)
|
|
{
|
|
//di.Prepare();
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
foreach (object di in itemNodes)
|
|
{
|
|
// pulizia oggetti
|
|
//di.Cleanup();
|
|
}
|
|
myBegun = false;
|
|
}
|
|
/// <summary>
|
|
/// Imposta TUTTI i data items null/unavailable
|
|
/// </summary>
|
|
public virtual void setAllUnavailable()
|
|
{
|
|
foreach (object di in itemNodes)
|
|
{
|
|
//// imposta a unavailable
|
|
//di.Unavailable();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// AVVIA processo lettura CNC e invio dati a client
|
|
/// </summary>
|
|
public virtual void start()
|
|
{
|
|
}
|
|
/// <summary>
|
|
/// FERMA processo lettura CNC e invio dati a client
|
|
/// </summary>
|
|
public virtual void stop()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region gestione controllo stato catena applicativi e messaggi da piattaforma
|
|
|
|
|
|
/// <summary>
|
|
/// Effettua chiamata URL e restituisce risultato
|
|
/// </summary>
|
|
/// <param name="URL"></param>
|
|
/// <returns></returns>
|
|
public string callUrl(string URL)
|
|
{
|
|
string answ = "";
|
|
try
|
|
{
|
|
var client = new WebClient();
|
|
client.Headers.Add("user-agent", utils.CRS("appName"));
|
|
answ = client.DownloadString(URL);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error(exc, "Errore in chiamata URL {0}{1}{2}", URL, Environment.NewLine, exc);
|
|
}
|
|
// restituisco valore!
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Verifica lo stato di un servizio indicato per via numerica (differente x MTC / OPC-UA-REDIS)
|
|
/// </summary>
|
|
/// <param name="numServ"></param>
|
|
/// <returns></returns>
|
|
public virtual bool checkStatus(int numServ)
|
|
{
|
|
bool answ = false;
|
|
return answ;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region gestione nodi (dataItems / conditions)
|
|
|
|
/// <summary>
|
|
/// Aggiunge un generico item all'elenco di quelli tracciati INDICANDO IL TYPE
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void addItemNodeByType(string key, itemType tipo)
|
|
{
|
|
if (!itemNodes.ContainsKey(key))
|
|
{
|
|
itemNode currItem = new itemNode(tipo, availStatus.UNAVAILABLE.ToString().ToLower());
|
|
itemNodes.Add(key, currItem);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Aggiunge un generico item all'elenco di quelli tracciati INDICANDO IL TYPE ed il valore iniziale
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void addItemNodeAndSet(string key, itemType tipo, object value)
|
|
{
|
|
addItemNodeByType(key, tipo);
|
|
// SOLO se richiesto FULL RESET...
|
|
if (baseUtils.CRB("fullReset"))
|
|
{
|
|
updateItemNodeValue(key, value);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Aggiunge un generico item all'elenco di quelli tracciati (NON STRONGLY TYPED!!! occhio!!!)
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void addItemNode(string key, object value)
|
|
{
|
|
// se non c'è già elemento lo aggiungo...
|
|
if (!itemNodes.ContainsKey(key))
|
|
{
|
|
// default: event...
|
|
itemNode currItem = new itemNode(itemType.Event, value);
|
|
itemNodes.Add(key, currItem);
|
|
}
|
|
}
|
|
public virtual void addItemNode(object value)
|
|
{
|
|
if (!itemNodes.ContainsKey(value.ToString()))
|
|
{
|
|
// default: event...
|
|
itemNode currItem = new itemNode(itemType.Event, value);
|
|
itemNodes.Add(value.ToString(), currItem);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Rimuove TUTTI i data items tracciati
|
|
/// </summary>
|
|
public virtual void removeAllItemNodes()
|
|
{
|
|
itemNodes.Clear();
|
|
}
|
|
/// <summary>
|
|
/// Rimuove un item dall'elenco di quelli tracciati
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
public virtual void removeItemNode(string key)
|
|
{
|
|
itemNodes.Remove(key);
|
|
}
|
|
/// <summary>
|
|
/// RESTITUISCE un item da KEY
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
public virtual object getItemNode(string key)
|
|
{
|
|
object answ = null;
|
|
// controllos e ci sia chiave...
|
|
if (itemNodes.ContainsKey(key))
|
|
{
|
|
try
|
|
{
|
|
answ = itemNodes[key].cObject;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Eccezione in getItemNode per la chiave {key}:{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
answ = "";
|
|
try
|
|
{
|
|
// salvo in memoria nelle chiavi non trovate
|
|
if (itemErrors.ContainsKey(key))
|
|
{
|
|
itemErrors[key]++;
|
|
}
|
|
else
|
|
{
|
|
itemErrors.Add(key, 1);
|
|
}
|
|
// ...se > 100 volte --> log...
|
|
if (itemErrors[key] > 100)
|
|
{
|
|
lg.Error($"Errore: è stato richiesto 100 volte in getItemNode la chiave {key} NON ESISTENTE");
|
|
itemErrors[key] = 1;
|
|
}
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// AGGIORNA un generico item all'elenco di quelli tracciati (NON STRONGLY TYPED!!! occhio!!!)
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void updateItemNodeValue(string key, object value)
|
|
{
|
|
itemNodes[key].cObject = value;
|
|
}
|
|
/// <summary>
|
|
/// AGGIORNA un generico item all'elenco di quelli tracciati (NON STRONGLY TYPED!!! occhio!!!)
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="code"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void updateItemNodeCodeValue(string key, string code, object value)
|
|
{
|
|
itemNodes[key].cObject = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggiunge un generico item all'elenco di quelli tracciati (NON STRONGLY TYPED!!! occhio!!!)
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <param name="value"></param>
|
|
public virtual void addConditionNode(string key, object value)
|
|
{
|
|
// se non c'è già elemento lo aggiungo...
|
|
if (!conditionNodes.ContainsKey(key))
|
|
{
|
|
// default: event...
|
|
itemNode currItem = new itemNode(itemType.Condition, value);
|
|
conditionNodes.Add(key, currItem);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Rimuove TUTTI i CONDITION NODES tracciati
|
|
/// </summary>
|
|
public virtual void removeAllConditionNodes()
|
|
{
|
|
conditionNodes.Clear();
|
|
}
|
|
/// <summary>
|
|
/// Rimuove un CONDITION NODE dall'elenco di quelli tracciati
|
|
/// </summary>
|
|
/// <param name="key">chaive</param>
|
|
public virtual void removeConditionNode(string key)
|
|
{
|
|
conditionNodes.Remove(key);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region gestione allarmi
|
|
|
|
/// <summary>
|
|
/// processo il vettore LOCALE degli allarmi
|
|
/// </summary>
|
|
public virtual void processAlarm()
|
|
{
|
|
// continuo controllo
|
|
if (AlarmFlags != null && AlarmFlags.Length > 0)
|
|
{
|
|
// variabili helper
|
|
StFlag32 AlarmBlock = 0;
|
|
allarme currAllarm;
|
|
Dictionary<string, string> grpAlarmList = new Dictionary<string, string>();
|
|
string alarmChannel = "";
|
|
// controllo TUTTI i bit della variabile COMPLETA degli status allarmi: se ce ne sono di alzati DEVO processare...
|
|
for (int i = 0; i < AlarmFlags.Length / 4; i++)
|
|
{
|
|
// leggo 32bit alla volta...
|
|
AlarmBlock = (StFlag32)BitConverter.ToUInt32(AlarmFlags, i * 4);
|
|
if (AlarmBlock != StFlag32.NONE)
|
|
{
|
|
alarmPresent = true;
|
|
for (int j = 0; j < 32; j++)
|
|
{
|
|
// converto! e aggiungo allarmi sollevati al corretto controller allarmi...
|
|
if (AlarmBlock.HasFlag((StFlag32)Math.Pow(2, j)))
|
|
{
|
|
// recupero allarme da oggetto in memoria...
|
|
currAllarm = elencoAllarmi[i * 32 + j];
|
|
addAlarm(currAllarm);
|
|
// ora in base al TIPO di gateway determino il canale allarme...
|
|
alarmChannel = currAllarm.gruppo;
|
|
// se sono in SOURS
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
// sostituisco gruppo con nome esatto...
|
|
switch (alarmChannel.ToUpper())
|
|
{
|
|
case "CNC":
|
|
alarmChannel = nCncConditions;
|
|
break;
|
|
case "HMI":
|
|
alarmChannel = nHmiConditions;
|
|
break;
|
|
case "PLC":
|
|
alarmChannel = nPlcConditions;
|
|
break;
|
|
}
|
|
// sostituisco livello allarme
|
|
|
|
// aggiorno stringa...
|
|
if (!grpAlarmList.ContainsKey(alarmChannel))
|
|
{
|
|
grpAlarmList.Add(alarmChannel, "");
|
|
}
|
|
grpAlarmList[alarmChannel] += string.Format("{0}|{1},", currAllarm.codNum, AdapterRed.condLevel(currAllarm.livello));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se in modalità SOUR riporto variabile alarmState
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
foreach (var item in grpAlarmList)
|
|
{
|
|
// tolgo ultima stringa
|
|
string elenco = item.Value.Substring(0, item.Value.Length - 1);
|
|
// salvo...
|
|
updateItemNodeValue(item.Key, elenco);
|
|
}
|
|
// se ho ALMENO un allarme attivo, altrimenti procedo che resetta da solo
|
|
if (grpAlarmList.Count > 0)
|
|
{
|
|
// salvo i LAST alarm...
|
|
lastAlarmList = grpAlarmList;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!alarmPresent)
|
|
{
|
|
// devo resettare gli allarmi notificati...
|
|
foreach (var item in lastAlarmList)
|
|
{
|
|
// tolgo ultima stringa
|
|
string elenco = "0|000";
|
|
// salvo...
|
|
updateItemNodeValue(item.Key, elenco);
|
|
}
|
|
// resetto
|
|
lastAlarmList = new Dictionary<string, string>();
|
|
}
|
|
// se in modalità SOUR riporto variabile alarmState
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
updateItemNodeValue("Alarm", alarmPresent);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Nome del channel allarmi CNC
|
|
/// </summary>
|
|
public string nCncConditions
|
|
{
|
|
get
|
|
{
|
|
string answ = "CNC";
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
answ = "Cnc:Condition";
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Nome del channel allarmi HMI
|
|
/// </summary>
|
|
public string nHmiConditions
|
|
{
|
|
get
|
|
{
|
|
string answ = "HMI";
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
answ = "Hmi:Condition";
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Nome del channel allarmi PLC
|
|
/// </summary>
|
|
public string nPlcConditions
|
|
{
|
|
get
|
|
{
|
|
string answ = "PLC";
|
|
if (protocollo == gwProtocol.SOURS)
|
|
{
|
|
answ = "Plc:Condition";
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// SETUP oggetti gestione allarmi
|
|
/// </summary>
|
|
public virtual void addAlarmNodes()
|
|
{
|
|
// minimo sempre PLC/CNC
|
|
addConditionNode(nCncConditions, "CNC");
|
|
addConditionNode(nPlcConditions, "PLC");
|
|
}
|
|
/// <summary>
|
|
/// INIT allarmi a normal
|
|
/// </summary>
|
|
public virtual void initAlarms()
|
|
{
|
|
}
|
|
/// <summary>
|
|
/// INIT di un SINGOLO NODO di allarme a normal
|
|
/// </summary>
|
|
/// <param name="alarmNode"></param>
|
|
public virtual void initAlarm(itemNode alarmNode)
|
|
{
|
|
}
|
|
/// <summary>
|
|
/// Aggiunta SINGOLO allarme su GENERICO (global) nodo
|
|
/// </summary>
|
|
/// <param name="currAllarm">Allarme da riportare al nodo</param>
|
|
public virtual void addAlarm(allarme currAllarm)
|
|
{
|
|
}
|
|
/// <summary>
|
|
/// Aggiunta SINGOLO allarme su SPECIFICO nodo
|
|
/// </summary>
|
|
/// <param name="alarmNode">Generico nodo di tipo condition</param>
|
|
/// <param name="currAllarm">Allarme da riportare al nodo</param>
|
|
public virtual void addAlarm(itemNode alarmNode, allarme currAllarm)
|
|
{
|
|
}
|
|
/// <summary>
|
|
/// RESTITUISCE un nodo CONDITION da KEY
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
public virtual object getAlarmNode(string key)
|
|
{
|
|
return conditionNodes[key];
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipologia protocolli di comunicazione ammessi
|
|
/// </summary>
|
|
public enum gwProtocol
|
|
{
|
|
/// <summary>
|
|
/// NESSUN protocollo reale --> FILE DUMP sul log...
|
|
/// </summary>
|
|
LOGFILE,
|
|
/// <summary>
|
|
/// Protocollo di comunicazione MTConnect
|
|
/// </summary>
|
|
MTC,
|
|
/// <summary>
|
|
/// Protocollo di comunicazione SCM.OPC.UA.REDIS.SERVER
|
|
/// </summary>
|
|
SOURS
|
|
}
|
|
}
|