Documentazione sistema di acquisizione

Documentazione sistema di acquisizione
Il sistema permette il reperimento dei dati ambientali dalle varie centraline connesse alla rete
della serra. Esso è composto da un software c# in esecuzione sul server del sistema (in cui è in
esecuzione anche il motore di database). La scelta di utilizzare un software C# per trasferire
dati nel server è stata obbligata dall’impossibilità di alcune centraline di salvare autonomamente
i dati sul database; in particolare per comunicare con il PLC si deve sfruttare il server http che
ha al suo interno.
In questo modo possiamo gestire il sistema di salvataggio dei dati in modo centralizzato (un
unico orologio, intervallo slegato dalla centralina).
Le centraline devono mettere a disposizione una pagina web aggiornata dinamicamente con i
valori attuali, secondo uno schema e dei tag predefiniti.
Esempio di pagina web fornita dalle centraline
__________________________________________________________________________
<html>
<head>
<meta charset="utf-8">
<title>Pascal-Garibaldi</title>
</head>
<body>
temperatura =<tem>28</tem><br/>
umidità =<umi>76</umi><br/>
luminosità =<lum>1234</lum><br/>
</body>
</html>
___________________________________________________________________________
Struttura dei dati nel software
Il software, prima di prelevare le misurazioni dalle
centraline, legge i dati necessari dal database per
l’interazione con quest’ultime(ad esempio URL e
tag) e le salva in una struttura temporanea interna
al programma.
Tramite queste informazioni scarica quindi la
pagina web da tutte le centraline ad intervalli
regolari, preleva le misurazioni attraverso i tag e le
inserisce nel database. I tag sono volutamente
case unsesitive, cioè non sensibili alle maiuscole.
Carico le impostazioni dal file di
configurazione
Aspetto l’ora di lettura
Carico dal database le informazioni delle
centraline e dei punti di misura
Leggo i valori dalle pagine web di ogni
centralina e li salvo temporaneamente
Inserisco nel database i valori letti
precedentemente
App.config
Le impostazioni del software si possono modificare dal file di configurazione
([nome_exe].exe.config) presente nella cartella dell’eseguibile.
In questo file possono essere modificate e/o aggiunte stringhe di connessione al Database oltre
alle configurazioni prettamente del software come l’intervallo di campionamento, il tempo di
timeout per scaricare la pagina web, ed il nome del file di log.
Esempio del file di config
__________________________________________________________________________
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<connectionStrings>
<add name="ConnString" connectionString="Server=172.16.39.160;
Database=serra; User Id=sa; Password=sql2014-15" />
</connectionStrings>
<appSettings>
<!-- Intervallo di campionamento in secondi -->
<add key="intervallo" value="5" />
<add key="log_filename"
value="C:\Serra\Logs\log-serra-scansione-centraline.txt" />
<add key="time-out-web-request" value="10" />
</appSettings>
</configuration>
__________________________________________________________________________
Struttura del file di log
[Data] - [Centralina o Punto Misura o Database]: Id - Errore: [Messaggio errore]
Esempio di un log
24/04/2015 15:06:50 - Centralina: 1 - Errore: Errore del server remoto: (404) Non trovato.
24/04/2015 15:09:55 - Centralina: 2 - Errore: Timeout dell'operazione.
27/04/2015 12:33:15 - Centralina: 3 - Errore: Errore del server remoto: (407) Richiesta
autenticazione proxy.
Comandi disponibili:
Esc - Chiude il programma correttamente
I
- Stampa a video le informazioni temporanee di Centraline e PuntiMisura
Listato del programma
/*
* ITT Pascal - Cesena
* Garden of things
* Sistema di acquisizione e consultazione dati ambientali della serra
*
* Programma:
Scansione centraline
* Versione:
1.0.0.6
* Ultima modifica: 27/04/2015
*
* Autori:
Edoardo Barbieri, Lorenzo Mondani, Emanuele Pancisi
*/
/*
* ITT Pascal - Cesena
* Garden of things
* Sistema di acquisizione e consultazione dati ambientali della serra
*
* Programma:
Scansione centraline
* Versione:
1.0.0.8
* Ultima modifica: 08/05/2015
*
* Autori:
Edoardo Barbieri, Lorenzo Mondani, Emanuele Pancisi
*/
using
using
using
using
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
System.Net;
System.Data.SqlClient;
System.Data;
System.Configuration;
System.Threading;
System.IO;
System.Runtime.InteropServices;
namespace client_http_msqls
{
class Program
{
static string vn = "1.0.0.8"; //aggiunto log_writer.Flush();
static string versione = vn + " (Ultima modifica: 08/05/2015)";
static int intervallo_secondi =
int.Parse(ConfigurationManager.AppSettings["intervallo"]);
static int time_out = int.Parse(ConfigurationManager.AppSettings["time-out-webrequest"]);
//static string indirizzo_web_server =
ConfigurationManager.AppSettings["indirizzo_web_server"];
static string conn_database =
ConfigurationManager.ConnectionStrings["ConnString"].ToString();
static List<Centralina> Centraline = new List<Centralina>();
static StreamWriter log_writer = new
StreamWriter(ConfigurationManager.AppSettings["log_filename"].ToString(), true);
static SqlConnection sql_connetion;
#region Exit Handler
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig)
{
if(sig != CtrlType.CTRL_C_EVENT)
log_writer.Close();
return true;
}
#endregion
static void Main(string[] args)
{
//Evento che mi gestisce la chiusura
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
//safe = senza eccezioni
Console.Title = "Scansione centraline
" + vn;
//stampo le variabili dell'app.config e la versione
Console.WriteLine("Versione: " +versione);
Console.WriteLine("Intervallo: " + intervallo_secondi+ " secondi");
Console.WriteLine(conn_database);
Console.WriteLine();
//Ciclo principale
while (true)
{
log_writer.Flush();
if (!AspettaIntervallo())
{
//è stato premuto escape
Console.WriteLine("Chiusura programma...");
Thread.Sleep(1000);
break;//Esco dal ciclo se viene premuto Escape
}
DateTime orario_lettura = new DateTime(DateTime.Now.Year,
DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute,
DateTime.Now.Second);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Nuovo tentativo di lettura: " +
orario_lettura.ToLongTimeString());
Console.ResetColor();
/*Apro la connessione con il server SQL*/
if (!ConnettiDB(orario_lettura))
{
continue;
}//safe
/*Carico i dati dei punti misura e delle centraline*/
if (!CaricaInformazioni(orario_lettura))
{
continue;
}//safe
foreach (Centralina c in Centraline) //Per ogni centralina scarico la
pagina web e faccio le insert nel database
{
try
{
//webclient per il download della pagina html
MyWebClient client = new MyWebClient();
MyWebClient.SecTimeOut = time_out;
string contenuto = client.DownloadString(c.Indirizzo_web);
contenuto = contenuto.ToLower();
/* Esempio di contenuto:
<tem>28.8</temp></br>
<umi>30</umi>
*/
foreach(PuntoMisura pm in c.PuntiMisura)
{
InserisciLettura(contenuto, pm, orario_lettura); //safe
}
}
catch(Exception ex) //In caso di eccezione la salvo nel file di log
{
log_writer.WriteLine(orario_lettura + " - Centralina: " + c.Id +
" - Errore: " + ex.Message);
log_writer.Flush();
Console.WriteLine(orario_lettura + " - Centralina: " + c.Id + " Errore: " + ex.Message);
}
}
//Chiudo la connessione con il db
sql_connetion.Close();
//aspetto almeno un secondo per evitare di effettuare un'altra lettura
nello stesso orario
Thread.Sleep(1000);
Console.WriteLine();
}
//Chiudo lo scrittore del file di log
log_writer.Close();
}
/// <summary>
/// Aspetto l'intervallo di tempo prestabilito
/// </summary>
/// <returns>Ritorna true se è stato premuto il tasto Escape</returns>
static bool AspettaIntervallo()//safe
{
//Aspetto fino alla data tonda dell'intervallo richiesto
while (((int)DateTime.Now.TimeOfDay.TotalSeconds) % intervallo_secondi != 0)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo k = Console.ReadKey();
Console.CursorLeft = 0;
if (k.Key == ConsoleKey.Escape)
return false;
else
Comandi(k);
}
Thread.Sleep(10);
}
//Torna true se bisogna continuare, false se si è spinto exit
return true;
}
/// <summary>
/// Eseguo i comandi [Ricarica = R, ]
/// </summary>
/// <param name="k">Tasto premuto</param>
static void Comandi(ConsoleKeyInfo k)//safe
{
if (k.Key == ConsoleKey.I)
{
StampaInformazioniCentraline();
}
}
/// <summary>
/// Stampa le informazioni delle varie centraline caricate
/// </summary>
static void StampaInformazioniCentraline()//safe
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Centraline:");
foreach (Centralina c in Centraline)
{
Console.WriteLine("\n" + c + "\n");
Console.WriteLine("\tPuntiMisura:\n");
foreach (PuntoMisura p in c.PuntiMisura)
Console.WriteLine("\t" + p + "\n");
}
Console.ResetColor();
}
/// <summary>
/// Mi connetto al database
/// </summary>
static bool ConnettiDB(DateTime orario_lettura)//safe
{
//Console.WriteLine("Connessione al database...");
try
{
sql_connetion = new SqlConnection(conn_database);
sql_connetion.Open();
return true;
}
catch(Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(orario_lettura + " - Database - Errore: " +
ex.Message);
log_writer.WriteLine(orario_lettura + " - Database - Errore: " +
ex.Message);
log_writer.Flush();
Thread.Sleep(1000);
Console.ResetColor();
sql_connetion = null;
return false;
}
}
/// <summary>
/// Carico le informazioni necessarie
/// </summary>
static bool CaricaInformazioni(DateTime orario_lettura)//safe
{
/* Carico le centraline */
SqlDataReader reader = null;
SqlCommand cmd = null;
try
{
Centraline.Clear();
cmd = new SqlCommand("SELECT * FROM View_Configurazione");
cmd.Connection = sql_connetion;
reader = cmd.ExecuteReader();
while (reader.Read())
{
int id_cen = int.Parse(reader["IdCentralina"].ToString());
int find = Centraline.FindIndex(c => c.Id == id_cen);
if(find==-1)
{
Centralina c = new
Centralina(int.Parse(reader["IdCentralina"].ToString()), reader["Centralina"].ToString(),
"", reader["Url"].ToString(), reader["TagCentralina"].ToString());
Centraline.Add(c);
}
find = Centraline.FindIndex(c => c.Id == id_cen);
PuntoMisura pm = new
PuntoMisura(int.Parse(reader["IdPunto"].ToString()), Centraline[find].Id,
reader["Sensore"].ToString(),
int.Parse(reader["IdGrandezza"].ToString()),
reader["Posizione"].ToString(),
reader["TagPunto"].ToString().ToLower(),
reader["Grandezza"].ToString(),
reader["UnitaMisura"].ToString(), ""/*reader["Formato"].ToString()*/);
Centraline[find].PuntiMisura.Add(pm);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(orario_lettura + " - Database - Errore: " +
ex.Message);
log_writer.WriteLine(orario_lettura + " - Database - Errore: " +
ex.Message);
log_writer.Flush();
Thread.Sleep(1000);
sql_connetion.Close();
return false;
}
finally
{
if (reader != null)
reader.Close();
if (cmd != null)
cmd.Dispose();
}
}
/// <summary>
/// Inserisce i dati nel database partendo dal contenuto della pagina web
/// </summary>
/// <param name="contenuto">Contenuto della pagina scaricata dalla
centralina</param>
/// <param name="pm">Verranno estrapolati ed inseriti i dati di questo punto di
misura (dal contenuto)</param>
/// <param name="now">Data di inserimento</param>
/// <returns>Ritorna true se l'operazione va a buon fine</returns>
static bool InserisciLettura(string contenuto, PuntoMisura pm, DateTime
now)//safe
{
try
{
string tag = "<" + pm.Tag + ">";
int i = contenuto.IndexOf(tag);
i += tag.Length;
tag = "</" + pm.Tag + ">";
int f = contenuto.IndexOf(tag);
string val = contenuto.Substring(i, f - i);
val = val.Replace("\n", "").Replace("\t", "").Replace(" ", "");
SqlCommand cmd = new SqlCommand("INSERT INTO
Rilevazione(Tempo,Valore,PuntoMisura) VALUES(@tempo,@valore,@pm)");
cmd.Parameters.Add(new SqlParameter("tempo", now));
cmd.Parameters.Add(new SqlParameter("valore", val));
cmd.Parameters.Add(new SqlParameter("pm", pm.Id));
cmd.Connection = sql_connetion;
cmd.ExecuteNonQuery();
correttamente
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Rilevazione sul punto misura " + pm.Id + " inserita
[" + pm.Tag + "=" + val + "]");
Console.ResetColor();
}
catch (Exception ex)
{
log_writer.WriteLine(now + " - PuntoMisura: " + pm.Id + " - Errore: " +
ex.Message);
log_writer.Flush();
Console.WriteLine(now + " - PuntoMisura: " + pm.Id + " - Errore: " +
ex.Message);
return false;
}
return true;
}
}
}