Files
2020-05-28 10:11:05 +02:00

492 lines
15 KiB
C#

using Newtonsoft.Json;
using NLog;
using S7.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows.Forms;
namespace MHT_Siemens
{
public partial class SiemensProxy : Form
{
#region oggetti di base
/// <summary>
/// configurazioen principale proxy
/// </summary>
protected dataProxy currDataProxy;
/// <summary>
/// Oggetto PLC da ri-utilizzare...
/// </summary>
protected Plc currPLC;
/// <summary>
/// parametri di connessione
/// </summary>
protected connParam parametri;
/// <summary>
/// oggetto logging
/// </summary>
public static Logger lg;
/// <summary>
/// oggetto uiTimer x gestione refresh UI
/// </summary>
protected Timer uiTimer = new Timer();
/// <summary>
/// oggetto uiTimer x sampling file testuale
/// </summary>
protected Timer sampleTimer = new Timer();
/// <summary>
/// oggetto uiTimer x verifiche
/// </summary>
protected Timer checkTimer = new Timer();
/// <summary>
/// file delle conf attivo
/// </summary>
protected string setupFile = "setup.json";
/// <summary>
/// Ultimi valori registrati da file
/// </summary>
protected Dictionary<string, string> prevValues;
/// <summary>
/// valori correntemente letti dal file
/// </summary>
protected Dictionary<string, string> currValues;
#endregion
#region inizializazione
public SiemensProxy()
{
InitializeComponent();
myInit();
}
private void myInit()
{
lg = LogManager.GetCurrentClassLogger();
tsslApp.Text = $"{utils.CRS("appName")}";
prevValues = new Dictionary<string, string>();
currValues = new Dictionary<string, string>();
loadMemConf();
loadPlc();
startUiTimer();
startSampleTimer();
startCheckTimer();
}
private void startUiTimer()
{
uiTimer.Interval = 20;
uiTimer.Tick += UiTimer_Tick;
uiTimer.Start();
}
private void startSampleTimer()
{
int sampleTimerMs = utils.CRI("sampleTimerMs");
sampleTimerMs = sampleTimerMs < 100 ? 100 : sampleTimerMs;
sampleTimer.Interval = sampleTimerMs;
sampleTimer.Tick += SampleTimer_Tick;
sampleTimer.Start();
}
private void startCheckTimer()
{
// ogni 5 minuti watchdog
checkTimer.Interval = sampleTimer.Interval * 60;
checkTimer.Tick += CheckTimer_Tick;
checkTimer.Start();
}
/// <summary>
/// init PLC
/// </summary>
private void loadPlc()
{
lgInfo("Refreshing connection...");
parametri = currDataProxy.confPLC;
// ora tento avvio PLC... SE PING OK...
if (testPing() == IPStatus.Success)
{
try
{
lgInfo($"PLC parameters: CPU {parametri.tipoCpu} | IP: {parametri.ipAdrr} | R/S: {parametri.rack}/{parametri.slot}");
currPLC = new Plc(parametri.tipoCpu, parametri.ipAdrr, parametri.rack, parametri.slot);
currPLC.Open();
if (currPLC.IsConnected)
{
lgInfo("PLC Connected!");
}
else
{
lgInfo("Connection error!");
}
}
catch (Exception exc)
{
lgError(exc, "Errore in INIT PLC");
}
}
}
/// <summary>
/// Init conf memoria
/// </summary>
protected void loadMemConf()
{
lgInfo("Starting loadMemConf");
if (File.Exists(setupFile))
{
lgInfo("Setup File found!");
try
{
StreamReader reader = new StreamReader(setupFile);
string jsonData = reader.ReadToEnd();
if (!string.IsNullOrEmpty(jsonData))
{
currDataProxy = JsonConvert.DeserializeObject<dataProxy>(jsonData);
lgInfo("Completed data deserialization");
}
reader.Close();
}
catch
{
currDataProxy = null;
}
}
// se non esistesse creo un nuovo file default
if (currDataProxy == null)
{
lgInfo($"File NOT found: {setupFile} - creating new");
dataConf newParam = new dataConf()
{
Column = "Valore assoluto",
Index = 8,
MemConf = "DB701.DBD142",
DataType = "real"
};
Dictionary<string, dataConf> paramList = new Dictionary<string, dataConf>();
paramList.Add(newParam.MemConf, newParam);
// creo nuovo obj...
currDataProxy = new dataProxy()
{
csvFilePath = @"c:\zz\prova1.csv",
csvHasHeader = true,
csvSeparator = ';',
confPLC = new connParam()
{
ipAdrr = "192.168.0.102",
tipoCpu = CpuType.S71500,
rack = 0,
slot = 1
},
parametersList = paramList
};
// salvo!
string json = JsonConvert.SerializeObject(currDataProxy);
//write string to file
File.WriteAllText(setupFile, json);
lgInfo($"Setup File saved: {setupFile}");
}
// adesso valorizzo tutti i parametri
txtCsvPath.Text = currDataProxy.csvFilePath;
txtIP.Text = currDataProxy.confPLC.ipAdrr;
txtCpuType.Text = $"{currDataProxy.confPLC.tipoCpu}";
txtRack.Text = $"{currDataProxy.confPLC.rack}";
txtSlot.Text = $"{currDataProxy.confPLC.slot}";
}
#endregion
#region metodi ricorrenti
private void UiTimer_Tick(object sender, EventArgs e)
{
toolStripProgressBar1.ProgressBar.Value++;
if (toolStripProgressBar1.ProgressBar.Value >= toolStripProgressBar1.ProgressBar.Maximum)
{
toolStripProgressBar1.ProgressBar.Value = 0;
}
}
private void CheckTimer_Tick(object sender, EventArgs e)
{
// loggo!
lgInfo("Program Alive control...");
//verifico PLC
if (currPLC.IsConnected)
{
lgInfo("PLC Connected!");
}
else
{
lgInfo("Connection error!");
}
// loggo che COMUNQUE forzo scrittura su PLC!
lgInfo("Forced PLC write");
// invio a PLC
saveToPLC();
}
private void SampleTimer_Tick(object sender, EventArgs e)
{
// rileggo il file
reloadFromFile();
// verifico valore confrontando con i precedenti...
if (dataChanged())
{
// loggo!
lgInfo("Data Change on File");
// invio a PLC
saveToPLC();
}
}
#endregion
#region helper methods
/// <summary>
/// salva i dati sul PLC
/// </summary>
private void saveToPLC()
{
// invio TUTTI i dati al PLC secondo configurazione...
if (testCncConn())
{
// decodifico memoria...
memAddress memoria = null;
double valore = -999;
foreach (var item in currValues)
{
memoria = new memAddress(item.Key);
double.TryParse(item.Value, out valore);
// hard coded REAL!!!
byte[] DB_Byte = new byte[4];
S7.Net.Types.Double.ToByteArray(valore).CopyTo(DB_Byte, 0);
currPLC.WriteBytes(DataType.DataBlock, memoria.DbNum, memoria.indiceMem, DB_Byte);
// loggo invio al PLC!
lgInfo($"Value sent to PLC: {item.Key} | {valore}");
}
}
}
/// <summary>
/// Effettua comparazioen dati vecchi/nuovi
/// </summary>
/// <returns></returns>
private bool dataChanged()
{
bool answ = false;
// se i 2 vettori sono diversi
if (currValues.Count != prevValues.Count)
{
resetPrevVal();
return true;
}
// verifico ogni singolo valore...
foreach (var item in currValues)
{
// cerco...
if (prevValues.ContainsKey(item.Key))
{
// verifico se diversi --> trovato cambio!
answ = currValues[item.Key] != prevValues[item.Key];
}
else
{
answ = true;
}
// se ho cambiamenti --> esco!
if (answ)
{
// salvo ed esco subito
resetPrevVal();
return true;
}
}
return answ;
}
/// <summary>
/// Refresh oggetto valori precedenti
/// </summary>
private void resetPrevVal()
{
// salvo ed esco subito
prevValues = new Dictionary<string, string>();
foreach (var item in currValues)
{
prevValues.Add(item.Key, item.Value);
}
}
/// <summary>
/// Effettua rilettura da file
/// </summary>
protected void reloadFromFile()
{
if (File.Exists(currDataProxy.csvFilePath))
{
// leggo...
using (StreamReader sr = new StreamReader(currDataProxy.csvFilePath))
{
string currentLine;
//se ha header salto la prima riga...
if (currDataProxy.csvHasHeader)
{
if ((currentLine = sr.ReadLine()) == null)
{
// vuoto: loggo ed esco!
lgError("Errore intestazione vuota!");
return;
}
}
// ora leggo la riga successiva... SE !=null
if ((currentLine = sr.ReadLine()) != null)
{
// splitto riga e cerco valore...
var valori = currentLine.Split(currDataProxy.csvSeparator);
// cerco parametri!
try
{
foreach (var item in currDataProxy.parametersList)
{
// cerco!
if (valori.Count() >= item.Value.Index)
{
upsertValue(item.Key, valori[item.Value.Index]);
}
}
}
catch
{ }
}
else
{
// vuoto: loggo ed esco!
lgError("Errore riga vuota!");
return;
}
}
}
else
{
lgError($"Errore! file non trovato: {currDataProxy.csvFilePath}");
}
}
protected void upsertValue(string key, string value)
{
// cerco se ci sia... aggiorno!
if (currValues.ContainsKey(key))
{
currValues[key] = value;
}
// altrimenti aggiungo
else
{
currValues.Add(key, value);
}
}
/// <summary>
/// test ping all'indirizzo impostato nei parametri
/// </summary>
/// <returns></returns>
private IPStatus testPing()
{
IPStatus answ = IPStatus.Unknown; ;
IPAddress address;
PingReply reply;
Ping pingSender = new Ping();
address = IPAddress.Loopback;
IPAddress.TryParse(parametri.ipAdrr, out address);
reply = pingSender.Send(address, 100);
answ = reply.Status;
return answ;
}
/// <summary>
/// Test connessione CNC
/// </summary>
/// <returns></returns>
private bool testCncConn()
{
bool answ = false;
IPStatus pingStatus = testPing();
// se passa il ping faccio il resto...
if (pingStatus != IPStatus.Success)
{
lgError("Errore ping");
}
else
{
if (!currPLC.IsConnected)
{
currPLC.Open();
}
if (!currPLC.IsConnected)
{
lgError("Errore connessione");
}
else
{
answ = true;
}
}
return answ;
}
private void appenRTLog(string contenuto)
{
// se troppe righe trimmo...
string fullLog = limitLine2show($"{contenuto}{Environment.NewLine}{txtLog.Text}");
// update
txtLog.Text = fullLog;
}
/// <summary>
/// Effettua un trim della stringa al numero max di linee da mostrare a video
/// </summary>
/// <param name="newString"></param>
/// <returns></returns>
public string limitLine2show(string newString)
{
if (!string.IsNullOrEmpty(newString))
{
// se num righe superiore a limite trimmo...
if (newString.Split('\n').Length > 50)
{
//int idx = newString.LastIndexOf('\r');
int idx = newString.LastIndexOf(Environment.NewLine);
newString = newString.Substring(0, idx);
}
}
return newString;
}
protected void lgInfo(string contenuto)
{
lg.Info(contenuto);
appenRTLog(contenuto);
}
protected void lgError(string contenuto)
{
lg.Error(contenuto);
appenRTLog(contenuto);
}
protected void lgError(Exception exc, string contenuto)
{
lg.Error(exc, contenuto);
appenRTLog(contenuto);
}
#endregion
}
}