Files
2017-03-29 18:11:29 +02:00

728 lines
25 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;
using MTC;
namespace MTC_ADB
{
using MTConnect;
using System.Configuration;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
public class AdapterGeneric
{
/// <summary>
/// wrapper di log
/// </summary>
public static Logger lg;
/// <summary>
/// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il DB
/// </summary>
protected bool adpCommAct;
/// <summary>
/// valore booleano di check se sia stato AVVIATO l'adapter (Running)
/// </summary>
public bool adpRunning;
/// <summary>
/// valore booleano di check se l'adapter STIA SALVANDO
/// </summary>
public bool adpSaving;
/// <summary>
/// valore booleano (richiesta di riavvio automatico)
/// </summary>
public bool adpTryRestart;
/// <summary>
/// ultimo ID letto
/// </summary>
protected uint lastId = 0;
/// <summary>
/// DataOra ultimo avvio adapter x watchdog
/// </summary>
protected DateTime adpStartRun;
/// <summary>
/// Data/ora ultimo avvio adapter
/// </summary>
public DateTime dtAvvioAdp = DateTime.Now;
/// <summary>
/// Data/ora ultimo spegnimento adapter
/// </summary>
public DateTime dtStopAdp = DateTime.Now;
/// <summary>
/// vettore gestione cronometraggi
/// </summary>
public DateTime inizio;
/// <summary>
/// adapter globale
/// </summary>
public Adapter mAdapter = new Adapter();
/// <summary>
/// Form chiamante
/// </summary>
protected MainForm parentForm;
/// <summary>
/// Numero tentativi di riconnesiosne prima di chiamare "fermaAdapter" nel parent...
/// </summary>
protected int numRetryConnDb = 10;
/// <summary>
/// Tipo server DB
/// </summary>
public string ServerType = "ND";
/// <summary>
/// Indirizzo server DB
/// </summary>
public string ServerAddress = "";
/// <summary>
/// Nome DB
/// </summary>
public string DbName = "";
/// <summary>
/// User connessione
/// </summary>
protected string DbUser = "";
/// <summary>
/// Pwd connessione
/// </summary>
protected string DbPwd = "";
/// <summary>
/// Oggetto connessione al DB
/// </summary>
protected DbConnection DbConn;
/// <summary>
/// testo della query da eseguire...
/// </summary>
public string Query { get; set; }
/// <summary>
/// Singola istanza valore tipo DbTimeseries
/// </summary>
public class DbTS
{
/// <summary>
/// Vettore della serie storica
/// </summary>
public Sample mSampleData;
/// <summary>
/// Classe gestione dati POWER
/// </summary>
/// <param name="ident">Nome (univoco) variabile da pubblicare</param>
public DbTS(string ident)
{
mSampleData = new Sample(ident);
}
}
#region DataItems Globali
/// <summary>
/// Disponibilità (del DB)
/// </summary>
public Event mAvail = new Event("AVAIL");
/// <summary>
/// Status (ADP Running = ON / stopped INACTIVE)
/// </summary>
public Event mStatus = new Event("STATUS");
/// <summary>
/// Orologio
/// </summary>
public Sample mClock = new Sample("CLOCK");
/// <summary>
/// Messaggi (generico)
/// </summary>
public MTConnect.Message mMessage = new MTConnect.Message("MESSAGE");
/// <summary>
/// vettore dati da lettura DB
/// </summary>
public DbTS[] vettDbData;
#endregion
public event EventHandler eh_refreshed;
/// <summary>
/// inizializzo l'oggetto
/// </summary>
/// <param name="caller"></param>
/// <param name="adpConf"></param>
public AdapterGeneric(MainForm caller)
{
lg = LogManager.GetCurrentClassLogger();
lg.Info("Avvio AdapterGeneric");
// salvo al form chiamante
parentForm = caller;
numRetryConnDb = utils.CRI("numRetryConnDb");
adpRunning = false;
adpSaving = false;
// item base
mAdapter.AddDataItem(mAvail);
mAdapter.AddDataItem(mStatus);
mAdapter.AddDataItem(mClock);
mAdapter.AddDataItem(mMessage);
mAvail.Value = "ND";
mStatus.Value = "ND";
mClock.Value = string.Format("{0:yyyy-MM-dd} {0:HH:mm:ss}", DateTime.Now);
mMessage.Code = "ND";
mMessage.Value = "INIT";
createDbConn();
// aggiunto 1 valore x la gestione timeseries...
vettDbData = new DbTS[1];
for (int i = 0; i < vettDbData.Length; i++)
{
vettDbData[i] = new DbTS(string.Format("DbVal_E_{0:00}", i + 1));
mAdapter.AddDataItem(vettDbData[i].mSampleData);
}
// concluso!
lg.Info("Istanziata classe ADB");
}
/// <summary>
/// Inizializzazione oggetti connessione al DB...
/// </summary>
private void createDbConn()
{
DbProviderFactory factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
DbConn = factory.CreateConnection();
}
/// <summary>
/// Lettura conf DB
/// </summary>
public void loadServerConf()
{
// disconnetto e riconnetto SE fosse già connesso...
if (!connectionOk)
{
ServerType = utils.CRS("ServerType");
ServerAddress = utils.CRS("ServerAddress");
DbName = utils.CRS("DbName");
DbUser = utils.CRS("DbUser");
DbPwd = utils.CRS("DbPwd");
DbConn.ConnectionString = string.Format("SERVER={0};database={1};uid={2};password={3}", ServerAddress, DbName, DbUser, DbPwd);
}
}
/// <summary>
/// Caricamento altri file necessari epr adapter all'avvio
/// </summary>
protected virtual void loadOtherFile()
{
}
#region metodi adapter
public void loadPersData()
{
// nuova lettura valori da file persistenza...
lastId = getStoredValUInt("LAST_ID");
}
/// <summary>
/// Avvia l'adapter sulla porta richiesta
/// </summary>
/// <param name="port"></param>
public virtual void startAdapter(int port)
{
lg.Info("Starting adapter...");
adpRunning = true;
dtAvvioAdp = DateTime.Now;
// inizializzo vettori di utility..
loadOtherFile();
// salvo porta!
mAdapter.Port = port;
// avvio!
mAdapter.Start();
// setto status a ON
mStatus.Value = "ON";
// resetto running flag...
adpCommAct = false;
//mAlarmGeneral.Normal();
adpTryRestart = true;
parentForm.displayTaskAndWait("Adapter Started!");
}
/// <summary>
/// ferma l'adapter...
/// </summary>
/// <param name="tryRestart">indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato in automatico)</param>
public void stopAdapter(bool tryRestart)
{
parentForm.displayTaskAndWait("Stopping adapter...");
adpTryRestart = false;
mStatus.Value = "INACTIVE";
// faccio una chiamata extra ad ogni metodo di check...
gaterAndSend(gatherCycle.HF);
gaterAndSend(gatherCycle.MF);
gaterAndSend(gatherCycle.LF);
gaterAndSend(gatherCycle.VLF);
parentForm.displayTaskAndWait("Stopping adapter - last periodic data read...");
// chiudo la connessione all'adapter...
tryDisconnect();
// Stop everything...
try
{
mAdapter.Stop();
}
catch (Exception exc)
{
lg.Error(exc, "Eccezione in chiusura Adapter");
}
dtStopAdp = DateTime.Now;
adpTryRestart = tryRestart;
adpRunning = false;
// chiudo!
parentForm.resetProgBar();
parentForm.displayTaskAndWait("Adapter Stopped.");
}
/// <summary>
/// effettua recupero dati ed invio valori modificati...
/// </summary>
/// <param name="ciclo"></param>
public void gaterAndSend(gatherCycle ciclo)
{
// controllo connessione/connettività
if (connectionOk)
{
// controllo non sia già in esecuzione...
if (!adpCommAct)
{
// provo ad avviare
try
{
// avvio fase raccolta dati
mAdapter.Begin();
// imposto flag adapter running..
adpCommAct = true;
adpStartRun = DateTime.Now;
}
catch (Exception exc)
{
parentForm.displayTaskAndWait(string.Format("Adapter NOT STARTED!!!{0}{1}", Environment.NewLine, exc));
adpCommAct = false;
adpStartRun = DateTime.Now;
}
if (adpCommAct)
{
// try / catch generale altrimenti segno che è disconnesso...
try
{
// ciclo HF (nulla)
if (ciclo == gatherCycle.HF)
{
// gestisco dati stato adapter
getGlobalData();
}
else if (ciclo == gatherCycle.MF)
{
getMainData();
}
// ciclo lento
else if (ciclo == gatherCycle.LF)
{
// invio dati "lenti"
getSlowChangingData();
}
// ciclo lentissimo
else if (ciclo == gatherCycle.VLF)
{
// eventuale log!
if (utils.CRB("recTime")) logTimeResults();
}
// INVIO dati variati!
mAdapter.SendChanged();
}
catch (Exception exc)
{
// segnalo eccezione e indico disconnesso...
DbConn.Close();
numRetryConnDb--;
if (numRetryConnDb < 0)
{
numRetryConnDb = utils.CRI("numRetryConnDb");
lg.Error(exc, string.Format("Errore in gestione ciclo principale ADP, richiesto reset completo{0}{1}", Environment.NewLine, exc));
mMessage.Code = "ERR-ADP-RESET";
mMessage.Value = "Effettuato Reset Adapter";
parentForm.fermaAdapter(true);
}
else
{
lg.Error(exc, string.Format("Errore in gestione ciclo principale ADP, tentativo reset{0}{1}", Environment.NewLine, exc));
mMessage.Code = "ERR-DB-RESET";
mMessage.Value = "Reset Connessione DB";
}
}
// tolgo flag running
adpCommAct = false;
}
else
{
lg.Info("ADP not running...");
}
}
else
{
// log ADP running
lg.Error("Non eseguo chiamata: ADP ancora in running");
// se è bloccato da oltre maxSec lo sblocco...
if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec"))
{
// tolgo flag running
adpCommAct = false;
adpStartRun = DateTime.Now;
}
}
}
else
{
// log connessione KO
lg.Error("ADP - Connessione non disponibile, provo a riconnettere");
mMessage.Code = "ERR-DB-CONN";
mMessage.Value = "Connessione DB non disponibile, tentativo riconnessione";
// provo a riconnettere SE abilitato tryRestart...
if (adpTryRestart && !connectionOk)
{
lg.Info("ConnKO - tryConnect");
tryConnect();
}
}
if (eh_refreshed != null)
{
eh_refreshed(this, new EventArgs());
}
}
/// <summary>
/// salvo e controllo dati globali...
/// </summary>
public virtual void getGlobalData()
{
string currAvail = "";
if (connectionOk)
{
currAvail = "AVAILABLE";
}
else
{
currAvail = "DISCONNECTED";
}
if (mAvail.Value.ToString() != currAvail)
{
mAvail.Value = currAvail;
}
}
/// <summary>
/// riporta il log di tutti i dati di results temporali registrati
/// </summary>
public void logTimeResults()
{
if (TimingData.results.Count > 0)
{
lg.Info("{0}--------------- START TIMING DATA ---------------", Environment.NewLine);
int globNumCall = 0;
TimeSpan globAvgMsec = new TimeSpan(0);
foreach (TimeRec item in TimingData.results)
{
lg.Info("Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec", item.codCall, item.numCall, item.avgMsec);
globNumCall += item.numCall;
globAvgMsec += item.totMsec;
}
// riporto conteggio medio al secondo...
lg.Info("Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec, impegno MEDIO del canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds);
lg.Info("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine);
}
}
/// <summary>
/// Metodo base connessione...
/// </summary>
public virtual void tryConnect()
{
if (!connectionOk)
{
try
{
DbConn.Open();
mMessage.Code = "DB-CONN";
mMessage.Value = "DB Connesso";
}
catch (Exception exc)
{
mMessage.Code = "DB-DISC";
mMessage.Value = "DB Disconnesso";
lg.Error(exc, string.Format("Errore apertura connessione DB: {0}{1}", Environment.NewLine, exc));
mAdapter.SendChanged();
}
}
else
{
lg.Info("Exit tryConnect (ADP connected)");
}
}
/// <summary>
/// Metodo base disconnessione...
/// </summary>
public virtual void tryDisconnect()
{
try
{
// CHIUDO!
DbConn.Close();
mMessage.Code = "DB-DISC";
mMessage.Value = "ADP - Disconnessione DB";
}
catch (Exception exc)
{
lg.Error(exc, string.Format("Errore in tryDisconnect da DB: {0}{1}", Environment.NewLine, exc));
mMessage.Code = "ERR-DB-DISC";
mMessage.Value = "Errore durante disconnessione DB";
}
}
/// <summary>
/// Salva verifica stato connessione OK
/// </summary>
/// <returns></returns>
public virtual bool connectionOk
{
get
{
return DbConn.State == System.Data.ConnectionState.Open;
}
}
/// <summary>
/// Restituisce conteggio record disponibili e lo restituisce
/// </summary>
/// <returns></returns>
public string numRecAvail
{
get
{
string answ = "NA";
if (connectionOk)
{
// recupero conteggio degli eventi...
Query = string.Format("SELECT COUNT(id) AS num FROM tracelog WHERE resource = 'E' AND device=1 AND id > {0}", lastId);
DbCommand qrycmd = DbConn.CreateCommand();
qrycmd.CommandText = Query;
qrycmd.Connection = DbConn;
using (DbDataReader reader = qrycmd.ExecuteReader())
{
try
{
while (reader.Read())
{
answ = reader["num"].ToString();
}
// chiudo reader!
reader.Close();
}
catch (Exception exc)
{
lg.Error(exc, string.Format("Errore in recupero conteggio eventi: {0}{1}", Environment.NewLine, exc));
answ = "ERR";
mMessage.Code = "ERR-DB-COUNT";
mMessage.Value = "Errore conteggio dati da DB";
}
}
}
return answ;
}
}
/// <summary>
/// Recupero dati principali (DB)
/// </summary>
public virtual void getMainData()
{
// recupero da DB ultimo valore NON inviato e se c'è lo invio...
string valore = "-999";
bool valFound = false;
if (connectionOk)
{
// recupero conteggio degli eventi... se in debug rileggo da inizio altrimenti SOLO ultimo valore...
#if DEBUG
Query = string.Format("SELECT id, value FROM tracelog WHERE resource = 'E' AND device=1 AND id > {0} ORDER BY id LIMIT 1", lastId);
#else
Query = string.Format("SELECT id, value FROM tracelog WHERE resource = 'E' AND device=1 AND id > {0} ORDER BY id DESC LIMIT 1", lastId);
#endif
DbCommand qrycmd = DbConn.CreateCommand();
qrycmd.CommandText = Query;
qrycmd.Connection = DbConn;
using (DbDataReader reader = qrycmd.ExecuteReader())
{
try
{
while (reader.Read())
{
valore = reader["value"].ToString();
lastId = Convert.ToUInt32(reader["id"].ToString());
valFound = true;
}
// chiudo reader!
reader.Close();
}
catch (Exception exc)
{
lg.Error(exc, string.Format("Errore in recupero dati da DB: {0}{1}", Environment.NewLine, exc));
valore = "-99";
mMessage.Code = "ERR-DB-READ";
mMessage.Value = "Errore recupero dati da DB";
}
}
if (valFound)
{
// salvo in sample SE trovato...!
vettDbData[0].mSampleData.Value = valore;
// salvo lastID...
updateValUInt(0, lastId, "LAST_ID");
}
}
}
/// <summary>
/// dati "lenti" relativi al device
/// </summary>
public virtual void getSlowChangingData()
{
// dati da PC
mClock.Value = string.Format("{0:yyyy-MM-dd} {0:HH:mm:ss}", DateTime.Now);
}
#endregion
#region layer persistenza dati
/// <summary>
/// Dizionario di persistenza per i valori da salvare da/su file
/// </summary>
public Dictionary<string, string> persistenceLayer;
/// <summary>
/// recupera valore salvato in persistence layer (se non c'è crea...)
/// </summary>
/// <param name="keyVal"></param>
/// <returns></returns>
private string getStoredVal(string keyVal)
{
string value = "";
try
{
if (!persistenceLayer.TryGetValue(keyVal, out value))
{
persistenceLayer.Add(keyVal, "0");
}
}
catch
{ }
return value;
}
/// <summary>
/// recupera valore salvato in persistence layer (se non c'è crea...) come UINT
/// </summary>
/// <param name="keyVal"></param>
/// <returns></returns>
private uint getStoredValUInt(string keyVal)
{
uint answ = 0;
try
{
answ = Convert.ToUInt32(getStoredVal(keyVal));
}
catch
{ }
return answ;
}
/// <summary>
/// recupera valore salvato in persistence layer (se non c'è crea...) come INT
/// </summary>
/// <param name="keyVal"></param>
/// <returns></returns>
private long getStoredValLong(string keyVal)
{
long answ = 0;
try
{
answ = Convert.ToInt64(getStoredVal(keyVal));
}
catch
{ }
return answ;
}
/// <summary>
/// recupera valore salvato in persistence layer (se non c'è crea...) come double
/// </summary>
/// <param name="keyVal"></param>
/// <returns></returns>
private double getStoredValDouble(string keyVal)
{
double answ = 0;
try
{
answ = Convert.ToDouble(getStoredVal(keyVal));
}
catch
{ }
return answ;
}
/// <summary>
/// Aggiorna un valore del dizionario in SOSTITUZIONE
/// </summary>
/// <param name="i"></param>
/// <param name="newVal"></param>
/// <param name="searchString"></param>
/// <returns>Nuovo valore incrementato</returns>
private void updateValString(int i, string newVal, string searchString)
{
// stringa da cercare..
string keyVal = string.Format(searchString, i + 1);
// salvo in ram!
persistenceLayer[keyVal] = newVal;
}
/// <summary>
/// Aggiorna un valore del dizionario in SOSTITUZIONE e lo restituisce
/// </summary>
/// <param name="i"></param>
/// <param name="newVal"></param>
/// <param name="searchString"></param>
/// <returns>Nuovo valore incrementato</returns>
private void updateValUInt(int i, uint newVal, string searchString)
{
// stringa da cercare..
string keyVal = string.Format(searchString, i + 1);
// salvo in ram!
persistenceLayer[keyVal] = newVal.ToString();
}
#endregion
}
}