Files
2025-08-04 09:58:06 +02:00

4777 lines
183 KiB
C#

using IOB_UT;
using MapoSDK;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace IOB_WIN
{
public class IobGeneric
{
#region Protected Fields
/// <summary>
/// wrapper di log
/// </summary>
protected static Logger lg;
protected bool _connOk = false;
/// <summary>
/// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC
/// </summary>
protected bool adpCommAct;
/// <summary>
/// porta x adapter (x restart)
/// </summary>
protected int adpPortNum;
/// <summary>
/// DataOra ultimo avvio adapter x watchdog
/// </summary>
protected DateTime adpStartRun;
/// <summary>
/// Vettore 32 BIT valori in ingresso al filtro
/// </summary>
protected int B_input;
/// <summary>
/// Vettore 32 BIT valori in uscita dal filtro
/// </summary>
protected int B_output;
/// <summary>
/// Vettore 32 BIT valori precedenti
/// </summary>
protected int B_previous;
/// <summary>
/// Dizionario valori impostati x produzione
/// </summary>
protected Dictionary<string, string> currProdData = new Dictionary<string, string>();
/// <summary>
/// Array dei contatori x segnali blinking
/// </summary>
protected int[] i_counters;
/// <summary>
/// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...)
/// </summary>
protected bool inSetup = false;
/// <summary>
/// ultimo tentativo connessione...
/// </summary>
protected DateTime lastConnectTry;
/// <summary>
/// Ultimo invio contapezzi (x invio delayed)
/// </summary>
protected DateTime lastPzCountSend;
/// <summary>
/// Dizionario ultimi valori (double) delle TSVC
/// </summary>
protected Dictionary<string, double> LastTSVC = new Dictionary<string, double>();
/// <summary>
/// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi)
/// </summary>
protected DateTime lastWarnODL;
/// <summary>
/// indica se serva refresh parametri e quindi PLC...
/// </summary>
protected bool needRefresh = true;
/// <summary>
/// Form chiamante
/// </summary>
protected AdapterForm parentForm;
/// <summary>
/// TImeout x ping al server
/// </summary>
protected int pingServerMsTimeout = utils.CRI("pingMsTimeout");
/// <summary>
/// Ritardo minimo x invio contapezzi
/// </summary>
protected int pzCountDelay;
/// <summary>
/// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...)
/// </summary>
protected Dictionary<string, VCData> TSVC_Data = new Dictionary<string, VCData>();
/// <summary>
/// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a scadenza periodo...)
/// </summary>
protected Dictionary<string, EVData> VarArray = new Dictionary<string, EVData>();
#endregion Protected Fields
#region Public Fields
/// <summary>
/// valore booleano di check se sia stato AVVIATO l'adapter (Running)
/// </summary>
public bool adpRunning = false;
/// <summary>
/// valore booleano di check se l'adapter STIA SALVANDO
/// </summary>
public bool adpSaving = false;
/// <summary>
/// valore booleano (richiesta di riavvio automatico)
/// </summary>
public bool adpTryRestart;
/// <summary>
/// Conf adapter corrente
/// </summary>
public IobConfiguration cIobConf;
/// <summary>
/// Conteggio ATTUALE ore macchina IN LAVORO
/// </summary>
public double contOreMaccLav;
/// <summary>
/// Conteggio ATTUALE ore macchina ON
/// </summary>
public double contOreMaccOn;
/// <summary>
/// contatore x simulazione valori input
/// </summary>
public int countSim = 0;
/// <summary>
/// ODL attualmente sulla macchina
/// </summary>
public Int32 currIdxODL = 0;
/// <summary>
/// Modo corrente (da classe ENUM)
/// </summary>
public CNC_MODE currMode;
/// <summary>
/// ODL corrente caricato sulla macchina (stringa, da chiamata MP/IO)
/// </summary>
public string currODL = "";
/// <summary>
/// Indica se sia richiesto campionamento memoria PERIODICO
/// </summary>
public bool doSampleMemory;
/// <summary>
/// Indica se si debba leggere e fare DUMP delle aree di memoria (1 volta solo all'avvio x debug...)
/// </summary>
public bool doStartMemDump;
/// <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>
/// Indicazione VETO check status IOB x evitare loop troppo stretti...
/// </summary>
public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1);
/// <summary>
/// Abilitazione lettura PrgName
/// </summary>
public bool enablePrgName = true;
/// <summary>
/// Abilitazione invio pezzi "in blocco" per recupero contapezzi
/// </summary>
public bool enableSendPzCountBlock = false;
/// <summary>
/// Determina se sia encessario convertire valori little/big endian (SIEMENS=true, OSAI=FALSE)
/// </summary>
public bool hasBigEndian = false;
/// <summary>
/// dataOra ultima verifica CNC disconnesso...
/// </summary>
public DateTime lastDisconnCheck;
/// <summary>
/// Data/ora ultima volta che IOB è stato dichairato online
/// </summary>
public DateTime lastIobOnline = DateTime.Now.AddHours(-1);
/// <summary>
/// dataOra ultimo log periodico...
/// </summary>
public DateTime lastPeriodicLog;
/// <summary>
/// dataOra ultimo PING inviato verso il PLC...
/// </summary>
public DateTime lastPING;
/// <summary>
/// DataOra ultima lettura da PLC
/// </summary>
public DateTime lastReadPLC;
/// <summary>
/// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...)
/// </summary>
public string lastSignInVal = "";
/// <summary>
/// DateTime Ultimo valore simulazione generato
/// </summary>
public DateTime lastSim;
/// <summary>
/// dataOra ultimo segnale inviato...
/// </summary>
public DateTime lastWatchDog;
/// <summary>
/// Massimo numero di px da inviare in blocco
/// </summary>
public int maxSendPzCountBlock = 10;
/// <summary>
/// Struttura memoria PLC x lettura/scrittura da JSON file
/// </summary>
public plcMemMap memMap;
/// <summary>
/// Minimo numero di px da inviare in blocco
/// </summary>
public int minSendPzCountBlock = 5;
/// <summary>
/// Variabile booleana che indica se sia necessario fare refresh del contapezzi
/// </summary>
public bool needRefreshPzCount = true;
/// <summary>
/// Dizionario di persistenza per i valori da salvare da/su file
/// </summary>
public Dictionary<string, string> persistenceLayer;
/// <summary>
/// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing "monoblocco" semplificato"=
/// </summary>
public bool procIotMem = false;
/// <summary>
/// Coda valori ALLARMI ove gestiti...
/// </summary>
public ConcurrentQueue<string> QueueAlarm = new ConcurrentQueue<string>();
/// <summary>
/// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi)
/// </summary>
public ConcurrentQueue<string> QueueFLog = new ConcurrentQueue<string>();
/// <summary>
/// Oggetto della coda degli elementi letti (e non ancora trasmessi)
/// </summary>
public ConcurrentQueue<string> QueueIN = new ConcurrentQueue<string>();
/// <summary>
/// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)...
/// </summary>
public ConcurrentQueue<string> QueueMessages = new ConcurrentQueue<string>();
/// <summary>
/// alias booleano false = R
/// </summary>
public bool R = false;
/// <summary>
/// 32 byte input base (es strobe, 8 word da 32 bit di flags...)
/// </summary>
public byte[] RawInput = new byte[32];
/// <summary>
/// 32 byte output base (es ack, 8 word da 32 bit di flags...)
/// </summary>
public byte[] RawOutput = new byte[32];
/// <summary>
/// Oggetto connessione REDIS
/// </summary>
public RedisIobCache redisMan;
/// <summary>
/// Oggetto cronometro x campionamento durate chiamate
/// </summary>
public Stopwatch stopwatch = new Stopwatch();
/// <summary>
/// Oggetto gestione TempiCiclo e contapezzi
/// </summary>
public TCMan tcMan = new TCMan(0.5, 1.3, 5);
/// <summary>
/// Imposta veto chiamata split (durante chiamata, per 60 sec)
/// </summary>
public DateTime vetoSplit = DateTime.Now.AddMinutes(1);
/// <summary>
/// alias booleano true = W
/// </summary>
public bool W = true;
#endregion Public Fields
#region Public Constructors
/// <summary>
/// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto
/// </summary>
/// <param name="caller"></param>
/// <param name="adpConf"></param>
public IobGeneric(AdapterForm caller, IobConfiguration IOBConf)
{
if (IOBConf != null)
{
// init oggetto redis...
redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB);
// initi oggetto TCMan
tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz);
// salvo il form chiamante
parentForm = caller;
// configurazione...
cIobConf = IOBConf;
lastConnectTry = DateTime.Now;
// aggiungo nel logger IDX Macchina
lg = LogManager.GetCurrentClassLogger();
lgInfo("Avvio preliminare AdapterGeneric");
// aggiungo altri defaults
setDefaults(true);
setParamPlc();
// checkLogDir x shrink!
checkShrinkDir();
// concluso!
lgInfo("Istanziata classe preliminare IOBGeneric");
}
else
{
lgError("Error: IobCOnf is null!");
}
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento Iob ha subito un refresh
/// </summary>
public event EventHandler<iobRefreshedEventArgs> eh_refreshed;
#endregion Public Events
#region Private Properties
/// <summary>
/// Verifica se la IOB sia ENABLED (da server o Demo)
/// </summary>
private bool checkIobEnabled
{
get
{
bool answ = false;
// controllo se ho veto al check...
if (dtVetoCheckIOB < DateTime.Now)
{
if (DemoOut)
{
answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend);
}
else
{
try
{
// chiamo URL, se restituisce "OK" è enabled!
string callResp = callUrl(urlIobEnabled, true);
answ = (callResp == "OK");
// attesa casuale se necessario
var rand = new Random();
// primi 2 test
int maxTry = 2;
while (maxTry > 0 && !answ)
{
Thread.Sleep(rand.Next(250, 500));
callResp = callUrl(urlIobEnabled, true);
answ = (callResp == "OK");
maxTry--;
}
// se NON OK riprovo ANCORA 1 volta...
if (!answ)
{
resetWebClients();
Thread.Sleep(rand.Next(250, 1000));
callResp = callUrl(urlIobEnabled, false);
answ = (callResp == "OK");
}
// altri 2
maxTry = 2;
while (maxTry > 0 && !answ)
{
Thread.Sleep(rand.Next(250, 500));
callResp = callUrl(urlIobEnabled, false);
answ = (callResp == "OK");
maxTry--;
}
// salvo status...
IobOnline = answ;
// se online imposto veto check a 5 x tempo reinvio...
if (answ)
{
lastIobOnline = DateTime.Now;
}
dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5);
}
catch
{ }
}
// verifico SE è variato stato online/offline...
if (IobOnline != answ)
{
// se ORA sono online riporto...
if (answ)
{
lgInfo("IOB ONLINE for server MP/IO");
}
else
{
lgInfo("IOB OFFLINE for server MP/IO");
}
}
// fix colore
if (answ)
{
parentForm.commSrvActive = 2;
}
else
{
parentForm.commSrvActive = 1;
}
}
else
{
// altrimenti passo ultimo valore noto
answ = IobOnline;
}
return answ;
}
}
/// <summary>
/// test ping all'indirizzo impostato nei parametri
/// </summary>
/// <returns></returns>
private IPStatus testPingServer
{
get
{
IPStatus answ = IPStatus.Unknown; ;
IPAddress address;
PingReply reply;
using (Ping pingSender = new Ping())
{
address = IPAddress.Loopback;
int maxRetry = maxPingRetry + 1;
int numRetry = 1; ;
string ipAdrr = cIobConf.serverData.MPIP.Replace("http://", "").Replace("https://", "");
IPAddress.TryParse(ipAdrr, out address);
reply = pingSender.Send(address, pingServerMsTimeout);
// se ho timeout riprovo...
while (reply.Status != IPStatus.Success && numRetry < maxRetry)
{
lgInfo($"Ping KO | reply: {reply.Status} --> retry");
reply = pingSender.Send(address, pingServerMsTimeout * numRetry / 2);
numRetry++;
if (reply.Status == IPStatus.Success)
{
lgInfo("PING OK!");
break;
}
}
}
answ = reply.Status;
return answ;
}
}
#endregion Private Properties
#region Protected Properties
/// <summary>
/// Valore del num max invii consecutivi da coda...
/// </summary>
protected static int nMaxSend
{
get
{
int answ = 5;
try
{
answ = utils.CRI("nMaxSend");
}
catch
{ }
return answ;
}
}
/// <summary>
/// Variabile di appoggio GLOBALE x indicare macchina RUN (es x gestione su reset pezzi)
/// </summary>
protected bool isRunning { get; set; } = false;
/// <summary>
/// Valore limite MASSIMO di invio di dati come array Json
/// </summary>
protected int maxJsonData { get; set; } = utils.CRI("maxJsonData");
/// <summary>
/// Valore limite MASSIMO di invio di dati come array Json x EVENTI
/// </summary>
protected int maxJsonDataEv { get; set; } = utils.CRI("maxJsonDataEv");
/// <summary>
/// Max tentativi ping permessi (default: 5)
/// </summary>
protected int maxPingRetry { get; set; } = 5;
/// <summary>
/// Coda massima ammessa per FLog (se <=0 disattivata...)
/// </summary>
protected int maxQueueFLog { get; set; } = utils.CRI("maxQueueFLog");
/// <summary>
/// Valore MINIMO limite x decidere invio di dati come array Json
/// </summary>
protected int minJsonData { get; set; } = utils.CRI("minJsonData");
/// <summary>
/// Numero letture IN da avvio
/// </summary>
protected int nReadFilt { get; set; }
/// <summary>
/// Numero letture IN da avvio
/// </summary>
protected int nReadIN { get; set; }
/// <summary>
/// Numero invii OUT (svuotamento coda)
/// </summary>
protected int nSendOut { get; set; }
/// <summary>
/// Numero simulazioni ammesse...
/// </summary>
protected int numSim { get; set; }
/// <summary>
/// Indica se sia stato resettato un contapezzi
/// </summary>
protected bool pzCountResetted { get; set; } = false;
/// <summary>
/// Fornisce il valore letto da BITMAP in formato valido x messa in coda nel formato dtEve#value#cont
/// </summary>
protected string qEncodeIN
{
get
{
string answ = "";
try
{
answ = string.Format("{0:yyyyMMddHHmmssfff}#{1:X2}#{2}", DateTime.Now, B_output, counterSigIN);
}
catch
{ }
return answ;
}
}
/// <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;
IPAddress.TryParse(cIobConf.cncIpAddr, out address);
int pingMsTimeout = cIobConf.pingMsTimeout;
reply = pingSender.Send(address, pingMsTimeout);
answ = reply.Status;
}
}
return answ;
}
}
/// <summary>
/// Secondi standard x veto check status e log
/// </summary>
protected int vetoSeconds { get; set; } = utils.CRI("vetoSeconds");
#endregion Protected Properties
#region Public Properties
/// <summary>
/// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...)
/// </summary>
public static bool DemoInSample
{
get
{
return baseUtils.CRB("DemoInSample");
}
}
/// <summary>
/// Verifica se sia in modalità DEMO x dati OUTPUT
/// </summary>
public static bool DemoOut
{
get
{
return utils.CRB("DemoOut");
}
}
/// <summary>
/// Indicazione VETO PING a server sino alla data-ora indicata
/// </summary>
public static DateTime dtVetoPing
{
get
{
return utils.dtVetoPing;
}
set
{
utils.dtVetoPing = value;
}
}
/// <summary>
/// Indicazione VETO invio a server sino alla data-ora indicata
/// </summary>
public static DateTime dtVetoSend
{
get
{
return utils.dtVetoSend;
}
set
{
utils.dtVetoSend = value;
}
}
/// <summary>
/// stato Online/Offline del server MP IO (su REDIS)
/// </summary>
public static bool MPOnline
{
get
{
return utils.MPIO_Online;
}
set
{
utils.MPIO_Online = value;
}
}
/// <summary>
/// Verifica se il server sia ALIVE (tramite PING)
/// </summary>
public bool checkServerAlive
{
get
{
bool answ = false;
// controllo se ho un VETO all'invio...
if (dtVetoPing < DateTime.Now)
{
if (DemoOut)
{
answ = false;
}
else
{
IPStatus pingStatus = testPingServer;
// se passa il ping faccio il resto...
if (pingStatus == IPStatus.Success)
{
string callResp = "";
try
{
// chiamo URL, se restituisce "OK" è alive!
callResp = callUrl(urlAlive, false);
answ = (callResp == "OK");
}
catch (Exception exc)
{
lgError("Errore in checkServerAlive:{0}{1}", Environment.NewLine, exc);
}
// attesa casuale se necessario
var rand = new Random();
// primi 3 test
int maxTry = 3;
while (maxTry > 0 && !answ)
{
try
{
Thread.Sleep(rand.Next(150, 500));
callResp = callUrl(urlAlive, false);
answ = (callResp == "OK");
maxTry--;
}
catch
{ }
}
// se NON OK riprovo ANCORA 1 volta...
if (!answ)
{
resetWebClients();
Thread.Sleep(rand.Next(500, 1000));
callResp = callUrl(urlAlive, false);
answ = (callResp == "OK");
}
// altri 3
maxTry = 3;
while (maxTry > 0 && !answ)
{
try
{
Thread.Sleep(rand.Next(150, 500));
callResp = callUrl(urlAlive, false);
answ = (callResp == "OK");
maxTry--;
}
catch
{ }
}
// verifico SE è variato stato online/offline...
if (MPOnline != answ)
{
// se ORA sono online riporto...
if (answ)
{
lgInfo("SERVER ONLINE in checkServerAlive");
parentForm.commSrvActive = 1;
dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec);
}
else
{
lgInfo("SERVER OFFLINE in checkServerAlive");
parentForm.commSrvActive = 0;
}
// salvo nuovo status...
MPOnline = answ;
}
else
{
// allungo periodo controllo...
dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3);
}
}
else
{
lgInfo($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})");
MPOnline = false;
// imposto veto a 10 volte reinvio dati standard...
dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3);
utils.dtVetoSend = dtVetoPing;
}
}
}
else
{
// altrimenti passo ultimo valore noto...
answ = MPOnline;
}
return answ;
}
}
/// <summary>
/// Salva verifica stato connessione OK
/// </summary>
/// <returns></returns>
public virtual bool connectionOk
{
get
{
return _connOk || DemoIn;
}
set
{
_connOk = value;
}
}
/// <summary>
/// Contapezzi attuale
/// </summary>
public Int32 contapezziIOB
{
get
{
return tcMan.pzCountIOB;
}
set
{
tcMan.pzCountIOB = value;
}
}
/// <summary>
/// Ultima lettura variabile contapezzi da CNC
/// </summary>
public Int32 contapezziPLC
{
get
{
return tcMan.pzCountPLC;
}
set
{
tcMan.pzCountPLC = value;
}
}
/// <summary>
/// Contatore x invio dati FluxLog
/// </summary>
public int counterFLog { get; set; }
/// <summary>
/// Contatore x invio dati SignalIN
/// </summary>
public int counterSigIN { get; set; }
/// <summary>
/// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA...
/// </summary>
public bool DemoIn
{
get
{
return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn");
}
}
/// <summary>
/// Indica lo stato Online/Offline della IOB
/// </summary>
public bool IobOnline
{
get
{
return utils.IOB_Online;
}
set
{
utils.IOB_Online = value;
}
}
/// <summary>
/// Verifica se sia macchina multi = DoppioPallet da CONF
/// </summary>
public bool isMulti
{
get
{
bool answ = false;
if (cIobConf.optPar.Count > 0)
{
// cerco con chiave reale IS_MULTI
string keyName = "IS_MULTI";
if (!cIobConf.optPar.ContainsKey(keyName))
{
// legacy: accetto anche SIM_MULTI...
keyName = "SIM_MULTI";
}
// vera verifica su chaive...
if (cIobConf.optPar.ContainsKey(keyName))
{
string SIM_MULTI = getOptPar(keyName);
answ = SIM_MULTI == "1";
}
}
return answ;
}
}
/// <summary>
/// Log verboso da configurazione (SOLO CHAIVE "verbose"...
/// </summary>
public bool isVerboseLog { get; set; } = utils.CRB("verbose");
/// <summary>
/// Ultimo Alarm letto
/// </summary>
public string lastAlarm { get; set; }
/// <summary>
/// Ultimo ARRAY DynData letto
/// </summary>
public Dictionary<string, string> lastDynData { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Ultimo DynData (sunto) letto
/// </summary>
public string lastDynDataCtrlVal { get; set; }
/// <summary>
/// Ultimo Override set letto
/// </summary>
public string lastOverrideFS { get; set; }
/// <summary>
/// Ultimo Override set letto
/// </summary>
public string lastOverrideRapid { get; set; }
/// <summary>
/// Ultimo programma letto
/// </summary>
public string lastPrgName { get; set; }
/// <summary>
/// Ultimo SysInfo letto
/// </summary>
public string lastSysInfo { get; set; }
/// <summary>
/// Ultimo URL
/// </summary>
public string lastUrl { get; set; }
/// <summary>
/// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per 100... 200% --> 200)
/// </summary>
public int maxPzDeltaPerc
{
get
{
int answ = 250;
if (cIobConf.optPar.Count > 0)
{
// cerco con chiave MAX_PZ_INCR_PERC
string keyName = "MAX_PZ_INCR_PERC";
// vera verifica su chaive...
if (cIobConf.optPar.ContainsKey(keyName))
{
string MAX_PZ_INCR_PERC = getOptPar(keyName);
if (!int.TryParse(MAX_PZ_INCR_PERC, out answ))
{
answ = 300;
}
}
}
return answ;
}
}
/// <summary>
/// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...)
/// </summary>
public bool periodicLog
{
get
{
bool answ = false;
answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut"));
if (answ)
{
lastPeriodicLog = DateTime.Now;
}
return answ;
}
}
/// <summary>
/// indica se ping disabilitato da optPar
/// </summary>
public bool pingDisabled
{
get
{
bool answ = false;
bool.TryParse(getOptPar("NO_PING"), out answ);
return answ;
}
}
/// <summary>
/// DataOra dell'ultima lettura variabile contapezzi da CNC in secondi
/// </summary>
public double plcAvgTc
{
get
{
double answ = tcMan.avgTC > 0 ? tcMan.avgTC : 1;
return answ;
}
}
/// <summary>
/// DataOra dell'ultima lettura variabile contapezzi da CNC
/// </summary>
public DateTime plcLastPzRead
{
get
{
return tcMan.lastObservedData;
}
}
/// <summary>
/// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)...
/// </summary>
public string urlAddPzCount
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/savePzCountInc/{3}?qty=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlAddPzCount");
}
return answ;
}
}
/// <summary>
/// URL per check alive...
/// </summary>
public string urlAlive
{
get
{
return string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE);
}
}
/// <summary>
/// URL per forzare split ODL...
/// </summary>
public string urlForceSplit
{
get
{
return string.Format(@"http://{0}{1}{2}{3}?doConfirm=true&qtyFromLast=true&roundStep=500", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_FORCLE_SPLIT_ODL, cIobConf.codIOB);
}
}
/// <summary>
/// URL per salvataggio contapezzi...
/// </summary>
public string urlGetCurrODL
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/getCurrODL/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetCurrODL");
}
return answ;
}
}
/// <summary>
/// URL per richiamo parametri da scrivere...
/// </summary>
public string urlGetParams2Write
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/getObjItems2Write/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetParams2Write");
}
return answ;
}
}
/// <summary>
/// URL per recupero contapezzi...
/// </summary>
public string urlGetPzCount
{
get
{
string answ = "";
try
{
answ = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCounter/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetPzCount");
}
return answ;
}
}
/// <summary>
/// URL per recupero contapezzi REGISTRATI da TC...
/// </summary>
public string urlGetPzCountRec
{
get
{
string answ = "";
try
{
answ = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCounterTCRec/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetPzCountRec");
}
return answ;
}
}
/// <summary>
/// URL per richiamo task da eseguire...
/// </summary>
public string urlGetTask2Exe
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/getTask2Exe/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetTask2Exe");
}
return answ;
}
}
/// <summary>
/// URL per recupero idle time IOB...
/// </summary>
public string urlIdleTime
{
get
{
return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_IDLE_TIME, cIobConf.codIOB);
}
}
/// <summary>
/// URL per recupero inizio ODL...
/// </summary>
public string urlInizioOdlIob
{
get
{
return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMD_ODL_STARTED, cIobConf.codIOB);
}
}
/// <summary>
/// URL per check se abilitato...
/// </summary>
public string urlIobEnabled
{
get
{
return string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDENABLED, cIobConf.codIOB);
}
}
/// <summary>
/// URL per segnalazione reboot...
/// </summary>
public string urlReboot
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}{3}&mac={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDREBO, cIobConf.codIOB, GetMACAddress());
}
catch
{
answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDREBO, cIobConf.codIOB);
}
return answ;
}
}
/// <summary>
/// URL per richiamo task da eseguire...
/// </summary>
public string urlRemTask2Exe
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/remTask2Exe/{3}?taskName=", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlRemTask2Exe");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio dati PARAMETRI IOB...
/// </summary>
public string urlSaveAllParams
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
string apiCall = "setObjItems";
answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveMemConf");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio dati conf memoria IOB...
/// </summary>
public string urlSaveMemMap
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = string.Format(@"http://{0}{1}{2}/saveConf/{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveMemConf");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio dati associazione Machine 2 IOB...
/// </summary>
public string urlSetM2IOB
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = string.Format(@"http://{0}{1}{2}/setM2IOB/{3}?IOB_name={4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB, machineName);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetM2IOB");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio VALORI opzionali...
/// </summary>
public string urlSetOptVal
{
get
{
string answ = "";
try
{
answ = string.Format(@"http://{0}{1}{2}/addOptPar/{3}?", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetOptVal");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio contapezzi...
/// </summary>
public string urlSetPzCount
{
get
{
string answ = "";
try
{
answ = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/{cIobConf.codIOB}?counter=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetPzCount");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio contapezzi (quelli della macchina: PLC/CNC)...
/// </summary>
public string urlSetPzCountMAC
{
get
{
string answ = "";
try
{
answ = $@"http://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setCounter/MAC_{cIobConf.codIOB}?counter=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetPzCountMAC");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio in UPSERT dei PARAMETRI IOB scritti...
/// </summary>
public string urlUpdateWriteParams
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
string apiCall = "upsertObjItems";
answ = string.Format(@"http://{0}{1}{2}/{3}/{4}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDALIVE, apiCall, cIobConf.codIOB);
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveMemConf");
}
return answ;
}
}
/// <summary>
/// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN)
/// </summary>
public bool verboseLog
{
get
{
bool answ = false;
int logEvery = utils.CRI("logEvery");
if (logEvery < 1)
{
logEvery = 10;
}
answ = utils.CRB("verbose") && (nReadIN % logEvery == 0);
return answ;
}
}
#endregion Public Properties
#region Private Methods
/// <summary>
/// Verifica e se necessario comprime directory log...
/// </summary>
private void checkShrinkDir()
{
string path = string.Format("{0}logs\\{1}", AppDomain.CurrentDomain.BaseDirectory, cIobConf.codIOB);
baseUtils.shrinkDir(path);
}
/// <summary>
/// Mostra i dati grezzi letti in esadecimale
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
/// </summary>
private void displayRawData(ref newDisplayData currDispData)
{
// mostro update...
string newString = string.Format("{0:X}", B_input);
currDispData.newInData = newString;
#if false
// salvo coda debug...
QueueDebug.Enqueue(B_input);
#endif
}
/// <summary>
/// Esegue filtraggio dati x bit blinking!!!
/// </summary>
private void filterData()
{
// effettuo filtraggio dei valori letti... inizializzo OUT!
B_output = 0;
// in primis verifico SE ci siano bit blinkng... se non ci sono OUT=IN...
if (cIobConf.BLINK_FILT == 0)
{
B_output = B_input;
}
else
{
// incomincio con i valori NON blinking: questi "passano invariati", inizio a sommare nel valore OUT...
B_output = B_input & ~cIobConf.BLINK_FILT;
// calcolo il valore dei BIT che "passano la maschera"
int iBlink = B_input & cIobConf.BLINK_FILT;
// ...aggiungo i "bit che passano"
B_output += iBlink;
// calcolo QUALI valori (tra quelli blink) siano PASSATI da 0 a 1 --> init counters...
BitArray bBlinkStart = new BitArray(new byte[] { Convert.ToByte(iBlink) });
int[] bitsUp = bBlinkStart.Cast<bool>().Select(bit => bit ? 1 : 0).ToArray();
for (int i = 0; i < bitsUp.Length; i++)
{
// SE 1... impostiamo contatori al MAX
if (bitsUp[i] == 1)
{
// se era zero indico START blink...
if (i_counters[i] == 0)
{
lgInfo("START BLINK: B{0}", i);
}
// imposto comunque contatore al cambio fronte...
i_counters[i] = cIobConf.MAX_COUNTER_BLINK;
}
}
// quelli che sono zero... LI RECUPERO E LI PROCESSO...
int iZero = ~B_input & cIobConf.BLINK_FILT;
BitArray bBlinkEnd = new BitArray(new byte[] { Convert.ToByte(iZero) });
int[] bitsDown = bBlinkEnd.Cast<bool>().Select(bit => bit ? 1 : 0).ToArray();
for (int i = 0; i < bitsDown.Length; i++)
{
// se era a zero (invertito...)
if (bitsDown[i] == 1)
{
// SE è in corso il conteggio...
if (i_counters[i] > 0)
{
// decremento!
i_counters[i] -= 1;
// se è zero NON faccio nulla, altrimenti SOMMO...
if (i_counters[i] > 0)
{
B_output += 1 << i;
}
else
{
lgInfo("END BLINK: B{0}", i);
}
}
}
}
}
}
/// <summary>
/// Imposta eventuali altri valori default
/// </summary>
private void fixDefaultPar()
{
// parametro max tentativi PING...
string s_maxPingRetry = getOptPar("MAX_PING_RETRY");
if (!string.IsNullOrEmpty(s_maxPingRetry))
{
int numRetry = 5;
int.TryParse(s_maxPingRetry, out numRetry);
maxPingRetry = numRetry;
}
}
/// <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 != null)
{
if (!persistenceLayer.TryGetValue(keyVal, out value))
{
persistenceLayer.Add(keyVal, "0");
}
}
}
catch (Exception exc)
{
lgError(string.Format("Eccezione in getStoredVal: {0}{1}", Environment.NewLine, exc));
}
return value;
}
/// <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 (Exception exc)
{
lgError(string.Format("Eccezione in getStoredValDouble: {0}{1}", Environment.NewLine, exc));
}
answ = (answ < (double.MaxValue / 10 * 9)) ? answ : 0;
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
{ }
// verifico che il valore sia minore di 9/10 del valore massimo...
answ = (answ < (long.MaxValue / 10 * 9)) ? answ : 0;
return answ;
}
/// <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 (Exception exc)
{
lgError(string.Format("Eccezione in getStoredValUInt: {0}{1}", Environment.NewLine, exc));
}
// verifico che il valore sia minore di 9/10 del valore massimo...
answ = (answ < (uint.MaxValue / 10 * 9)) ? answ : 0;
return answ;
}
/// <summary>
/// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo...
/// </summary>
private void processAllMemory()
{
// init obj display
newDisplayData currDispData = new newDisplayData();
// in primis SALVO valori previous/precedenti
B_previous = B_output;
// poi faccio lettura NUOVI valori
readAllData(ref currDispData);
// eseguo il filtering dei valori (per i bit "blinking")
filterData();
// effettuo confronto valori vecchi/nuovi... SE trovo variazione OPPURE se è passato + di un timeout di controllo...
if (B_output != B_previous)
{
accodaSigIN(ref currDispData);
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua gestioen programma: legge e mostra su display...
/// </summary>
private void processProgram()
{
string currPrgName = "";
// se abilitata lettura prgName
if (enablePrgName)
{
if (connectionOk)
{
currPrgName = getPrgName();
}
else
{
lgError("Errore connessione mancante x getPrgName");
}
}
else
{
currPrgName = lastPrgName;
}
// verifico SE sia cambiato il programma...
if (lastPrgName != currPrgName)
{
// salvo!
lastPrgName = currPrgName;
string sVal = string.Format("[PROG]{0}", currPrgName);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog("PROG", currPrgName));
}
}
/// <summary>
/// Processo lettura dati sysinfo
/// </summary>
private void processSysInfo()
{
if (utils.CRB("enableSysInfo"))
{
Dictionary<string, string> currSysInfo = new Dictionary<string, string>();
if (connectionOk)
{
currSysInfo = getSysInfo();
}
else
{
lgError("Errore connessione mancante x getSysInfo");
}
// verifico SE sia cambiato il programma...
if (lastSysInfo != currSysInfo["SYSINFO"])
{
// salvo!
lastSysInfo = currSysInfo["SYSINFO"];
// per ogni valore del dizionario mostro ed accodo!
string sVal = "";
foreach (var item in currSysInfo)
{
sVal = string.Format("[SYSINFO]{0}|{1}", item.Key, item.Value);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog(item.Key, item.Value));
}
}
}
}
private void reportDataProc()
{
// update valori visualizzazione...
parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut);
}
/// <summary>
/// Imposto alcuni valori di default
/// </summary>
/// <param name="resetQueue">indica se sia richeisto di SVUOTARE le code delel info</param>
private void setDefaults(bool resetQueue)
{
numSim = utils.CRI("numSim");
lastPrgName = "";
nReadIN = 0;
nReadFilt = 0;
nSendOut = 0;
currMode = 0;
lastAlarm = "";
doStartMemDump = utils.CRB("doStartMemDump");
doSampleMemory = utils.CRB("doSampleMemory");
// svuoto code se richiesto
if (resetQueue)
{
QueueIN = new ConcurrentQueue<string>();
QueueFLog = new ConcurrentQueue<string>();
QueueAlarm = new ConcurrentQueue<string>();
QueueMessages = new ConcurrentQueue<string>();
}
// imposto contatori blink a zero...
i_counters = new int[32];
lastPeriodicLog = DateTime.Now;
// fix parametri generali...
enablePrgName = true;
}
private void svuotaCodaContapezzi()
{
// permetto al max 2 tentativi infruttuosi...
int maxTry = 2;
int oldContapezzi = contapezziIOB;
// se ho contapezzi OLTRE limite...
while ((MPOnline) && (contapezziPLC > contapezziIOB + minSendPzCountBlock))
{
lgInfo($"Ciclo svuotaCodaContapezzi --> contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}");
if (!isMulti)
{
pzCntReload(true);
}
// provo invio
trySendPzCountBlock();
// verifica per evitare loop infinito invio fallito
if (oldContapezzi == contapezziIOB)
{
maxTry--;
}
else
{
maxTry = 2;
oldContapezzi = contapezziIOB;
}
// verifico maxTry: se li ho esauriti esco!
if (maxTry <= 0)
{
return;
}
// aspetto x dare tempo calcolo
Thread.Sleep(400);
}
}
/// <summary>
/// Processo la coda FLog...
/// </summary>
private void svuotaCodaFLog()
{
//controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!!
if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec"))
{
string wdStatus = "elapsed";
string sVal = string.Format("[WDST]{0}", wdStatus);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog("WDST", wdStatus));
lastWatchDog = DateTime.Now;
}
// verifico SE la coda abbia dei valori...
if (QueueFLog.Count > 0)
{
// invio pacchetto di dati (max da conf)
for (int i = 0; i < nMaxSend; i++)
{
// SE ho qualcosa in coda...
if (QueueFLog.Count > 0)
{
string currVal = "";
if (MPOnline)
{
if (IobOnline)
{
// se ho + di 2 elementi in coda --> uso invio JSON in blocco...
if (QueueFLog.Count > 1)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueFLog.Count > maxJsonData)
{
// prendoi primi maxJsonDataValori
for (int j = 0; j < maxJsonData; j++)
{
QueueFLog.TryDequeue(out currVal);
listaValori.Add(currVal);
}
sendDataBlock(urlType.FLog, listaValori);
}
else
{
// invio in blocco
listaValori = QueueFLog.ToList();
// invio
sendDataBlock(urlType.FLog, listaValori);
// svuoto!
QueueFLog = new ConcurrentQueue<string>();
}
}
else
{
// INVIO SINGOLO...!!!
QueueFLog.TryDequeue(out currVal);
sendToMoonPro(urlType.FLog, currVal);
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
}
/// <summary>
/// Cerca di inviare su un altro thread i vari dati accumulati...
/// </summary>
private void trySendValues()
{
// init obj display
newDisplayData currDispData = new newDisplayData();
try
{
// verifico se risponde il server...
if (checkServerAlive)
{
bool iobOk = false;
if (utils.CRB("sendDataByThread"))
{
Task taskCheck = TaskEx.Run(() => iobOk = checkIobEnabled);
}
else
{
iobOk = checkIobEnabled;
}
// verifico SE posso inviare dati
if (iobOk)
{
currDispData.semOut = Semaforo.SV;
// verificare come gestire il task secondario senza interferenza (chiamate update su FORM da thread secondari danno errori)
if (utils.CRB("sendDataByThread"))
{
// invio con thread separato...
Task taskSigIN = TaskEx.Run(() => svuotaCodaSignIN());
Task taskFlog = TaskEx.Run(() => svuotaCodaFLog());
}
else
{
// gestione queue SignalIN (invio, display)
svuotaCodaSignIN();
currDispData.counter = contapezziIOB;
raiseRefresh(currDispData);
// provo a svuotare coda contapezzi
svuotaCodaContapezzi();
currDispData.counter = contapezziIOB;
raiseRefresh(currDispData);
// gestione queue FluxLog (invio, display)
svuotaCodaFLog();
raiseRefresh(currDispData);
}
}
else
{
// mostro VETO-SEND x invio... GIALLO
currDispData.semOut = Semaforo.SG;
if (periodicLog)
{
lgInfo("IOB - VETO SEND");
}
}
}
else
{
// mostro SERVER KO x invio... ROSSO
currDispData.semOut = Semaforo.SR;
if (periodicLog)
{
lgInfo("IOB - SERVER NOT READY");
}
}
}
catch (Exception exc)
{
lgError($"Errore in fase trySendValues{Environment.NewLine}{exc}");
currDispData.semOut = Semaforo.SR;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce
/// </summary>
/// <param name="i"></param>
/// <param name="delta"></param>
/// <param name="searchString"></param>
/// <returns>Nuovo valore incrementato</returns>
private double updateValDoubleByIncr(int i, double delta, string searchString)
{
// stringa da cercare..
string keyVal = string.Format(searchString, i + 1);
// recupero valore precedente...
double contAct = getStoredValDouble(keyVal);
// nuovo valore...
contAct += delta;
// salvo in ram!
persistenceLayer[keyVal] = contAct.ToString();
// rendo il valore!
return contAct;
}
/// <summary>
/// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce
/// </summary>
/// <param name="i"></param>
/// <param name="delta"></param>
/// <param name="searchString"></param>
/// <returns>Nuovo valore incrementato</returns>
private long updateValLongByIncr(int i, long delta, string searchString)
{
// stringa da cercare..
string keyVal = string.Format(searchString, i + 1);
// recupero valore precedente...
long contAct = getStoredValLong(keyVal);
// nuovo valore...
contAct += delta;
// salvo in ram!
persistenceLayer[keyVal] = contAct.ToString();
// rendo il valore!
return contAct;
}
/// <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();
}
/// <summary>
/// Aggiorna un valore del dizionario in INCREMENTO e lo restituisce
/// </summary>
/// <param name="i"></param>
/// <param name="delta"></param>
/// <param name="searchString"></param>
/// <returns>Nuovo valore incrementato</returns>
private uint updateValUIntByIncr(int i, uint delta, string searchString)
{
// stringa da cercare..
string keyVal = string.Format(searchString, i + 1);
// recupero valore precedente...
uint contAct = getStoredValUInt(keyVal);
// nuovo valore...
contAct += delta;
// salvo in ram!
persistenceLayer[keyVal] = contAct.ToString();
// rendo il valore!
return contAct;
}
#endregion Private Methods
#region Protected Methods
/// <summary>
/// Decodifica file MAP (caso <paramref name="ByteNum"/>.bit)
/// </summary>
/// <param name="linea"></param>
/// <param name="separator"></param>
/// <param name="ByteNum">indirizzo Byte: indirizzo di partenza memoria</param>
/// <param name="memSize">dimensione singolo slot in byte</param>
/// <param name="BitNum">indirizzo bit: numero riga x calcolo indice bit</param>
/// <returns></returns>
protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum)
{
if (linea != null)
{
string[] valori = linea.Split(separator);
int shift = 0;
try
{
shift = Convert.ToInt32(valori[0]) - 1;
}
catch
{ }
int resto = 0;
Math.DivRem(BitNum, 8, out resto);
string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto);
return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim());
}
else
{
return null;
}
}
/// <summary>
/// Decodifica file MAP generico
/// </summary>
/// <param name="linea"></param>
/// <param name="separator"></param>
/// <param name="memPre"></param>
/// <param name="baseAddr"></param>
/// <param name="memSize"></param>
/// <returns></returns>
protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize)
{
if (linea != null)
{
string[] valori = linea.Split(separator);
int shift = 0;
try
{
shift = Convert.ToInt32(valori[0]) - 1;
}
catch
{ }
string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize);
return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim());
}
else
{
return null;
}
}
/// <summary>
/// Decodifica valore della coda IN nel formato
/// answ[0]=dtEve
/// answ[1]=valore
/// answ[2]=counter
/// </summary>
/// <param name="queueVal">dtEve + '#' + value + '#' + cont</param>
/// <returns></returns>
protected static string[] qDecodeIN(string queueVal)
{
string[] answ = null;
if (!string.IsNullOrEmpty(queueVal))
{
try
{
answ = queueVal.Split('#');
}
catch
{ }
}
return answ;
}
/// <summary>
/// Stringa raw dei parametri da scrivere...
/// </summary>
/// <returns></returns>
protected string getParams2write()
{
string answ = "";
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);
}
return answ;
}
/// <summary>
/// Chiede elenco dei task da eseguire
/// - formato Json
/// - array di KVP / Dictionary
/// - formato definito da API x MP/IO/:
/// - KEY: task
/// - VALUE: array JSon KVP
/// </summary>
protected string getTask2exe()
{
string answ = "";
if (checkServerAlive)
{
string url2call = $"{urlGetTask2Exe}";
if (verboseLog)
{
lgInfo("chiamata URL " + url2call);
}
answ = utils.callUrlNow(url2call);
}
return answ;
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message, bool sendToForm = true)
{
lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Error(message);
if (sendToForm)
{
sendToLogWatch("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.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Error(message, args);
sendToLogWatch("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.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Error(exception, message, args);
sendToLogWatch("ERROR", message, exception, args);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgFatal(string message, bool sendToForm = true)
{
lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Fatal(message);
if (sendToForm)
{
sendToLogWatch("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.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Fatal(message, args);
sendToLogWatch("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.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Fatal(exception, message, args);
sendToLogWatch("FATAL", message, exception, args);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message, bool sendToForm = true)
{
lg.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Info(message);
if (sendToForm)
{
sendToLogWatch("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.Factory.Configuration.Variables["codIOB"] = cIobConf.codIOB;
lg.Info(message, args);
sendToLogWatch("INFO", message, args);
}
/// <summary>
/// Legge il file di conf di una MAP di informazioni da gestire con lettura set memoria
/// </summary>
/// <param name="vettoreConf">nome vettore memoria</param>
/// <param name="nomeFile">file origine</param>
/// <param name="memSize">dimensione (in byte) della memoria</param>
/// <param name="numVett">dimensione (in byte) della memoria</param>
protected void loadConfFile(ref otherData[] vettoreConf, string nomeFile, int memSize, ref int numVett)
{
otherData lastData = new otherData();
int totRighe = 0;
string linea;
totRighe = File.ReadLines(nomeFile).Count();
// creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco...
vettoreConf = new otherData[File.ReadLines(nomeFile).Count()];
// carica da file...
StreamReader file = new StreamReader(nomeFile);
// leggo 1 linea alla volta...
int numRiga = 0;
int bitNum = 0;
int byteNum = 0;
while ((linea = file.ReadLine()) != null)
{
// SE non è un commento...
if (linea.Substring(0, 1) != "#")
{
// se finisce per BIT allora processo bit-a-bit...
if (linea.EndsWith("BOOL"))
{
try
{
string[] memIdx = linea.Split(utils.CRC("testCharSep"))[0].Split('.');
// calcolo bit e byte number...
int.TryParse(memIdx[0], out byteNum);
if (memIdx.Length > 1)
{
int.TryParse(memIdx[1], out bitNum);
}
else
{
bitNum = 0;
}
}
catch
{
byteNum = 0;
bitNum = 0;
}
lastData = decodeBitData(linea, utils.CRC("testCharSep"), byteNum, 1, bitNum);
vettoreConf[numRiga] = lastData;
}
else
{
lastData = decodeOtherData(linea, utils.CRC("testCharSep"), "", 1, memSize);
vettoreConf[numRiga] = lastData;
}
numRiga++;
}
}
// salvo lunghezza file...
try
{
numVett = Convert.ToInt32(lastData.memAddr) + 1;
}
catch
{
numVett = numRiga + 1;
}
// chiudo file
file.Close();
// ora trimmo vettore al solo numero VERO dei valori caricati...
Array.Resize<otherData>(ref vettoreConf, numRiga);
if (isVerboseLog)
{
lgInfo(string.Format("Fine caricamento vettore di {0} variabili per file {1}", numRiga, nomeFile));
}
}
/// <summary>
/// Lettura memorie conf speciali (json)
/// ...SE inserito in [OPTPAR] come PARAM_CONF=nome.json
/// </summary>
protected virtual void loadMemConf()
{
lgInfo("BEGIN loadMemConf");
// variabili x gestione send contapezzi in blocco
string currPar = getOptPar("ENABLE_SEND_PZC_BLOCK");
if (!string.IsNullOrEmpty(currPar))
{
bool.TryParse(currPar, out enableSendPzCountBlock);
// se abilitato leggo num pezzi da reinviare in blocco
if (enableSendPzCountBlock)
{
int.TryParse(getOptPar("MAX_SEND_PZC_BLOCK"), out maxSendPzCountBlock);
int.TryParse(getOptPar("MIN_SEND_PZC_BLOCK"), out minSendPzCountBlock);
}
}
else
{
lgError("loadMemConf: parametro ENABLE_SEND_PZC_BLOCK non trovato, verificare anche MAX_SEND_PZC_BLOCK e MIN_SEND_PZC_BLOCK");
}
// inizializzo LUT decodifica
string jsonConf = getOptPar("PARAM_CONF");
if (!string.IsNullOrEmpty(jsonConf))
{
string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{jsonConf}";
lgInfo($"Apertura file {jsonFileName}");
StreamReader reader = new StreamReader(jsonFileName);
string jsonData = reader.ReadToEnd();
if (!string.IsNullOrEmpty(jsonData))
{
lgInfo($"File json composto da {jsonData.Length} caratteri");
try
{
memMap = JsonConvert.DeserializeObject<plcMemMap>(jsonData);
lgInfo($"Decodifica aree memMap: trovati {memMap.mMapRead.Count} valori TSVC");
lgInfo($"Decodifica aree memMap: trovati {memMap.mMapWrite.Count} parametri ");
// 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)
{
lgInfo($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}");
// salvo i valori PREC...
LastTSVC.Add(item.Key, 0);
}
}
// infine se obj memoria valido salvo in MP-IO x sue applicazioni
if (memMap != null)
{
// invio su cloud conf memoria...
string rawData = JsonConvert.SerializeObject(memMap);
utils.callUrlNow($"{urlSaveMemMap}", rawData);
// salvo ANCHE come parametri i valori...
objItem currItem = new objItem();
List<objItem> allParam = new List<objItem>();
// valori WRITE
foreach (var item in memMap.mMapWrite)
{
currItem = new objItem()
{
uid = item.Value.name,
name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name,
writable = true
};
allParam.Add(currItem);
}
// valori READ
foreach (var item in memMap.mMapRead)
{
currItem = new objItem()
{
uid = item.Value.name,
name = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name,
writable = false
};
allParam.Add(currItem);
}
// invio su cloud parametri!
rawData = JsonConvert.SerializeObject(allParam);
utils.callUrl($"{urlSaveAllParams}", rawData);
}
}
catch (Exception exc)
{
lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}");
}
}
else
{
lgError("Errore in loadMemConf: file json vuoto!");
}
reader.Dispose();
}
else
{
lgInfo("loadMemConf: non trovata opzione PARAM_CONF in file INI");
}
// loggo
lgInfo("DONE loadMemConf");
}
/// <summary>
/// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
/// </summary>
/// <param name="updatedPar"></param>
protected virtual void plcWriteParams(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($"Effettuato update parametro: 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(updatedPar);
// invio su cloud parametri!
string rawData = JsonConvert.SerializeObject(updatedPar);
utils.callUrl($"{urlUpdateWriteParams}", rawData);
}
}
catch (Exception exc)
{
lgError($"Eccezione in processMemWriteRequests:{Environment.NewLine}{exc}");
}
}
else
{
lgError("Non è stata ricevuta risposta x task da eseguire");
}
return answ;
}
protected void raiseRefresh(newDisplayData currDispData)
{
if (currDispData != null)
{
if (currDispData.hasData)
{
// segnalo refresh!
if (eh_refreshed != null)
{
eh_refreshed(this, new iobRefreshedEventArgs(currDispData));
}
}
}
}
/// <summary>
/// Cancella dal server i task eseguiti
/// </summary>
/// <param name="taskName"></param>
/// <param name="esitoTask"></param>
/// <returns></returns>
protected string remTask2exe(string taskName, string esitoTask)
{
string answ = "";
if (checkServerAlive)
{
string url2call = $"{urlRemTask2Exe}{taskName}";
lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}");
answ = utils.callUrlNow(url2call);
}
return answ;
}
/// <summary>
/// Invia informazioni associazione IOB 2 machine
/// </summary>
protected void sendM2IOB()
{
if (checkServerAlive)
{
lgInfo("chiamata URL " + urlSetM2IOB);
utils.callUrlNow(urlSetM2IOB);
}
}
/// <summary>
/// Invia al server IO i valori dei parametri opzionali (es counters)
/// </summary>
/// <param name="paramName">Nome parametro</param>
/// <param name="paramValue">Valore parametro</param>
protected void sendOptVal(string paramName, string paramValue)
{
if (checkServerAlive)
{
string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}";
lgInfo("chiamata URL " + url2call);
utils.callUrlNow(url2call);
}
}
/// <summary>
/// Invia al server IO i valori dei parametri opzionali (es counters)
/// </summary>
/// <param name="paramName">Nome parametro</param>
/// <param name="paramValueInt">Valore parametro INT</param>
protected void sendOptVal(string paramName, int paramValueInt)
{
// override!
sendOptVal(paramName, paramValueInt.ToString());
}
/// <summary>
/// Invia messaggio a logWatcher
/// </summary>
/// <param name="messType"></param>
/// <param name="message"></param>
protected void sendToLogWatch(string messType, string message)
{
newDisplayData currDispData = new newDisplayData();
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}";
parentForm.updateFormDisplay(currDispData);
}
/// <summary>
/// Invia messaggio a logWatcher
/// </summary>
/// <param name="messType"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void sendToLogWatch(string messType, string message, params object[] args)
{
try
{
string expString = string.Format(message, args);
newDisplayData currDispData = new newDisplayData();
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}";
parentForm.updateFormDisplay(currDispData);
}
catch
{ }
}
/// <summary>
/// Invia messaggio a logWatcher
/// </summary>
/// <param name="messType"></param>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void sendToLogWatch(string messType, string message, Exception exception, params object[] args)
{
try
{
string expString = string.Format(message, args);
newDisplayData currDispData = new newDisplayData();
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {expString}{Environment.NewLine}{exception}";
parentForm.updateFormDisplay(currDispData);
}
catch
{ }
}
/// <summary>
/// Invia messaggio a logWatcher
/// </summary>
/// <param name="messType"></param>
/// <param name="message"></param>
protected void sendToTaskWatch(string messType, string message)
{
parentForm.taskWatcher = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | {messType} | {message}";
}
/// <summary>
/// Impostazioni parametri PLC
/// </summary>
protected virtual void setParamPlc()
{
loadMemConf();
fixDefaultPar();
}
#endregion Protected Methods
#region Public Methods
/// <summary>
/// Effettua chiamata URL e restituisce risultato
/// </summary>
/// <param name="URL"></param>
/// <param name="doAsync">invio in modalità async (NON GARANTITO ordine...)</param>
/// <returns></returns>
public static string callUrl(string URL, bool doAsync)
{
string answ = "";
// Chiamata ASINCRONA
if (doAsync)
{
//Task<string> resp = utils.callUrlAsync(URL);
//answ = resp.Result;
answ = utils.callUrlAsync(URL);
}
// chiamata SOLO NORMALE SINCRONA...
else
{
answ = utils.callUrl(URL);
}
return answ;
}
/// <summary>
/// Effettua chiamata URL e restituisce risultato
/// </summary>
/// <param name="URL"></param>
/// <param name="payload"></param>
/// <param name="doAsync">invio in modalità async (NON GARANTITO ordine...)</param>
/// <returns></returns>
public static string callUrlWithPayload(string URL, string payload, bool doAsync)
{
string answ = "";
// Chiamata ASINCRONA
if (doAsync)
{
answ = utils.callUrlAsync(URL, payload);
}
// chiamata SOLO NORMALE SINCRONA...
else
{
answ = utils.callUrl(URL, payload);
}
return answ;
}
/// <summary>
/// processa dataLayer e se necessario salva/mostra
/// </summary>
public static void checkSavePersDataLayer()
{
}
public static string GetMACAddress()
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
String sMacAddress = string.Empty;
foreach (NetworkInterface adapter in nics)
{
if (string.IsNullOrEmpty(sMacAddress))// only return MAC Address from first card
{
IPInterfaceProperties properties = adapter.GetIPProperties();
//sMacAddress = adapter.GetPhysicalAddress().ToString();
sMacAddress = string.Join(":", (from z in adapter.GetPhysicalAddress().GetAddressBytes() select z.ToString("X2")).ToArray());
}
}
return sMacAddress;
}
public static void resetDebugConsole()
{
}
/// <summary>
/// Reset dei webclients
/// </summary>
public static void resetWebClients()
{
utils.resetWebClients();
}
/// <summary>
/// Accumula in coda i valori ALARM e logga...
/// </summary>
/// <param name="val">VALORE RAW (x display)</param>
/// <param name="encodedVal">VALORE già processato con qEncodeFLog(...)</param>
public void accodaAlarmLog(string val, string encodedVal)
{
// mostro dati variati letti...
displayOtherData(val);
// accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)!
QueueFLog.Enqueue(encodedVal);
// accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati...
// loggo!
lgInfo(string.Format("[QUEUE-ALARM-LOG] {0}", encodedVal));
counterFLog++;
if (counterFLog > 9999)
{
counterFLog = 0;
}
}
/// <summary>
/// Accumula in coda i valori Flux Log e logga...
/// </summary>
/// <param name="val">VALORE RAW (x display)</param>
/// <param name="encodedVal">VALORE già processato con qEncodeFLog(...)</param>
public void accodaFLog(string val, string encodedVal)
{
// mostro dati variati letti...
displayOtherData(val);
// --> accodo (valore già formattato)!
QueueFLog.Enqueue(encodedVal);
// se abilitato controllo coda FLog (superiore a 0...)
if (maxQueueFLog > 0)
{
// se ho una coda superiore a max ammesso
if (QueueFLog.Count > maxQueueFLog)
{
// elimino valori iniziali fino a tornare al max ammesso...
while (QueueFLog.Count > maxQueueFLog)
{
string currVal = "";
QueueFLog.TryDequeue(out currVal);
lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}");
}
}
}
// loggo!
lgInfo(string.Format("[QUEUE-FLOG] {0}", encodedVal));
counterFLog++;
if (counterFLog > 9999)
{
counterFLog = 0;
}
}
/// <summary>
/// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA
/// </summary>
/// <param name="newLine"></param>
public void accodaOtherData(string newLine)
{
// inserisco in cima allo stack, trimmo e aggiorno display
string strOtherData = limitLine2show(string.Format("{0}{1}{2}", newLine, Environment.NewLine, parentForm.dataMonitor_3));
//parentForm.dataMonitor_3 = strOtherData;
parentForm.WriteTextSafe(strOtherData);
}
/// <summary>
/// Accumula in coda i valori Signal IN e logga...
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
/// </summary>
public void accodaSigIN(ref newDisplayData currDispData)
{
// mostro dati variati letti...
displayInData(ref currDispData);
// --> accodo (valore già formattato)!
QueueIN.Enqueue(qEncodeIN);
// loggo!
lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN));
// aggiorno counters ed eventuale reset
nReadFilt++;
if (nReadFilt > int.MaxValue - 1)
{
nReadFilt = 0; // per evitare buffer overflow...
}
counterSigIN++;
if (counterSigIN > 9999)
{
counterSigIN = 0;
}
}
/// <summary>
/// Update visualizzaizone BIT in ingresso
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
/// </summary>
public void displayInData(ref newDisplayData currDispData)
{
if (currDispData != null)
{
// mostro update...
string newString = string.Format("{0:0000}|{1}", counterSigIN, utils.IntToBinStr(B_output, 8));
currDispData.newSignalData = newString;
}
}
/// <summary>
/// Mostra cosa ha/avrebbe inviato
/// </summary>
/// <param name="newData"></param>
public void displayOtherData(string newData)
{
// mostro update...
accodaOtherData(newData);
}
/// <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)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
foreach (var item in task2exe)
{
try
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.nihil:
case taskType.fixStopSetup:
case taskType.forceSetPzCount:
case taskType.sendWatchDogMes2Plc:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
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}";
}
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 (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE")
{
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 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 = "SKIPPED | NO EXEC";
lgInfo($"Chiamata default senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
catch(Exception exc)
{
lgError($"Eccezione in executeTasks:{Environment.NewLine}{exc}");
lgError($"Dettaglio eccezione: item.key {item.Key} | item.Val {item.Value}");
}
}
}
return taskDone;
}
/// <summary>
/// Cerca parametri opzionali in modalità "like" del nome
/// </summary>
/// <param name="keyStartSearch"></param>
/// <returns></returns>
public Dictionary<string, string> findOptPar(string keyStartSearch = "")
{
Dictionary<string, string> answ = new Dictionary<string, string>();
// controllo SE keySearch !=""
if (!string.IsNullOrWhiteSpace(keyStartSearch))
{
if (cIobConf.optPar.Count > 0)
{
// ciclo su tutti e cerco occorrenze che INIZINO...
foreach (var item in cIobConf.optPar)
{
if (item.Key.StartsWith(keyStartSearch))
{
answ.Add(item.Key, item.Value);
}
}
}
}
return answ;
}
/// <summary>
/// Effettua chiamata x split ODL
/// </summary>
/// <returns></returns>
public bool forceSplitOdl()
{
bool fatto = false;
if (vetoSplit < DateTime.Now)
{
// imposto veto x 1 minuto ad altre chiamate...
vetoSplit = DateTime.Now.AddMinutes(1);
// eseguo SOLO SE sono online...
if (MPOnline && IobOnline)
{
string fullUrl = "";
string rawSplit = "";
string IOB_MULTI_CNAME = "";
string[] elencoMulti = null;
try
{
/***************************************************
* Descrizione procedura (OK X SIMULATORI...)
*
* - chiamata su MP/IO
* - verifica che su DB sia abilitato AUTO ODL
* - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW
* - viene duplicato e chiuso ODL corrente
* - viene fatto partire ODL nuovo ADESSO
* - num pezzi come ODL precedente (o da media 3 ODL precedenti)
* - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL)
* - reset contapezzi PLC locale...
*
*
*
* DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE
* - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI)
* - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!)
* - viene fatto reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...)
* - viene fatto reset + nuovo ODL (con stessi articoli e quantità) su follower, SENZA avere gestione x ODL di un codice esterno (quindi registra TUTTO ma NON RITORNERA' dati non avendo link verso esterno)
* - serve NUOVA TABELLA delle macchine LEADER | FOLLOWER (1:n) e gestione da MP/IO
*
* ***************************************************/
if (isMulti)
{
// devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali...
IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME");
elencoMulti = IOB_MULTI_CNAME.Split(',');
}
// se normale splitto!
if (!isMulti)
{
// invio chiamata URL x reset ODL su macchina
rawSplit = callUrl(urlForceSplit, false);
fatto = (rawSplit != "KO") ? true : false;
}
// se multi gestisco il bit delle tavole...
else
{
foreach (string item in elencoMulti)
{
// invio chiamata URL x reset ODL su macchina, ATTENZIONE scriviamo | al posto di "#" che in URL sarebbe filtrato...
fullUrl = $"{urlForceSplit}|{item}";
rawSplit = callUrl(fullUrl, false);
}
fatto = (rawSplit == "OK") ? true : false;
}
}
catch (Exception exc)
{
lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}");
}
// se fatto --> resetto contapezzi!!!
if (fatto)
{
contapezziPLC = 0;
contapezziIOB = 0;
}
}
else
{
lgError("Richiesto forceSplitOdl ma MP/IOB offline --> NON eseguito");
}
}
else
{
lgError("Richiesto forceSplitOdl ma veto attivo --> NON eseguito");
}
return fatto;
}
/// <summary>
/// effettua recupero dati ed invio valori modificati...
/// </summary>
/// <param name="ciclo"></param>
public void getAndSend(gatherCycle ciclo)
{
// init obj display
newDisplayData currDispData = new newDisplayData();
// IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!!
try
{
trySendValues();
}
catch (Exception exc)
{
lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria");
currDispData.semOut = Semaforo.SR;
}
// controllo connessione/connettività
if (connectionOk)
{
// controllo non sia già in esecuzione...
if (!adpCommAct)
{
// provo ad avviare
try
{
// imposto flag adapter running..
adpCommAct = true;
adpStartRun = DateTime.Now;
}
catch (Exception exc)
{
string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}";
adpCommAct = false;
adpStartRun = DateTime.Now;
currDispData.newLiveLogData = errore;
}
if (adpCommAct)
{
// try / catch generale altrimenti segno che è disconnesso...
try
{
bool showDebugData = false;
if (ciclo == gatherCycle.VHF)
{
processVHF();
}
// processing dati memoria (lettura, filtraggio, enqueque)
else if (ciclo == gatherCycle.HF)
{
processWhatchDog();
processAllMemory();
processMode();
}
else if (ciclo == gatherCycle.MF)
{
processServerRequests();
processOverride();
processContapezzi();
processCncAlarms();
processDynData();
}
else if (ciclo == gatherCycle.LF)
{
processOtherCounters();
processProgram();
// verifico se devo gestire cambio ODL in modo automatico
processAutoOdl();
}
else if (ciclo == gatherCycle.VLF)
{
if (utils.CRB("enableContapezzi"))
{
// rilettura contapezzi da server...
lgInfo("Ciclo VLF: pzCntReload(true)");
if (!isMulti)
{
pzCntReload(true);
}
// refresh associazione Macchina - IOB
sendM2IOB();
}
// recupero dati SETUP (sysinfo) e li invio/mostro se variati...
processSysInfo();
// checkLogDir x shrink!
checkShrinkDir();
// eventuale log!
if (utils.CRB("recTime"))
{
logTimeResults();
}
}
// mostra eventuali altri dati di processo...
reportDataProc();
if (showDebugData)
{
// verifica se debba salvare e mostrare dati
checkSavePersDataLayer();
}
}
catch (Exception exc)
{
// segnalo eccezione e indico disconnesso...
lgError(exc, string.Format("Errore in gestione ciclo principale ADP, fermo adapter{0}{1}", Environment.NewLine, exc));
parentForm.fermaAdapter(true, false, true);
}
// tolgo flag running
adpCommAct = false;
}
else
{
if (periodicLog)
{
lgInfo("ADP not running...");
}
}
}
else
{
// log ADP running
lgError("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
{
// provo a riconnettere SE abilitato tryRestart...
if (adpTryRestart && !connectionOk)
{
// controllo se sia scaduto periodi di veto al tryConnect...
int waitRecMSec = utils.CRI("waitRecMSec") * 2;
DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec);
if (DateTime.Now > dtVeto)
{
lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect");
lastConnectTry = DateTime.Now;
tryConnect();
}
}
currDispData.semIn = Semaforo.SR;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Recupera eventuali allarmi CNC...
/// </summary>
public virtual Dictionary<string, string> getCncAlarms()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
return outVal;
}
/// <summary>
/// Restituisce info DINAMICHE
/// </summary>
/// <returns></returns>
public virtual Dictionary<string, string> getDynData()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
return outVal;
}
/// <summary>
/// Cerca se esiste il parametro opzionale e lo restituisce
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string getOptPar(string key)
{
string answ = "";
if (cIobConf.optPar.Count > 0)
{
// controllo SE HO il parametro
if (cIobConf.optPar.ContainsKey(key))
{
answ = cIobConf.optPar[key];
}
}
return answ;
}
/// <summary>
/// Restituisce info OVERRIDES
/// </summary>
/// <returns></returns>
public virtual Dictionary<string, string> getOverrides()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
return outVal;
}
/// <summary>
/// Restituisce programma in esecuzione
/// </summary>
public virtual string getPrgName()
{
return "";
}
/// <summary>
/// Restituisce info sistema
/// </summary>
/// <returns></returns>
public virtual Dictionary<string, string> getSysInfo()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
return outVal;
}
/// <summary>
/// Recupera la VC x TS, svuotando lista e resettando periodo partenza
/// </summary>
/// <param name="VCName">Nome della VC</param>
/// <param name="doReset">Reimposta e resetta array VC</param>
/// <returns></returns>
public double getVal_TSVC(string VCName, bool doReset)
{
double answ = 0;
// cerco VC...
if (TSVC_Data.ContainsKey(VCName))
{
// !!!FARE!!! vero calcolo... x ora FIX a MAX...
foreach (var item in TSVC_Data[VCName].dataArray)
{
answ = item > answ ? item : answ;
}
// ora resetto... SE richiesto...
if (doReset)
{
TSVC_Data[VCName].dataArray = new List<double>();
TSVC_Data[VCName].DTStart = DateTime.Now;
}
}
return answ;
}
/// <summary>
/// Recupera la VC x TS, svuotando lista e resettando periodo partenza
/// </summary>
/// <param name="VCName">Nome della VC</param>
/// <param name="doReset">Reimposta e resetta array VC</param>
/// <returns></returns>
public int getVal_TSVC_int(string VCName, bool doReset)
{
int answ = 0;
// cerco VC...
if (TSVC_Data.ContainsKey(VCName))
{
// !!!FARE!!! vero calcolo... x ora FIX a MAX...
foreach (var item in TSVC_Data[VCName].dataArray)
{
answ = (int)item > answ ? (int)item : answ;
}
// ora resetto... SE richiesto..
if (doReset)
{
TSVC_Data[VCName].dataArray = new List<double>();
TSVC_Data[VCName].DTStart = DateTime.Now;
}
}
return answ;
}
/// <summary>
/// Restituisce un payload in formato json della lista di valori ricevuta
/// </summary>
/// <param name="tipoUrl">Tipo di URL (eventi / FLog)</param>
/// <param name="elencoValori">elenco di valori da coda string salvata</param>
/// <returns></returns>
public string jsonPayload(urlType tipoUrl, List<string> elencoValori)
{
string answ = "";
if (elencoValori != null)
{
if (tipoUrl == urlType.FLog)
{
flogData currData = new flogData();
flogJsonPayload fullObj = new flogJsonPayload();
fullObj.fluxData = new List<flogData>();
string[] valori;
int counter = 0;
DateTime dtEve = DateTime.Now;
// inizio processando ogni valore
foreach (var item in elencoValori)
{
valori = qDecodeIN(item);
//DateTime.TryParse(valori[0], out dtEve);
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve);
int.TryParse(valori[3], out counter);
currData = new flogData()
{
flux = valori[1],
valore = valori[2],
dtEve = dtEve,
dtCurr = DateTime.Now,
cnt = counter
};
fullObj.fluxData.Add(currData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullObj);
}
catch (Exception exc)
{
lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
}
else
{
evData currData = new evData();
evJsonPayload fullObj = new evJsonPayload();
fullObj.eventList = new List<evData>();
string[] valori;
int counter = 0;
DateTime dtEve = DateTime.Now;
// inizio processando ogni valore
foreach (var item in elencoValori)
{
valori = qDecodeIN(item);
//DateTime.TryParse(valori[0], out dtEve);
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve);
int.TryParse(valori[2], out counter);
currData = new evData()
{
valore = valori[1],
dtEve = dtEve,
dtCurr = DateTime.Now,
cnt = counter
};
fullObj.eventList.Add(currData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullObj);
}
catch (Exception exc)
{
lgError($"Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
}
}
return answ;
}
/// <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)
{
// se num righe superiore a limite trimmo...
if (newString.Split('\n').Length > parentForm.nLine2show)
{
//int idx = newString.LastIndexOf('\r');
int idx = newString.LastIndexOf(Environment.NewLine);
newString = newString.Substring(0, idx);
}
return newString;
}
/// <summary>
/// riporta il log di tutti i dati di results temporali registrati
/// </summary>
public void logTimeResults()
{
if (TimingData.results.Count > 0)
{
lgInfo("{0}--------------- START TIMING DATA ---------------", Environment.NewLine);
int globNumCall = 0;
TimeSpan globAvgMsec = new TimeSpan(0);
foreach (TimeRec item in TimingData.results)
{
// loggo SOLO se del mio IOB corrente...
if (item.classCall == cIobConf.codIOB)
{
lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB);
globNumCall += item.numCall;
globAvgMsec += item.totMsec;
}
}
// riporto conteggio medio al secondo...
lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB);
lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine);
// mostro in form statistiche globali!
parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds));
}
}
/// <summary>
/// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza)
/// </summary>
/// <param name="newVal"></param>
/// <param name="item"></param>
/// <returns></returns>
public bool monItem2Send(string newVal, DynDataItem item)
{
bool answ = false;
if (item != null)
{
// controllo in base al tipo di function...
switch (item.func)
{
case "SAMPLE":
// controllo se scaduto sample period...
if (item.DTScad < DateTime.Now)
{
answ = true;
}
break;
case "CHANGE":
default:
// controllo se scaduto o se variato...
if (newVal != item.actVal || item.DTScad < DateTime.Now)
{
// aggiorno scadenza e che vada inviato
answ = true;
}
break;
}
}
return answ;
}
/// <summary>
/// Verifica e processing x gestione ODL automatica
/// </summary>
public void processAutoOdl()
{
lgInfo("Richiesta processAutoOdl");
bool fatto = false;
if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL")))
{
string IOB_MULTI_CNAME = "";
string[] elencoMulti = null;
string fullUrl = "";
if (isMulti)
{
// devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali...
IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME");
elencoMulti = IOB_MULTI_CNAME.Split(',');
}
// controllo SIA abilitato...
bool doProc = false;
DateTime adesso = DateTime.Now;
bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProc);
if (doProc)
{
lgInfo("AUTO_CHANGE_ODL abilitato");
bool callChangeODL = false;
// modalità change ODL
string CHANGE_ODL_MODE = "TIME";
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
/* verifico se sia "armato" il reset cambio ODL, DEVO essere :
* - NON in produzione
* - contapezzi ACT < contapezzi LAST
* */
lgInfo("processAutoOdl: caso PZCOUNT_RESET");
if (!isRunning && pzCountResetted)
{
callChangeODL = true;
lgInfo("Attivato cambio ODL da PZCOUNT_RESET");
}
else
{
lgInfo($"isRunning: {isRunning} | pzCountResetted: {pzCountResetted} | contapezziIOB: {contapezziIOB} | contapezziPLC: {contapezziPLC}");
}
}
else if (CHANGE_ODL_MODE == "TIME")
{
// carico i parametri di configurazione x reset ODL...
string CHANGE_ODL_HOURS = getOptPar("CHANGE_ODL_HOURS");
string CHANGE_ODL_IDLE_MIN = getOptPar("CHANGE_ODL_IDLE_MIN");
if (!string.IsNullOrEmpty(CHANGE_ODL_HOURS) && !string.IsNullOrEmpty(CHANGE_ODL_IDLE_MIN))
{
int minOdlDurHours = -1;
int minPlcIdelMin = -1;
int.TryParse(CHANGE_ODL_HOURS, out minOdlDurHours);
int.TryParse(CHANGE_ODL_IDLE_MIN, out minPlcIdelMin);
// controllo parametri validi
if (minOdlDurHours > 0 && minPlcIdelMin >= 0)
{
// leggo da server inizio ODL... se non multi 1 solo...
DateTime inizioOdl = DateTime.Now;
string rawDataInizio = "";
if (!isMulti)
{
rawDataInizio = callUrl(urlInizioOdlIob, false);
DateTime.TryParse(rawDataInizio, out inizioOdl);
}
else
{
DateTime tmpData = DateTime.Now;
// prendo il + vecchio...
foreach (var item in elencoMulti)
{
fullUrl = $"{urlInizioOdlIob}|{item}";
rawDataInizio = callUrl(fullUrl, false);
DateTime.TryParse(rawDataInizio, out tmpData);
inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl;
}
}
// verifico se sia scaduto...
if (inizioOdl.AddHours(minOdlDurHours) < adesso)
{
string rawIdle = "";
int idlePeriod = 0;
if (!isMulti)
{
// controllo SE sono fermo (spento o in manuale) per il periodo minimo richiesto...
rawIdle = callUrl(urlIdleTime, false);
int.TryParse(rawIdle, out idlePeriod);
}
else
{
int tmpIdle = 0;
// prendo il + grande...
foreach (var item in elencoMulti)
{
fullUrl = $"{urlIdleTime}|{item}";
rawIdle = callUrl(fullUrl, false);
int.TryParse(rawIdle, out tmpIdle);
idlePeriod = tmpIdle > idlePeriod ? tmpIdle : idlePeriod;
}
}
if (idlePeriod >= minPlcIdelMin)
{
callChangeODL = true;
}
}
}
}
}
if (callChangeODL)
{
lgInfo("Chiamata: processAutoOdl --> forceSplitODL");
fatto = forceSplitOdl();
pzCountResetted = false;
lgInfo("Esecuzione processAutoOdl completata --> pzCountResetted = false");
}
}
else
{
lgInfo("AUTO_CHANGE_ODL DISABILITATO");
}
}
// loggo se fatto
if (fatto)
{
lgInfo("Effettuato processAutoOdl");
}
}
/// <summary>
/// Effettua processing degli allarmi CNC SE disponibili
/// </summary>
public void processCncAlarms()
{
if (utils.CRB("enableAlarms"))
{
Dictionary<string, string> currAlarms = new Dictionary<string, string>();
if (connectionOk)
{
currAlarms = getCncAlarms();
}
else
{
lgError("Errore connessione mancante x getCncAlarms");
}
// verifico SE sia cambiato il programma...
if (currAlarms.Count > 0)
{
try
{
if (lastAlarm != currAlarms["CNC_ALARM"])
{
// salvo!
lastAlarm = currAlarms["CNC_ALARM"];
// per ogni valore del dizionario mostro ed accodo!
string sVal = "";
foreach (var item in currAlarms)
{
sVal = string.Format("[CNC_ALARM]{0}|{1}", item.Key, item.Value);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog(item.Key, item.Value));
}
}
}
catch (Exception exc)
{
lgError("Eccezione in processCncAlarms{0}{1}", Environment.NewLine, exc);
}
}
}
}
/// <summary>
/// Effettua processing contapezzi (ed eventualmente alza il bit di contapezzo...)
/// </summary>
public virtual void processContapezzi()
{ }
/// <summary>
/// Effettua processing del recupero delle speed (RPM, feedrate) degli assi
/// </summary>
public void processDynData()
{
bool enableByApp = utils.CRB("enableDynData");
bool enableByIob = (getOptPar("ENABLE_DYN_DATA") == "TRUE");
bool forceSendByIob = (getOptPar("FORCE_DYN_DATA") == "TRUE");
Dictionary<string, string> currDynData = new Dictionary<string, string>();
if (enableByApp || enableByIob)
{
lgInfo("Inizio processDynData");
if (connectionOk)
{
currDynData = getDynData();
}
else
{
lgError("Errore connessione mancante x getDynData");
}
try
{
string sVal = "";
// se richiesto send diretto...
if (forceSendByIob)
{
// per ogni valore del dizionario mostro ed accodo!
foreach (var item in currDynData)
{
sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog(item.Key, item.Value));
}
}
// altrimenti verifico SE sia cambiato il valore dei DynData...
else if (lastDynDataCtrlVal != currDynData["DYNDATA"])
{
// salvo!
lastDynDataCtrlVal = currDynData["DYNDATA"];
// per ogni valore del dizionario mostro ed accodo!
foreach (var item in currDynData)
{
sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog(item.Key, item.Value));
}
}
// salvo array...
lastDynData = currDynData;
}
catch (Exception exc)
{
lgError(exc, "Eccezione in processDynData");
}
}
}
/// <summary>
/// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC...
/// </summary>
public void processMemoryDiscon()
{
// init obj display
newDisplayData currDispData = new newDisplayData();
// controllo contatore invio "keepalive"... invio solo a scadenza
if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec"))
{
// resetto tutti i vlaori BYTE IN/PREV/OUT... così invio macchina spenta...
B_input = 0;
B_output = 0;
B_previous = 0;
accodaSigIN(ref currDispData);
// update controllo
lastDisconnCheck = DateTime.Now;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua processing mode/status (EDIT/MDI/...)
/// </summary>
public virtual void processMode()
{ }
/// <summary>
/// Effettua processing ALTRI contatori/parametri (ed invia ad IO)
/// </summary>
public virtual void processOtherCounters()
{ }
/// <summary>
/// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid)
/// </summary>
public virtual void processOverride()
{
bool enableByApp = utils.CRB("enableOverrides");
bool enableByIob = (getOptPar("ENABLE_OVERRIDES") == "TRUE");
Dictionary<string, string> currOverride = new Dictionary<string, string>();
if (enableByApp || enableByIob)
{
lgInfo("Inizio processOverride");
if (connectionOk)
{
currOverride = getOverrides();
}
else
{
lgError("Errore connessione mancante x getOverrides");
}
// SE sono connesso...
if (connectionOk)
{
// se HO dei valori override...
if (currOverride.Count > 0)
{
// verifico SE sia cambiato il programma...
if (lastOverrideFS != currOverride["FEED_OVER"] || lastOverrideRapid != currOverride["RAPID_OVER"])
{
// salvo!
lastOverrideFS = currOverride["FEED_OVER"];
lastOverrideRapid = currOverride["RAPID_OVER"];
// per ogni valore del dizionario mostro ed accodo!
string sVal = "";
foreach (var item in currOverride)
{
sVal = string.Format("[OVERRIDES]{0}|{1}", item.Key, item.Value);
// chiamo accodamento...
accodaFLog(sVal, qEncodeFLog(item.Key, item.Value));
}
}
}
}
}
}
/// <summary>
/// Effettua ciclo controllo richieste server
/// </summary>
public void processServerRequests()
{
Dictionary<string, string> task2exe = new Dictionary<string, string>();
Dictionary<string, string> taskDone = new Dictionary<string, string>();
// recupero elenco delle cose da fare
string resp = getTask2exe();
if (!string.IsNullOrEmpty(resp))
{
try
{
task2exe = JsonConvert.DeserializeObject<Dictionary<string, string>>(resp);
// se ho da fare chiamo esecuzione..
if (task2exe.Count > 0)
{
taskDone = processTask(task2exe);
}
}
catch (Exception exc)
{
lgError($"Eccezione in processServerRequests:{Environment.NewLine}{exc}");
}
}
}
public Dictionary<string, string> processTask(Dictionary<string, string> task2exe)
{
Dictionary<string, string> taskDone = new Dictionary<string, string>();
if (task2exe != null)
{
lgInfo($"Task2Exe: trovati {task2exe.Count} task da eseguire, procedo");
// chiamo procedura esecutiva (diversa x ogni IOB)
taskDone = executeTasks(task2exe);
// loggo tutti i task done...
foreach (var item in taskDone)
{
sendToTaskWatch(item.Key, item.Value);
}
// ora chiamo la cancellazione dei task eseguiti...
foreach (var item in taskDone)
{
remTask2exe(item.Key, item.Value);
}
}
return taskDone;
}
/// <summary>
/// Classe fittizia in caso di processing task in VHF
/// </summary>
public virtual void processVHF()
{
}
/// <summary>
/// Classe fittizia in caso di processing watchdog data
/// </summary>
public virtual void processWhatchDog()
{
}
/// <summary>
/// Effettua rilettura del contapezzi dal server MP/IO
/// </summary>
/// <param name="forceCountRec">Forza rilettura da DB tempi ciclo rilevati</param>
public void pzCntReload(bool forceCountRec)
{
// legge da IO server ULTIMO valore CONTPEZZI al riavvio...
string currServerCount = "";
string lastIdxODL = "";
if (checkServerAlive)
{
// leggo PRIMA ODL ....
lastIdxODL = utils.callUrl(urlGetCurrODL);
lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL);
// se ho valori in coda da trasmettere uso dati REDIS
if (forceCountRec)
{
// uso dati da TCiclo registrati...
currServerCount = utils.callUrl(urlGetPzCountRec);
lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount);
}
else
{
// uso il contapezzi dichiarato dall'IOB stesso
currServerCount = utils.callUrl(urlGetPzCount);
lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount);
}
// controllo: SE NON HO ODL...
if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0")
{
// NON AGGIORNO
contapezziIOB = contapezziPLC;
lgInfo($"Errore lettura ODL (vuoto) resta tutto invariato contapezzi: {contapezziIOB} | contapezziPLC {contapezziPLC}");
}
else
{
if (!string.IsNullOrEmpty(currServerCount))
{
// se "-1" resto a ultimo...
if (currServerCount != "-1")
{
int newVal = -1;
Int32.TryParse(currServerCount, out newVal);
contapezziIOB = newVal > -1 ? newVal : contapezziIOB;
lgInfo("Ricevuta conferma da server di {0} pezzi registrati per ODL", currServerCount);
}
else
{
// NON AGGIORNO
contapezziIOB = contapezziPLC;
lgInfo($"Errore lettura contapezzi (-1) - uso contapezziPLC --> {contapezziPLC}");
}
}
else
{
// registro che ho UN NUOVO ODL
lgInfo($"Lettura ODL in pzCntReload, currIdxODL {currIdxODL} --> lastIdxODL {lastIdxODL}");
// se ODL differente e NUOVO è zero --> resetto!
if (currIdxODL.ToString() != lastIdxODL && lastIdxODL == "0")
{
// cambiato ODL quindi reset...
contapezziIOB = 0;
lgInfo("Nuovo ODL==0, RESET contapezzi (-->ZERO)");
}
// provo a salvare nuovo ODL
int.TryParse(lastIdxODL, out currIdxODL);
}
}
}
else
{
// se server NON pronto...
contapezziIOB = contapezziPLC;
lgError("Errore server NON pronto in pzCntReload");
}
}
/// <summary>
/// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont
/// <paramref name="flusso">Flusso dati</paramref>
/// <paramref name="valore">Valore da salvare</paramref>
/// </summary>
public string qEncodeFLog(string flusso, string valore)
{
string answ = "";
try
{
answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", DateTime.Now, flusso, valore, counterFLog);
}
catch
{ }
return answ;
}
/// <summary>
/// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato dtEve#flux#value#cont
/// <paramref name="eventDT">DataOra evento registrato</paramref>
/// <paramref name="flusso">Flusso dati</paramref>
/// <paramref name="valore">Valore da salvare</paramref>
/// </summary>
public string qEncodeFLog(DateTime eventDT, string flusso, string valore)
{
string answ = "";
try
{
answ = string.Format("{0:yyyyMMddHHmmssfff}#{1}#{2}#{3}", eventDT, flusso, valore, counterFLog);
}
catch
{ }
return answ;
}
/// <summary>
/// Effettua lettura dati
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
/// </summary>
public virtual void readAllData(ref newDisplayData currDispData)
{
if (currDispData == null)
{
currDispData = new newDisplayData();
}
try
{
if (DemoIn)
{
// segnalo che sono in Demo
currDispData.semIn = Semaforo.SV;
}
if (connectionOk)
{
readSemafori(ref currDispData);
lastReadPLC = DateTime.Now;
}
else
{
lgError("Errore connessione mancante x readSemafori");
}
nReadIN++;
// aggiorno valore mostrato...
displayRawData(ref currDispData);
}
catch
{
currDispData.semIn = Semaforo.SR;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua lettura semafori principale
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
/// </summary>
public virtual void readSemafori(ref newDisplayData currDispData)
{
lastReadPLC = DateTime.Now;
}
/// <summary>
/// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati
/// </summary>
public virtual void reportRawInput(ref newDisplayData currDispData)
{
// processo eventualmente aggiungendo ad elementi esistenti...
if (currDispData == null)
{
currDispData = new newDisplayData();
}
try
{
StringBuilder sb = new StringBuilder();
sb.Append($"B_input --> {(short)B_input}{Environment.NewLine}");
sb.Append($"{baseUtils.binaryForm(B_input)}{Environment.NewLine}");
sb.Append($"{Environment.NewLine}");
sb.Append($"----------- RAW Data Memory -----------{Environment.NewLine}");
int i = 0;
foreach (var item in RawInput)
{
sb.Append($"B{i:00} --> {baseUtils.binaryForm(item)} = {(short)item}{Environment.NewLine}");
i++;
}
sb.Append("-------------------------------");
currDispData.currBitmap = sb.ToString();
}
catch
{ }
}
/// <summary>
/// Metodo generico di reset contapezzi...
/// </summary>
/// <returns></returns>
public virtual bool resetcontapezziPLC()
{
return false;
}
/// <summary>
/// metodo dummy x salvataggio aree memoria conf x CN
/// </summary>
/// <param name="tipo">tipo di DUMP</param>
public virtual void saveMemDump(dumpType tipo)
{
}
/// <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>
///
/// </summary>
/// <param name="outVal"></param>
/// <param name="valore"></param>
/// <param name="chiave"></param>
/// <returns></returns>
public void saveValue(ref Dictionary<string, string> outVal, double valore, string chiave)
{
//check obj preliminare
if (outVal == null)
{
outVal = new Dictionary<string, string>();
}
bool scaduto = stackVal_TSVC(chiave, valore);
// recupero VC
valore = getVal_TSVC(chiave, scaduto);
if (scaduto)
{
outVal.Add(chiave, $"{valore}");
}
LastTSVC[chiave] = valore;
}
/// <summary>
/// Invia una LISTA di valori
/// </summary>
/// <param name="tipoUrl"></param>
/// <param name="queueVal"></param>
public void sendDataBlock(urlType tipoUrl, List<string> listQueueVal)
{
// init obj display
newDisplayData currDispData = new newDisplayData();
if (listQueueVal != null)
{
try
{
// recupero e formatto URL dati da coda...
lastUrl = urlDataBlock(tipoUrl);
// in base al tipo di dato compongo il payload Json da inviare
string payload = jsonPayload(tipoUrl, listQueueVal);
// async a true SE FLog
bool doAsync = tipoUrl == urlType.FLog ? true : false;
// se NON sono in demo effettuo invio!
if (!DemoOut)
{
// SE server alive...
if (checkServerAlive)
{
// chiamo URL!
string answ = callUrlWithPayload(lastUrl, payload, doAsync);
// loggo!
lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}");
// se "OK" verde, altrimenti errore --> ROSSO
if (answ.Contains("OK"))
{
currDispData.semOut = Semaforo.SV;
// se oltre 1 min NON era online --> check pezzi!
if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti)
{
lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)");
pzCntReload(true);
}
lastIobOnline = DateTime.Now;
}
else
{
currDispData.semOut = Semaforo.SR;
}
}
else
{
lgInfo($"[SERVER KO] {listQueueVal.Count}");
}
}
else
{
currDispData.semOut = Semaforo.SV;
// loggo!
lgInfo($"{listQueueVal.Count} records --> [SIM]");
}
nSendOut += listQueueVal.Count;
// riporto cosa inviato
currDispData.newUrlCallData = lastUrl;
// aggiorno data ultimo watchdog...
lastWatchDog = DateTime.Now;
}
catch
{
currDispData.semOut = Semaforo.SR;
}
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua invio a MoonPro del valore richiesto
/// </summary>
/// <param name="tipoUrl"></param>
/// <param name="queueVal">Valore da trasmettere: es
/// INPUT: lo status rilevato in HEX
/// FLog: il valore da trasmettere per il flusso indicato</param>
public void sendToMoonPro(urlType tipoUrl, string queueVal)
{
// init obj display
newDisplayData currDispData = new newDisplayData();
try
{
// recupero e formatto URL dati da coda...
switch (tipoUrl)
{
case urlType.FLog:
lastUrl = urlFLog(queueVal);
break;
case urlType.SignIN:
lastUrl = urlInput(queueVal);
break;
default:
lastUrl = "";
break;
}
// se NON sono in demo effettuo invio!
if (!DemoOut)
{
// SE server alive...
if (checkServerAlive)
{
// chiamo URL!
string answ = callUrl(lastUrl, false);
// loggo!
lgInfo(string.Format("[SEND] {0} -> {1}", queueVal, answ));
// se oltre 1 min NON era online --> check pezzi!
if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti)
{
lgInfo($"sendToMoonPro --> offline timeaout ({lastIobOnline}) --> pzCntReload(true)");
pzCntReload(true);
}
// se richiesto effettuo refresh contapezzi
if (needRefreshPzCount && !isMulti)
{
pzCntReload(true);
needRefreshPzCount = false;
}
lastIobOnline = DateTime.Now;
// se "OK" verde, altrimenti errore --> ROSSO
if (answ == "OK")
{
currDispData.semOut = Semaforo.SV;
}
else
{
currDispData.semOut = Semaforo.SR;
}
}
else
{
lgInfo(string.Format("[SERVER KO] {0}", queueVal));
}
}
else
{
currDispData.semOut = Semaforo.SV;
// loggo!
lgInfo(string.Format("{0} -> [SIM]", queueVal));
}
nSendOut++;
// riporto cosa inviato
#if false
displayOutData();
#endif
currDispData.newUrlCallData = lastUrl;
// aggiorno data ultimo watchdog...
lastWatchDog = DateTime.Now;
}
catch
{
currDispData.semOut = Semaforo.SR;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi...
/// </summary>
/// <param name="newPzCount">Pezzi richiesti</param>
/// <returns></returns>
public virtual bool setcontapezziPLC(int newPzCount)
{
return false;
}
/// <summary>
/// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e restituisce bool val se SCADUTO periodo controllo
/// </summary>
/// <param name="VCName">Nome della VC</param>
/// <param name="VCVal">Valore (nuovo) delal VC</param>
/// <returns></returns>
public bool stackVal_TSVC(string VCName, double VCVal)
{
bool answ = false;
// cerco VC...
if (TSVC_Data.ContainsKey(VCName))
{
TSVC_Data[VCName].dataArray.Add(VCVal);
// ora verifico scadenza...
if (TSVC_Data[VCName].DTStart.AddSeconds(TSVC_Data[VCName].Period) < DateTime.Now)
{
answ = true;
}
}
return answ;
}
/// <summary>
/// Avvia l'adapter sulla porta richiesta
/// </summary>
/// <param name="resetQueue">indica se sia richeisto di SVUOTARE le code delel info</param>
public virtual void startAdapter(bool resetQueue)
{
lgInfo("Starting adapter...");
maxJsonData = utils.CRI("maxJsonData");
maxJsonDataEv = utils.CRI("maxJsonDataEv");
parentForm.commPlcActive = false;
adpRunning = true;
dtAvvioAdp = DateTime.Now;
lastWatchDog = dtAvvioAdp;
lastPING = dtAvvioAdp;
lastReadPLC = dtAvvioAdp.AddMinutes(-1);
lastDisconnCheck = dtAvvioAdp;
TimingData.resetData();
// aggiungo altri defaults
setDefaults(resetQueue);
adpTryRestart = true;
parentForm.displayTaskAndLog("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>
/// <param name="forceDequeue">indica se sia richeisto di SVUOTARE le code delel info</param>
public virtual void stopAdapter(bool tryRestart, bool forceDequeue)
{
if (forceDequeue)
{
// svuoto le code dei valori letti e non ancora trasmessi...
parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda segnali...");
while (QueueIN.Count > 0)
{
// INVIO COMUNQUE...!!!
string valore = "";
QueueIN.TryDequeue(out valore);
sendToMoonPro(urlType.SignIN, valore);
}
parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda FluxLOG...");
// se ho + di 2 elementi in coda --> uso invio JSON in blocco...
if (QueueFLog.Count > minJsonData)
{
while (QueueFLog.Count > 0)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueFLog.Count > maxJsonData)
{
string currVal = "";
// prendoi primi maxJsonDataValori
for (int i = 0; i < maxJsonData; i++)
{
QueueFLog.TryDequeue(out currVal);
listaValori.Add(currVal);
}
sendDataBlock(urlType.FLog, listaValori);
}
else
{
// invio in blocco
listaValori = QueueFLog.ToList();
// invio
sendDataBlock(urlType.FLog, listaValori);
// svuoto!
QueueFLog = new ConcurrentQueue<string>();
}
}
// HO FINITO invio di FLog...
}
else
{
string currVal = "";
while (QueueFLog.Count > 0)
{
// INVIO COMUNQUE...!!!
QueueFLog.TryDequeue(out currVal);
sendToMoonPro(urlType.FLog, currVal);
}
}
}
parentForm.displayTaskAndLog("[STOP] Stopping adapter...");
adpTryRestart = false;
parentForm.displayTaskAndLog("[STOP] Stopping adapter - last periodic data read...");
// chiudo la connessione all'adapter...
tryDisconnect();
dtStopAdp = DateTime.Now;
adpTryRestart = tryRestart;
adpRunning = false;
// chiudo!
parentForm.displayTaskAndLog("Adapter Stopped.");
parentForm.commPlcActive = false;
}
/// <summary>
/// Processo la coda SignalIN...
/// </summary>
public void svuotaCodaSignIN()
{
// verifico SE la coda abbia dei valori...
if (QueueIN.Count > 0)
{
// invio pacchetto di dati (max da conf)
for (int i = 0; i < nMaxSend; i++)
{
if (QueueIN.Count > 0)
{
string currVal = "";
// se online provo
if (MPOnline)
{
if (IobOnline)
{
// se ho + di 2 elementi in coda --> uso invio JSON in blocco...
if (QueueIN.Count > 1)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueIN.Count > maxJsonDataEv)
{
// prendoi primi maxJsonDataValori
for (int j = 0; j < maxJsonDataEv; j++)
{
QueueIN.TryDequeue(out currVal);
listaValori.Add(currVal);
}
sendDataBlock(urlType.SignIN, listaValori);
}
else
{
// invio in blocco
listaValori = QueueIN.ToList();
// invio
sendDataBlock(urlType.SignIN, listaValori);
// svuoto!
QueueIN = new ConcurrentQueue<string>();
}
}
else
{
// INVIO SINGOLO...!!!
QueueIN.TryDequeue(out currVal);
sendToMoonPro(urlType.SignIN, currVal);
}
// salvo come last signal in il currVal ultimo... SE !=""
if (!string.IsNullOrEmpty(currVal))
{
lastSignInVal = currVal;
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
}
/// <summary>
/// Metodo base connessione...
/// </summary>
public virtual void tryConnect()
{
dtAvvioAdp = DateTime.Now;
}
/// <summary>
/// Metodo base disconnessione...
/// </summary>
public virtual void tryDisconnect()
{
}
/// <summary>
/// Effettua verifica se abilitato invio pezzi in blocco e nel caso
/// - invio in blocco pezzi
/// - aggiornamento del contapezzi (passato come ref) x nuovo valore post invio
/// </summary>
public void trySendPzCountBlock()
{
// in primis HA SENSO procedere SOLO SE server MP è Online...
if (MPOnline)
{
// SOLO SE online la macchina...
if (IobOnline)
{
int numIncr = 0;
int qtyAdded = 0;
// verifico se la funzione SIA abilitata
if (enableSendPzCountBlock)
{
int delta = contapezziPLC - contapezziIOB;
// se è abilitata verifico differenza: se ho DELTA > minSendPzCountBlock --> invio un blocco <= maxSendPzCountBlock
if (delta > minSendPzCountBlock)
{
// init obj display
newDisplayData currDispData = new newDisplayData();
// resta indietro di ALMENO minSendPzCountBlock pezzi x recuperare 1:1...
numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock;
// invio il num max di pezzi ammesso in blocco!
lastUrl = $"{urlAddPzCount}{numIncr}";
string resp = utils.callUrlNow(lastUrl);
if (!string.IsNullOrEmpty(resp))
{
// dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti)
int.TryParse(resp, out qtyAdded);
if (qtyAdded > 0)
{
// aggiorno IL MIO contapezzi...
contapezziIOB += qtyAdded;
lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}");
// invio conferma contapezzi..
string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}");
// verifica se tutto OK
if (retVal != contapezziIOB.ToString())
{
// errore salvataggio contapezzi
lgInfo($"trySendPzCountBlock: errore salvataggio contapezzi: contapezziIOB {contapezziIOB} | risposta: {retVal}");
}
}
else
{
lgError($"Richiesto incremento {numIncr} ma NON registrato su server MP-IO");
}
}
currDispData.newUrlCallData = lastUrl;
currDispData.counter = contapezziIOB;
currDispData.semOut = Semaforo.SV;
raiseRefresh(currDispData);
}
}
}
else
{
lgInfo("Impossibile trySendPzCountBlock: IobOnline è false");
}
}
else
{
lgInfo("Impossibile trySendPzCountBlock: MPOnline è false");
}
}
/// <summary>
/// Formatta URL x invio in DataBlock Json dei dati FLog / eventi
/// </summary>
/// <param name="tipoUrl"></param>
/// <returns></returns>
public string urlDataBlock(urlType tipoUrl)
{
// verifico la parte di link "tipoComando"
string tipoComando = tipoUrl == urlType.FLog ? cIobConf.serverData.CMDFLOG_JSON : cIobConf.serverData.CMDBASE_JSON;
// URL base x input
string answ = string.Format(@"http://{0}{1}{2}{3}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, tipoComando, cIobConf.codIOB);
return answ;
}
/// <summary>
/// Fornisce URL di tipo FluxLog
/// </summary>
/// <param name="queueVal">valore salvato in coda nel formato dtEve#flux#valore#counter</param>
/// <returns></returns>
public string urlFLog(string queueVal)
{
// URL base x input
string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDFLOG);
// decodifica valore!
string[] valori = qDecodeIN(queueVal);
// aggiungo macchina e valore...
answ += string.Format(@"{0}?flux={1}&&valore={2}", cIobConf.codIOB, valori[1], valori[2]);
// aggiondo dataOra evento e corrente + contatore...
answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[3]);
return answ;
}
/// <summary>
/// Fornisce URL INPUT per i parametri richiesti
/// </summary>
/// <param name="queueVal">valore salvato in coda formato dtEve#valore#counter</param>
/// <returns></returns>
public string urlInput(string queueVal)
{
// URL base x input
string answ = string.Format(@"http://{0}{1}{2}", cIobConf.serverData.MPIP, cIobConf.serverData.MPURL, cIobConf.serverData.CMDBASE);
// decodifica valore!
string[] valori = qDecodeIN(queueVal);
// aggiungo macchina e valore...
answ += string.Format(@"{0}?valore={1}", cIobConf.codIOB, valori[1]);
// aggiondo dataOra evento e corrente + contatore...
answ += string.Format(@"&&dtEve={0}&&dtCurr={1:yyyyMMddHHmmssfff}&&cnt={2}", valori[0], DateTime.Now, valori[2]);
return answ;
}
#endregion Public Methods
}
/// <summary>
/// Evento per incapsulare dati x refresh pagina
/// </summary>
public class iobRefreshedEventArgs : EventArgs
{
#region Private Fields
/// <summary>
/// classe obj privata
/// </summary>
private readonly newDisplayData _newDisplayData;
#endregion Private Fields
#region Public Constructors
/// <summary>
/// salvataggio obj
/// </summary>
/// <param name="newObject"></param>
public iobRefreshedEventArgs(newDisplayData newObject)
{
_newDisplayData = newObject;
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Proprietà lettura displayData aggiornato
/// </summary>
public newDisplayData DisplayDataObject
{
get { return _newDisplayData; }
}
#endregion Public Properties
}
}