Files
2022-06-14 10:51:02 +02:00

631 lines
23 KiB
C#

using Microsoft.Extensions.Configuration;
using MP.MONO.Core;
using MP.MONO.Core.CONF;
using Newtonsoft.Json;
using NLog;
using System.Net;
using System.Net.NetworkInformation;
using static MP.MONO.Core.CONF.OpcUaParamConf;
using static MP.MONO.Core.Enums;
namespace MP.MONO.ADAPTER
{
public class IobGeneric
{
#region Public Fields
/// <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>
/// dataOra ultimo log periodico...
/// </summary>
public DateTime lastPeriodicLog;
/// <summary>
/// dataOra ultimo PING inviato verso il PLC...
/// </summary>
public DateTime lastPING = DateTime.Now.AddHours(-1);
/// <summary>
/// Struttura memoria PLC x lettura/scrittura da JSON file
/// </summary>
public plcMemMap memMap;
#endregion Public Fields
#region Public Constructors
/// <summary>
/// Avvia generico IOB
/// </summary>
/// <param name="confPath"></param>
/// <param name="config"></param>
public IobGeneric(string confPath, IConfigurationRoot? config)
{
lg = LogManager.GetCurrentClassLogger();
connectionOk = false;
configPath = confPath;
confMan = config;
if (confMan != null)
{
var selection = confMan.GetSection("Endpoint");
if (selection.Exists())
{
// recupero dati endpoint
cIobConf = selection.Get<EndpointData>();
}
}
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Salva verifica stato connessione OK
/// </summary>
/// <returns></returns>
public virtual bool connectionOk
{
get
{
return _connOk;
}
set
{
_connOk = value;
}
}
/// <summary>
/// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN)
/// </summary>
public bool verboseLog
{
get
{
bool answ = false;
int logEvery = confMan.GetValue<int>("logEvery");
if (logEvery < 1)
{
logEvery = 10;
}
answ = confMan.GetValue<bool>("verbose") && (nReadIN % logEvery == 0);
return answ;
}
}
#endregion Public Properties
#region Public Methods
/// <summary>
/// Esecuzione dei task richiesti e pulizia coda richieste eseguite
/// </summary>
/// <param name="task2exe"></param>
public virtual Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
Dictionary<string, string> taskDone = new Dictionary<string, string>();
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setArt:
case taskType.setComm:
case taskType.setProg:
case taskType.setPzComm:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, item.Value));
break;
case taskType.forceResetPzCount:
// reset contapezzi inizio setup
taskOk = resetcontapezziPLC();
taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.startSetup:
// reset contapezzi inizio setup
taskOk = resetcontapezziPLC();
taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.stopSetup:
// reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO
if (confMan.GetValue<bool>("ENABLE_PZ_RESET_stopSetup"))
{
taskOk = resetcontapezziPLC();
}
taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.setParameter:
// richiedo da URL i parametri WRITE da popolare
lgInfo("Chiamata setParameter --> processMemWriteRequests");
taskVal = processMemWriteRequests();
// se restituiscce "" faccio altra prova...
if (string.IsNullOrEmpty(taskVal))
{
// i parametri me li aspetto come stringa composta paramName|paramvalue
if (item.Value.Contains("|"))
{
string[] paramsJob = item.Value.Split('|');
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
}
else
{
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
}
}
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
return taskDone;
}
/// <summary>
/// Metodo generico di reset contapezzi...
/// </summary>
/// <returns></returns>
public virtual bool resetcontapezziPLC()
{
return false;
}
/// <summary>
/// Salva valori indicati in prod data
/// </summary>
/// <param name="item">Item KVP di cui salvare i dati in currProdData come chiave/valore</param>
/// <returns></returns>
public void saveProdData(KeyValuePair<string, string> item)
{
// imposto i valori...
if (currProdData.ContainsKey(item.Key))
{
currProdData[item.Key] = item.Value;
}
else
{
currProdData.Add(item.Key, item.Value);
}
}
/// <summary>
/// Metodo base connessione...
/// </summary>
public virtual void tryConnect()
{
dtAvvioAdp = DateTime.Now;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// wrapper di log
/// </summary>
protected static Logger lg;
protected bool _connOk = false;
/// <summary>
/// Dizionario valori impostati x produzione
/// </summary>
protected Dictionary<string, string> currProdData = new Dictionary<string, string>();
/// <summary>
/// Dizionario ultimi valori (double) delle TSVC
/// </summary>
protected Dictionary<string, double> LastTSVC = new Dictionary<string, double>();
/// <summary>
/// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...)
/// </summary>
protected Dictionary<string, VCData> TSVC_Data = new Dictionary<string, VCData>();
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Numero letture IN da avvio
/// </summary>
protected int nReadIN { get; set; }
/// <summary>
/// COnfiguraizone Endpoint corrente
/// </summary>
private EndpointData cIobConf { get; set; } = new EndpointData();
/// <summary>
/// test ping all'indirizzo PLC/CNC impostato nei parametri
/// </summary>
/// <returns></returns>
protected IPStatus testPingMachine
{
get
{
IPStatus answ = IPStatus.Unknown;
// se disabilitato salto...
if (pingDisabled)
{
answ = IPStatus.Success;
}
else
{
IPAddress address;
PingReply reply;
using (Ping pingSender = new Ping())
{
address = IPAddress.Loopback;
int pingMsTimeout = cIobConf.PingMsTimeout;
IPAddress.TryParse(cIobConf.IpAddress, out address);
try
{
// se != null --> uso address...
if (address != null)
{
reply = pingSender.Send(address, pingMsTimeout);
}
else
{
reply = pingSender.Send(cIobConf.IpAddress, pingMsTimeout);
}
}
catch
{
reply = pingSender.Send(IPAddress.Loopback, pingMsTimeout);
}
answ = reply.Status;
}
}
return answ;
}
}
/// <summary>
/// indica se ping disabilitato da optPar
/// </summary>
public bool pingDisabled
{
get
{
bool answ = false;
bool.TryParse(confMan.GetValue<string>("NO_PING"), out answ);
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Stringa raw dei parametri da scrivere...
/// </summary>
/// <returns></returns>
protected string getParams2write()
{
string answ = "";
// recuperare da una apposita area REDIS...
#if false
string url2call = $"{urlGetParams2Write}";
if (verboseLog)
{
lgInfo("chiamata URL " + url2call);
}
answ = utils.callUrlNow(url2call);
// se vuoto faccio seconda prova...
if (string.IsNullOrEmpty(answ))
{
answ = utils.callUrlNow(url2call);
}
#endif
return answ;
}
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgDebug(string message)
{
lg.Debug(message);
}
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgDebug(string message, params object[] args)
{
lg.Debug(message, args);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message)
{
lg.Error(message);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgError(string message, params object[] args)
{
lg.Error(message, args);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgError(Exception exception, string message, params object[] args)
{
lg.Error(exception, message, args);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgFatal(string message)
{
lg.Fatal(message);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgFatal(string message, params object[] args)
{
lg.Fatal(message, args);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgFatal(Exception exception, string message, params object[] args)
{
lg.Fatal(exception, message, args);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message)
{
lg.Info(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgInfo(string message, params object[] args)
{
lg.Info(message, args);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgTrace(string message)
{
lg.Trace(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgTrace(string message, params object[] args)
{
lg.Trace(message, args);
}
/// <summary>
/// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
/// </summary>
/// <param name="updatedPar"></param>
protected virtual void plcWriteParams(ref List<objItem> updatedPar)
{
// non faccio nulla di base...
}
/// <summary>
/// Processa le richieste di scrittura memoria
/// </summary>
/// <returns></returns>
protected string processMemWriteRequests()
{
string answ = "";
// li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC)
List<objItem> writeList = new List<objItem>();
List<objItem> updatedPar = new List<objItem>();
// recupero elenco delle cose da fare
string resp = getParams2write();
if (!string.IsNullOrEmpty(resp))
{
try
{
writeList = JsonConvert.DeserializeObject<List<objItem>>(resp);
// se ho da fare chiamo esecuzione..
if (writeList.Count > 0)
{
foreach (var item in writeList)
{
// scrivo in memoria
if (memMap.mMapWrite.ContainsKey(item.uid))
{
memMap.mMapWrite[item.uid].value = item.reqValue;
// accodo in stringa taskVal...
answ += $" | Parameter {item.uid} --> {item.reqValue}";
// sistemo valori
item.value = item.reqValue;
lgInfo($"Richiesta update parametro {item.uid} | actVal = {item.value} | reqVal = {item.reqValue}");
item.reqValue = "";
// salvo in lista da ritrasmettere
updatedPar.Add(item);
}
else
{
answ += $" | Error: parameter {item.uid} not found";
}
}
// richiamo scrittura parametri su PLC
plcWriteParams(ref updatedPar);
// invio su cloud parametri!
string rawData = JsonConvert.SerializeObject(updatedPar);
lgInfo("Notifica a server scrittura parametri");
#if false
utils.callUrl($"{urlUpdateWriteParams}", rawData);
#endif
}
}
catch (Exception exc)
{
lgError($"Eccezione in processMemWriteRequests:{Environment.NewLine}{exc}");
}
}
else
{
lgError("Non è stata ricevuta risposta x task da eseguire");
}
return answ;
}
/// <summary>
/// setup parametri da file di conf
/// </summary>
protected void setupMemMap()
{
lgInfo($"setupMemMap | trovati {memMap.mMapRead.Count} parametri Read (TSVC)");
lgInfo($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write");
if (confMan.GetValue<bool>("verbose"))
{
string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented);
lgDebug($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}");
}
// se ho variabili read --> genero dati TSVC...
if (memMap.mMapRead.Count > 0)
{
TSVC_Data.Clear();
LastTSVC.Clear();
VCData currConf;
int periodo = 0;
VC_func funz = VC_func.POINT;
// accodo nella conf...
foreach (var item in memMap.mMapRead)
{
funz = item.Value.func;
periodo = item.Value.period;
currConf = new VCData()
{
Funzione = funz,
Period = periodo,
DTStart = DateTime.Now.AddHours(-1),
dataArray = new List<double>()
};
TSVC_Data.Add(item.Key, currConf);
}
// documento...
foreach (var item in TSVC_Data)
{
lgTrace($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}");
// salvo i valori PREC...
LastTSVC.Add(item.Key, 0);
}
}
}
#endregion Protected Methods
#region Private Properties
internal string configPath { get; set; } = "";
internal IConfigurationRoot? confMan { get; set; } = null!;
#endregion Private Properties
}
}