Files
Mapo-IOB-WIN/IOB-WIN-NEXT/Iob/Generic.cs
T
Samuele Locatelli 530fa4790f Mitsubishi
- OK SEmaforo
- OK contapezzi
2024-11-19 17:32:20 +01:00

8728 lines
342 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using EgwProxy.Ftp;
using EgwProxy.MultiCncLib.App.Native;
using IOB_UT_NEXT;
using MapoSDK;
using MathNet.Numerics.Statistics;
using MTConnect;
using MTConnect.Clients;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections;
using System.Collections.Generic;
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;
using System.Xml.Serialization;
using static IOB_UT_NEXT.BaseAlarmConf;
using static IOB_UT_NEXT.CustomObj;
using static IOB_UT_NEXT.DataModel.Fimat;
using static MapoSDK.WharehouseData;
namespace IOB_WIN_NEXT.Iob
{
public class Generic : BaseObj
{
#region Public Fields
public int numPzReqOdl = 0;
#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 Generic(AdapterForm caller, IobConfiguration IOBConf)
{
if (IOBConf != null)
{
// init oggetto redis...
redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.filenameIOB, $"{IOBConf.tipoIob}", IOBConf.minDeltaSec);
//redisMan = new RedisIobCache(IOBConf.serverData.MPIP, IOBConf.codIOB, $"{IOBConf.tipoIob}", IOBConf.minDeltaSec);
// initi oggetto TCMan
tcMan = new TCMan(IOBConf.TCLambda, IOBConf.TCMaxDelayFactor, IOBConf.TCMaxIncrPz);
// salvo il form chiamante
parentForm = caller;
// configurazione...
cIobConf = IOBConf;
// imposto le code!
QueueIN = new DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue);
lastConnectTry = DateTime.Now;
// aggiungo nel logger IDX Macchina
lg = LogManager.GetCurrentClassLogger();
lgInfo("Avvio preliminare AdapterGeneric");
lastLogStartup = DateTime.Now;
// setup currProdData & last prod data
currProdData = redisMan.redGetHashDict(rKeyCurrProdData);
// aggiungo altri defaults
setDefaults(true);
// imposta valori memoria (e resetta parametri su server)
setParamPlc();
// checkLogDir x shrink!
checkShrinkDir();
// imposto veto invio per i prossimi sec
dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn);
lgInfoStartup($"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}");
// invio info IOB
SendM2IOB();
// invio altri dati accessori...
SendMachineConf();
// concluso!
lgInfoStartup("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 Public Properties
/// <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 = true;
}
else
{
IPStatus pingStatus = testPingServer;
// se passa il ping faccio il resto...
if (pingStatus == IPStatus.Success)
{
string callResp = "";
try
{
// chiamo URL, se restituisce "OK" è alive! provo con chiamata http
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 * 5);
}
else
{
lgError("SERVER OFFLINE in checkServerAlive");
parentForm.commSrvActive = 0;
}
// salvo nuovo status...
MPOnline = answ;
}
else
{
// allungo periodo controllo...
dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20);
}
}
else
{
lgError($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})");
MPOnline = false;
// imposto veto a 10 volte reinvio dati standard...
dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10);
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 RawTransf
/// </summary>
public int counterRawTransf { get; set; }
/// <summary>
/// Contatore x invio dati SignalIN
/// </summary>
public int counterSigIN { get; set; }
/// <summary>
/// Contatore x invio dati UserLog
/// </summary>
public int counterULog { get; set; }
/// <summary>
/// Verifica se sia in modalità DEMO --&gt; da tipo IOB SIMULA...
/// </summary>
public bool DemoIn
{
get
{
return (cIobConf.tipoIob == tipoAdapter.SIMULA);// baseUtils.CRB("DemoIn");
}
}
/// <summary>
/// Indica se la chiamata WDST dit racking watchdog sia disabilitata dall'invio nel FluxLog
/// </summary>
public bool disableWdst { get; set; } = false;
/// <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 chiave...
if (cIobConf.optPar.ContainsKey(keyName))
{
string SIM_MULTI = getOptPar(keyName);
answ = SIM_MULTI == "1";
}
}
return answ;
}
}
/// <summary>
/// Log verboso da configurazione (SOLO CHIAVE "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% --&gt; 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 chiave...
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>
/// Finestra dei byte da mostrare di default x il RawDataInput
/// </summary>
public int RawDataInputSize { get; set; } = 8;
/// <summary>
/// Indice di partenza della memoria RawData Input da mostrare
/// </summary>
public int RawDataInputStart { get; set; } = 0;
/// <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 = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/savePzCountInc/{cIobConf.codIOB}?qty=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlAddPzCount");
}
return answ;
}
}
/// <summary>
/// URL per INVIO IN BLOCCO di un INCREMENTO x contapezzi (quelli della macchina: PLC/CNC)
/// in una data SPECIFICA
/// </summary>
public string urlAddPzCountAtDate
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/savePzCountIncAtDate/{cIobConf.codIOB}?qty=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlAddPzCount");
}
return answ;
}
}
/// <summary>
/// URL per check alive...
/// </summary>
public string urlAlive
{
get
{
return $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}";
}
}
/// <summary>
/// URL per creazione di un PODL da cod art (metodo GET)...
/// </summary>
public string urlCreatePOdl
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/forceCreatePOdl/{cIobConf.codIOB}?";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlStartPODL");
}
return answ;
}
}
/// <summary>
/// URL per chiamata generazione Snapshot Dossier giornalieri alla data
/// </summary>
public string urlFixDailyDossier
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/fixDailyDossier/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlFixDailyDossier");
}
return answ;
}
}
/// <summary>
/// URL per chiamata generazione ODL giornalieri alla data
/// </summary>
public string urlFixDailyOdl
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/fixDailyOdl/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlFixDailyOdl");
}
return answ;
}
}
/// <summary>
/// URL per chiamata generazione ODL giornalieri alla data + conferma PzCount ad ogni step
/// </summary>
public string urlFixDailyOdlConfPzCount
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/fixDailyOdlConfPzCount/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlFixDailyOdlConfPzCount");
}
return answ;
}
}
/// <summary>
/// URL per forzare split ODL...
/// </summary>
public string urlForceSplit
{
get
{
string answ = $"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMD_FORCLE_SPLIT_ODL}{cIobConf.codIOB}?doConfirm=true&qtyFromLast=true&roundStep=500";
// se richiesto inviare Key Richiesta --> accodo!
if (sendKeyRichiesta)
{
answ += $"&keyRichiesta={nextKeyRich}";
}
return answ;
}
}
/// <summary>
/// URL per recupero ARTICOLI correnti x macchina...
/// </summary>
public string urlGetCurrArt
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getLastArtByMacc/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetCurrArt");
}
return answ;
}
}
/// <summary>
/// URL per recupero DOSS correnti x macchina...
/// </summary>
public string urlGetCurrDOSS
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getLastDossByMacc/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetCurrDOSS");
}
return answ;
}
}
/// <summary>
/// URL per recupero dati ODL corrente...
/// </summary>
public string urlGetCurrODL
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrODL/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetCurrODL");
}
return answ;
}
}
/// <summary>
/// URL per recupero PODL correnti x macchina...
/// </summary>
public string urlGetCurrPODL
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrPODL/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetCurrPODL");
}
return answ;
}
}
/// <summary>
/// URL per recupero ListValue tipo fasi...
/// </summary>
public string urlGetListValFasiPodl
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getListValByTable/PODL";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetListValFasiPodl");
}
return answ;
}
}
/// <summary>
/// URL per recupero traduzione CodArt in numerico...
/// </summary>
public string urlGetNumArt
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getArtNum/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetNumArt");
}
return answ;
}
}
/// <summary>
/// URL per recupero traduzione CodComm in numerico...
/// </summary>
public string urlGetNumComm
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getXdlNum/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetNumComm");
}
return answ;
}
}
/// <summary>
/// URL per recupero num pezzi ODL corrente...
/// </summary>
public string urlGetNumPzCurrODL
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getCurrOdlQtaReq/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlGetNumPzCurrODL");
}
return answ;
}
}
/// <summary>
/// URL per richiamo parametri da scrivere...
/// </summary>
public string urlGetParams2Write
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getObjItems2Write/{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 = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getTask2Exe/{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 $@"{cIobConf.serverData.TRANSP}://{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 $@"{cIobConf.serverData.TRANSP}://{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 $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDENABLED}{cIobConf.codIOB}";
}
}
/// <summary>
/// URL per richiesta chiusura manuale ad utente x ODL corrente (metodo GET)...
/// </summary>
public string urlODLAskClose
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/askCloseODL/{cIobConf.codIOB}?idxOdl=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlODLClose");
}
return answ;
}
}
/// <summary>
/// URL per chiusura ODL corrente (metodo GET)...
/// </summary>
public string urlODLClose
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/closeODL/{cIobConf.codIOB}?idxOdl=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlODLClose");
}
return answ;
}
}
/// <summary>
/// URL per AVVIO di un ODL da PODL indicato (metodo GET)...
/// </summary>
public string urlOdlStartFromPOdl
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/forceStartPOdl/{cIobConf.codIOB}?idxPODL=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlStartPODL");
}
return answ;
}
}
/// <summary>
/// URL per chiusura PODL --&gt; ODL corrente (metodo GET)...
/// </summary>
public string urlPODLClose
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/closePODL/{cIobConf.codIOB}?idxPOdl=";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlPODLClose");
}
return answ;
}
}
/// <summary>
/// URL per segnalazione reboot...
/// </summary>
public string urlReboot
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDREBO}{cIobConf.codIOB}&mac={GetMACAddress()}";
}
catch
{
answ = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/remTask2Exe/{cIobConf.codIOB}?taskName=";
}
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;
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setObjItems/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveMemConf");
}
return answ;
}
}
/// <summary>
/// URL per invio MachineIobConf info
/// </summary>
public string urlSaveMachIobConf
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveMachineIobConf/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetHashDict");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio dati conf memoria IOB...
/// </summary>
public string urlSaveMemMap
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveConf/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSaveMemConf");
}
return answ;
}
}
/// <summary>
/// URL per INVIO di un update dello status di un certo allarme
/// </summary>
public string urlSendAlarm
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/sendAlarmBankUpdate/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlAddPzCount");
}
return answ;
}
}
/// <summary>
/// URL per invio info HASH gestione recipes
/// </summary>
public string urlSetHashDict
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setRedisHashDict/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlSetHashDict");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio dati associazione Machine 2 IOB...
/// </summary>
public string urlSetM2IOB
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/setM2IOB/{cIobConf.codIOB}?IOB_name={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 = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/addOptPar/{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 = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{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 effettuare salvataggio parametri (snapshot) macchina...
/// </summary>
public string urlTakeSnapshot
{
get
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/takeFlogSnapshot/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlTakeSnapshot");
}
return answ;
}
}
/// <summary>
/// URL per salvataggio in UPSERT dei PARAMETRI IOB scritti...
/// </summary>
public string urlUpdateWriteParams
{
get
{
string answ = "";
try
{
string machineName = Environment.MachineName;
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/upsertObjItems/{cIobConf.codIOB}";
}
catch (Exception exc)
{
lgError(exc, "Errore in composizione urlUpdateWriteParams");
}
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 Public Methods
/// <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 da coda FLog per superamento maxLengh: {currVal}");
}
}
}
// loggo!
lgTrace(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
parentForm.WriteTextSafe(newLine);
}
/// <summary>
/// Accumula in coda i valori RawData + log
/// </summary>
/// <param name="mesType"></param>
/// <param name="mesContent"></param>
public void accodaRawData(rawTransfType mesType, object mesContent)
{
/*--------------------------------
* nuova gestione coda dictionary
* fixme todo da fare !!!
*
* - conterrà una lista di oggetti baseRawTransf
* - i dati vanno poi "scodati" dal + vecchio ed inviati a MP/IO
* - mostra un sunto delle info da inviare
* - accodamento vero e proprio
* - verifica (opzionale) coda massima x gestire roundRobin ultimi eventi
* - trace della coda
* - counter invio??? valutare se c'è dataora e poi sono da salvare su MongoDb / Redis
*
* */
// serializzo il valore...
JObject njObj;
if (mesType == rawTransfType.IcoelBatch || mesType == rawTransfType.IcoelVarInfo)
{
njObj = (JObject)mesContent;
}
else
{
njObj = (JObject)JToken.FromObject(mesContent);
}
BaseRawTransf newVal = new BaseRawTransf(DateTime.Now, njObj, mesType);
string encodedVal = JsonConvert.SerializeObject(newVal);
// --> accodo (valore già formattato)!
QueueRawTransf.Enqueue(encodedVal);
// se abilitato controllo coda Max (superiore a 0...)
if (maxQueueRawTransf > 0)
{
// se ho una coda superiore a max ammesso
if (QueueRawTransf.Count > maxQueueRawTransf)
{
// elimino valori iniziali fino a tornare al max ammesso...
while (QueueRawTransf.Count > maxQueueRawTransf)
{
string currVal = "";
QueueRawTransf.TryDequeue(out currVal);
lgInfo($"Eliminazione da coda RawTransf per superamento maxLengh: {currVal}");
}
}
}
// loggo!
lgTrace(string.Format("[QUEUE-RTRANSF] {0}", encodedVal));
counterRawTransf++;
if (counterRawTransf > 9999)
{
counterRawTransf = 0;
}
}
/// <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)
{
DateTime adesso = DateTime.Now;
// mostro dati variati letti...
displayInData(ref currDispData);
// verifico veto a invio status macchina
string keyName = "VETO_SIG_IN";
if (getOptPar(keyName).ToUpper() == "TRUE")
{
lgTrace($"Filtrato accodamento valore da conf IOB | {keyName} | [QUEUE-IN] {qEncodeIN}");
}
else
{
// verifico non sia in veto invio iniziale...
if (queueInEnabCurr)
{
// --> accodo (valore già formattato)!
QueueIN.Enqueue(qEncodeIN);
// loggo!
lgInfo($"[QUEUE-IN] {qEncodeIN}");
}
else
{
lgInfo($"[VETO FOR QUEUE-IN] | {qEncodeIN} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
checkVetoQueueIn();
}
// aggiorno counters ed eventuale reset
nReadFilt++;
if (nReadFilt > int.MaxValue - 1)
{
nReadFilt = 0; // per evitare buffer overflow...
}
counterSigIN++;
if (counterSigIN > 9999)
{
counterSigIN = 0;
}
}
}
/// <summary>
/// Accumula in coda i valori USER LOG e logga...
/// </summary>
/// <param name="val">VALORE RAW (x display)</param>
/// <param name="encodedVal">VALORE già processato con qEncodeULog(...)</param>
public void accodaUserLog(string val, string encodedVal)
{
// mostro dati variati letti...
displayOtherData(val);
// accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)!
QueueULog.Enqueue(encodedVal);
// loggo!
lgInfo(string.Format("[QUEUE-USER-LOG] {0}", encodedVal));
counterULog++;
if (counterULog > 9999)
{
counterFLog = 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.newInData = $"{B_output}";
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)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
string newVal = "";
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setArt:
case taskType.setComm:
case taskType.setProg:
case taskType.setPzComm:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, item.Value));
break;
case taskType.setArtNum:
// in primis faccio una chiamata per tutta la tab SE fosse vuoto il
// dict di traduzione
if (DictNumArt == null || DictNumArt.Count == 0)
{
getNumArt("");
}
// chiamo server x avere decodifica valore INT
newVal = getNumArt(item.Value);
// procedo come il resto cercando mappatura in memMap: 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} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = newVal;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
break;
case taskType.setCommNum:
// chiamo server x avere decodifica valore INT
newVal = getNumComm(item.Value);
// procedo come il resto cercando mappatura in memMap: 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} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = newVal;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
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 setParameter --> processMemWriteRequests");
taskVal = processMemWriteRequests();
// se restituiscce "" faccio altra prova...
if (string.IsNullOrEmpty(taskVal))
{
// i parametri me li aspetto come stringa composta paramName|paramvalue
if (item.Value.Contains("|"))
{
string[] paramsJob = item.Value.Split('|');
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
}
else
{
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
}
}
break;
case taskType.syncDbData:
processDataSync();
break;
case taskType.processOtherInfo:
bool okProc = processOtherInfo(item.Key, item.Value);
taskVal = okProc ? $"OK processOtherInfo | {item.Key} | {item.Value}" : $"ERROR processOtherInfo | {item.Key} | {item.Value}";
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
else
{
foreach (var item in task2exe)
{
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
string taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED (no MemMap) | NO EXEC";
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe! Tutte le richieste sono state chiuse senza esecuzione");
}
}
return taskDone;
}
/// <summary>
/// Effettua verifica delle memorie WRITE, se ci siano variazioni da riportare verso il PLC
/// Da impiegare ad esempio in caso di chiamate complesse (ad 2 aree di memoria condivise
/// sul PLC; coem commessa + articolo EWON)
/// </summary>
/// <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>
/// forza reset ODL
/// </summary>
public void forceResetOdl()
{
lgInfo("Registrato richiesta forzatura reset ODL");
pzCountResetted = true;
}
/// <summary>
/// Effettua chiamata x split ODL
/// </summary>
/// <returns></returns>
public bool forceSplitOdl()
{
bool fatto = false;
if (vetoSplit < DateTime.Now)
{
lgInfo("Richiesto forceSplitOdl");
// 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 okReport 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 okReport reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...)
* - viene okReport 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}&multi={item}";
rawSplit = callUrl(fullUrl, false);
lgDebug($"Esecuzione forceSplit | URL: {fullUrl} | esito: {rawSplit}");
}
fatto = (rawSplit == "OK") ? true : false;
}
}
catch (Exception exc)
{
lgError($"Eccezione in forceSplitOdl{Environment.NewLine}{exc}");
}
// se okReport --> 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>
/// Processing degli allarmi (se presenti e gestiti)
/// </summary>
/// <returns></returns>
public virtual Dictionary<string, string> getAlarmData()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
// ora aggiungo (se ci fossero) gli allarmi...
if (alarmMaps != null && alarmMaps.Count > 0)
{
if (hasAlarms())
{
string bankVal = "";
foreach (var item in alarmMaps)
{
bankVal = "";
// verifico ed eseguo secondo il tipo di allarme gestito...
if (alarmType == AlarmBlockType.Bitmap)
{
var currState = currAlarmsState(item);
// calcolo vettore stringa degli allarmi...
bankVal = getAlarmState(item, currState);
}
else if (alarmType == AlarmBlockType.ActiveList)
{
// cerco se sia superiore al livello minimo da conf
if (item.blockLevel >= alarmLevelMin)
{
int numActive = getAlarmStatus(item);
// calcolo vettore stringa degli allarmi...
bankVal = getAlarmState(item, numActive);
}
}
// sistemo se OK e/o invio
bankVal = string.IsNullOrEmpty(bankVal) ? "OK" : bankVal;
saveAlarmString(ref outVal, bankVal, item.description);
}
}
}
else
{
lgTrace("No alarm found in alarmMaps");
}
if (periodicLog || outVal.Count > 0)
{
lgDebug($"Esito getAlarmData: {outVal.Count} allarmi attivi/validi in outVal");
}
return outVal;
}
/// <summary>
/// Effettua conversione da valore bitmap ai valori configurati x allarme
/// </summary>
/// <param name="memConf">Configurazione banco allarmi</param>
/// <param name="bState">BitState allarmi</param>
/// <returns></returns>
public string getAlarmState(BaseAlarmConf memConf, byte[] bState)
{
string valTransl = "";
List<string> descAttivi = new List<string>();
BitArray bitAttivi = new BitArray(bState);
bool[] bitStati = new bool[bitAttivi.Count];
bitAttivi.CopyTo(bitStati, 0);
// cerco bit attivi
int idx = 0;
foreach (var item in bitStati)
{
if (item && memConf.messages.Count > idx)
{
string currState = memConf.messages[idx];
descAttivi.Add(currState);
}
idx++;
}
// combino string
valTransl = string.Join(",", descAttivi);
return valTransl;
}
/// <summary>
/// Effettua conversione tra gli allarmi attivi secondo configurazione banco allarmi
/// </summary>
/// <param name="memConf">Configurazione banco allarmi</param>
/// <param name="numAlarms">num allarmi attivi</param>
/// <returns></returns>
public virtual string getAlarmState(BaseAlarmConf memConf, int numAlarms)
{
string valTransl = "";
List<string> descAttivi = new List<string>();
// FAKE!!!: dovrebbe cercare in aree memoria x ogni valore configurato, vedere
// implementazione specifica es OpcUa
// combino string
valTransl = string.Join(",", descAttivi);
return valTransl;
}
/// <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();
processCustomTaskMF();
processOverride();
processContapezzi();
processCncAlarms();
processDynData();
processMem2Write();
}
else if (ciclo == gatherCycle.LF)
{
processCustomTaskLF();
processOtherCounters();
processProgram();
// verifico se devo gestire cambio ODL in modo automatico
processAutoOdl();
// verifico se devo gestire auto generazione dossier quotidiana
processAutoDossier();
// effettua gestione import file se configurato...
processFileImport();
// effettua process ritorno ricette
processRecipeFileRet();
}
else if (ciclo == gatherCycle.VLF)
{
if (utils.CRB("enableContapezzi"))
{
// rilettura contapezzi da server...
lgTrace("Ciclo VLF: pzCntReload(true)");
if (!isMulti)
{
pzCntReload(true);
}
// refresh associazione Macchina - IOB
SendM2IOB();
// invio altri dati accessori...
SendMachineConf();
}
// recupero dati SETUP (sysinfo) e li invio/mostro se variati...
processSysInfo();
// checkLogDir x shrink!
checkShrinkDir();
// eventuale log!
if (utils.CRB("recTime"))
{
try
{
logTimeResults();
}
catch
{ }
}
processRecipeSyncArch();
if (enableSlowData)
{
processSlowDataRead();
}
}
// 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)
{
lgDebug("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
{
// anche se NON connesso alcuni task di bassa freq li eseguo...
if (ciclo == gatherCycle.LF)
{
// verifico se devo gestire cambio ODL in modo automatico
processAutoOdl();
// verifico se devo gestire auto generazione dossier quotidiana
processAutoDossier();
// effettua gestione import file se configurato...
processFileImport();
// effettua process ritorno ricette
processRecipeFileRet();
}
else if (ciclo == gatherCycle.VLF)
{
processRecipeSyncArch();
}
// provo a riconnettere SE abilitato tryRestart...
if (adpTryRestart && !connectionOk)
{
// controllo se sia scaduto periodi di veto al tryConnect...
int waitRecMSec = utils.CRI("waitRecMSec") * 2;
// cerco se ci sia un valore in ovverride x il singolo IOB...
if (cIobConf.waitRecMSec > 0)
{
waitRecMSec = cIobConf.waitRecMSec;
}
DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec);
if (DateTime.Now > dtVeto)
{
lgInfo($"Veto Time Elapsed | lastConnectTry: {lastConnectTry} | waitRecMSec {waitRecMSec} ms) | NOW tryConnect");
lastConnectTry = DateTime.Now;
tryConnect();
}
}
currDispData.semIn = Semaforo.SR;
processDisconnectedTask();
processMemoryDiscon();
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua conversione da valore bitmap ai valori configurati
/// </summary>
/// <param name="memConf"></param>
/// <param name="bState"></param>
/// <returns></returns>
public string getBitmapState(dataConfTSVC memConf, byte[] bState)
{
string valTransl = "";
List<string> descAttivi = new List<string>();
BitArray bitAttivi = new BitArray(bState);
bool[] bitStati = new bool[bitAttivi.Count];
bitAttivi.CopyTo(bitStati, 0);
// cerco bit attivi
int idx = 0;
foreach (var item in bitStati)
{
if (item && memConf.decodeMap.Count > idx)
{
string currState = memConf.decodeMap[idx];
descAttivi.Add(currState);
}
idx++;
}
// combino string
valTransl = string.Join(",", descAttivi);
return valTransl;
}
/// <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 nei KVP (JSON) e lo restituisce
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string getOptJsonKVP(string key)
{
string answ = "";
// controllo SE HO il parametro
if (memMap != null && memMap.optKVP != null && memMap.optKVP.Count > 0)
{
if (memMap.optKVP.ContainsKey(key))
{
answ = memMap.optKVP[key];
}
}
return answ;
}
/// <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>
/// Cerca se esiste un link tra aree di memoria in write e lo restituisce
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string getOptWriteLink(string key)
{
string answ = "";
// controllo SE HO il parametro
if (memMap != null && memMap.mMapWriteLink != null && memMap.mMapWriteLink.Count > 0)
{
if (memMap.mMapWriteLink.ContainsKey(key))
{
answ = memMap.mMapWriteLink[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 = -999999;
// cerco VC...
if (TSVC_Data.ContainsKey(VCName))
{
try
{
switch (TSVC_Data[VCName].Funzione)
{
case VC_func.POINT:
// prendo PRIMO
answ = TSVC_Data[VCName].dataArray.FirstOrDefault();
break;
case VC_func.AVG:
answ = TSVC_Data[VCName].dataArray.Average();
break;
case VC_func.MEDIAN:
answ = TSVC_Data[VCName].dataArray.Median();
break;
case VC_func.MIN:
answ = TSVC_Data[VCName].dataArray.Min();
break;
case VC_func.MAX:
default:
answ = TSVC_Data[VCName].dataArray.Max();
break;
}
}
catch
{ }
// 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)
{
string[] valori;
int counter = 0;
DateTime dtEve = DateTime.Now;
switch (tipoUrl)
{
case urlType.FLog:
flogData currFlData = new flogData();
flogJsonPayload fullFlObj = new flogJsonPayload();
fullFlObj.fluxData = new List<flogData>();
// inizio processando ogni valore
foreach (var item in elencoValori)
{
valori = qDecodeIN(item);
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve);
int.TryParse(valori[3], out counter);
currFlData = new flogData()
{
flux = valori[1],
valore = valori[2],
dtEve = dtEve,
dtCurr = DateTime.Now,
cnt = counter
};
fullFlObj.fluxData.Add(currFlData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullFlObj);
}
catch (Exception exc)
{
lgError($"FLog Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
break;
case urlType.SignIN:
evData currSigData = new evData();
evJsonPayload fullEvObj = new evJsonPayload();
fullEvObj.eventList = new List<evData>();
// inizio processando ogni valore
foreach (var item in elencoValori)
{
valori = qDecodeIN(item);
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve);
int.TryParse(valori[2], out counter);
currSigData = new evData()
{
valore = valori[1],
dtEve = dtEve,
dtCurr = DateTime.Now,
cnt = counter
};
fullEvObj.eventList.Add(currSigData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullEvObj);
}
catch (Exception exc)
{
lgError($"SignIN Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
break;
case urlType.RawTransf:
BaseRawTransf currRTData = new BaseRawTransf();
#if false
rawTransfJsonPayload fullRTObj = new rawTransfJsonPayload();
fullRTObj.rawTransfData = new List<BaseRawTransf>();
// inizio processando ogni valore
foreach (var item in elencoValori)
{
try
{
currRTData = JsonConvert.DeserializeObject<BaseRawTransf>(item);
}
catch (Exception exc)
{
lgError($"Eccezione in deserializzazione BaseRawTransf:{Environment.NewLine}{exc}");
}
fullRTObj.rawTransfData.Add(currRTData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullRTObj);
}
catch (Exception exc)
{
lgError($"RawTransf Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
#endif
// provo una serializzazione "brutale", ovvero aggiungo alla stringa il
// valore di testa...
string rawResult = "";
foreach (var item in elencoValori)
{
rawResult += $"{item},";
}
if (rawResult.Length > 0)
{
rawResult = rawResult.Substring(0, rawResult.Length - 1);
}
answ = $"[{rawResult}]";
//answ = "{" + $"\"rawTransfData\":[{rawResult}]" + "}";
break;
case urlType.ULog:
int numVal = 0;
int matrOp = 0;
ulogData currUlData = new ulogData();
ulogJsonPayload fullUlObj = new ulogJsonPayload();
fullUlObj.fluxData = new List<ulogData>();
// inizio processando ogni valore
foreach (var item in elencoValori)
{
valori = qDecodeIN(item);
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime.TryParseExact(valori[0], "yyyyMMddHHmmssfff", provider, DateTimeStyles.AssumeLocal, out dtEve);
int.TryParse(valori[3], out matrOp);
int.TryParse(valori[5], out numVal);
int.TryParse(valori[6], out counter);
currUlData = new ulogData()
{
flux = valori[1],
valore = valori[2],
dtEve = dtEve,
dtCurr = DateTime.Now,
cnt = counter,
matrOpr = matrOp,
label = valori[4],
valNum = numVal
};
fullUlObj.fluxData.Add(currUlData);
}
// conversione finale
try
{
answ = JsonConvert.SerializeObject(fullUlObj);
}
catch (Exception exc)
{
lgError($"ULog Errore in costruzione jsonPayload:{Environment.NewLine}{exc}");
}
break;
default:
break;
}
}
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 Dossier quotidiani automatica
/// </summary>
public void processAutoDossier()
{
bool fatto = false;
string callResp = "";
if (!string.IsNullOrEmpty(getOptPar("AUTO_SNAPSHOT_DOSSIER")))
{
lgTrace("Richiesta processAutoDossier");
string IOB_MULTI_CNAME = "";
string[] elencoMulti = null;
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_SNAPSHOT_DOSSIER"), out doProc);
if (doProc)
{
lgTrace("AUTO_SNAPSHOT_DOSSIER abilitato");
// chiamo stored x creare Snapshot Dossier giornalieri fino alla data...
callResp = utils.callUrl(urlFixDailyDossier);
fatto = callResp == "OK";
lgTrace($"Esecuzione processAutoDossier completata --> esito: {callResp}");
}
else
{
lgTrace("AUTO_SNAPSHOT_DOSSIER DISABILITATO");
}
}
// loggo se fatto
if (fatto)
{
lgTrace($"Effettuato processAutoDossier, esito positivo | {DateTime.Now:HH.mm.ss}");
}
}
/// <summary>
/// Verifica e processing x gestione ODL automatica
/// </summary>
public void processAutoOdl()
{
bool fatto = false;
if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL")))
{
lgTrace("Richiesta processAutoOdl");
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)
{
lgTrace("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
* */
lgTrace("processAutoOdl: caso PZCOUNT_RESET");
if ((!isRunning || forceResetInRun) && pzCountResetted)
{
callChangeODL = true;
lgInfo("Attivato cambio ODL da PZCOUNT_RESET");
}
else
{
lgInfo($"isRunning: {isRunning} | pzCountResetted: {pzCountResetted} | forceResetInRun: {forceResetInRun} | contapezziIOB: {contapezziIOB} | contapezziPLC: {contapezziPLC}");
}
}
else if (CHANGE_ODL_MODE == "DAILY")
{
// chiamo stored x creare ODL giornalieri alla data...
string autoOdlRes = utils.callUrl(urlFixDailyOdl);
}
else if (CHANGE_ODL_MODE == "DAILY_CONF_PZ")
{
// chiamo stored x creare ODL giornalieri alla data + conferma pezzi...
string autoOdlRes = utils.callUrl(urlFixDailyOdlConfPzCount);
}
else if (CHANGE_ODL_MODE == "TIME" || CHANGE_ODL_MODE == "SIMUL")
{
// 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;
}
}
}
// se NON fosse scaduto MA è simulato esegue controllo sul num pezzi da
// fare, se sfora (RANDOM) > +(50...110)% --> cambia!
if (!callChangeODL && CHANGE_ODL_MODE == "SIMUL")
{
var rawCount = callUrl(urlGetNumPzCurrODL, false);
if (int.TryParse(rawCount, out var numPzReqOdl))
{
int limitQty = (numPzReqOdl * rndGen.Next(150, 210)) / 100;
callChangeODL = contapezziPLC > limitQty;
}
}
}
}
// vero processing...
if (callChangeODL)
{
lgTrace("Chiamata: processAutoOdl --> forceSplitODL");
fatto = forceSplitOdl();
pzCountResetted = false;
lgInfo("Esecuzione processAutoOdl completata --> pzCountResetted = false");
}
}
else
{
lgTrace("AUTO_CHANGE_ODL DISABILITATO");
}
}
// loggo se okReport
if (fatto)
{
lgTrace("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
{
string sVal = "";
if (lastAlarm != currAlarms["CNC_ALARM"])
{
// salvo!
lastAlarm = currAlarms["CNC_ALARM"];
// per ogni valore del dizionario mostro ed accodo!
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));
}
}
// accodo ALTRI allarmi NON CNC...
foreach (var item in currAlarms.Where(X => X.Key != "CNC_ALARM"))
{
sVal = $"{item.Key} | {item.Value}";
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 CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
/// ogni 5 sec se base timer 10ms, vedere app.config)
/// </summary>
public virtual void processCustomTaskLF()
{ }
/// <summary>
/// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
/// ogni 3 sec se base timer 10ms, vedere app.config)
/// </summary>
public virtual void processCustomTaskMF()
{ }
/// <summary>
/// Task periodici SE disconnesso
/// </summary>
public virtual void processDisconnectedTask()
{
}
/// <summary>
/// Effettua processing del recupero dei valori dinamici:
/// es: speed (RPM, feedrate) degli assi, valori pressioni, temeprature
/// </summary>
public void processDynData()
{
bool enableByApp = utils.CRB("enableDynData");
bool enableByIob = (getOptPar("ENABLE_DYN_DATA") == "TRUE");
bool forceSendByIob = (getOptPar("FORCE_DYN_DATA") == "TRUE");
bool disableDynData = (getOptPar("DISABLE_DYN_DATA") == "TRUE");
Dictionary<string, string> currDynData = new Dictionary<string, string>();
if (enableByApp || enableByIob)
{
// verifico se ho fattore demoltiplica...
if (demFactDynData > 1)
{
lgTrace($"Non eseguo processDynData: fatt veto {demFactDynData}");
// riduco...
demFactDynData--;
}
else
{
lgTrace("Inizio processDynData");
// sistemo il valore di demoltiplica a default
fixDemFactDynData();
// proseguo
if (connectionOk)
{
currDynData = getDynData();
var currAlarmData = getAlarmData();
lgTrace($"currDynData: {currDynData.Count} rec | currAlarmData: {currAlarmData.Count} rec");
// se ho allarmi
if (currAlarmData != null && currAlarmData.Count > 0)
{
foreach (var item in currAlarmData)
{
if (!currDynData.ContainsKey(item.Key))
{
// aggiungo!
currDynData.Add(item.Key, item.Value);
}
}
}
// verifico parametro sintesi... che ricalcolo ogni volta
if (!currDynData.ContainsKey("DYNDATA"))
{
currDynData.Add("DYNDATA", "");
}
// verifico se DynData sia abilitato IN GENERALE
if (!disableDynData)
{
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 == null || 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");
}
}
}
else
{
lgError("Errore connessione mancante x getDynData");
}
}
}
}
/// <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 valori BYTE IN/PREV/OUT... così invio macchina spenta...
B_input = 0;
B_output = 0;
B_previous = -1;
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) && resp.Length > 2)
{
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>();
Dictionary<string, string> task2Add = new Dictionary<string, string>();
// eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)...
if (!cIobConf.disableExeTask)
{
if (task2exe != null)
{
lgInfo($"Task2Exe S01: {task2exe.Count} task ricevuti:");
int idTask = 0;
foreach (var item in task2exe)
{
idTask++;
lgInfo($"[{idTask:00}] - {item.Key} --> {item.Value}");
// verifico SE il task abbia un duplo writeLink e nel caso lo aggiungo...
var linkVal = getOptWriteLink(item.Key);
if (!string.IsNullOrEmpty(linkVal))
{
// aggiungo a task2add...
task2Add.Add(linkVal, item.Value);
lgInfo($"Aggiunta task linked: {linkVal} -> {item.Value}");
}
}
// se ho task Link da aggiungere li aggiungo!
if (task2Add.Count > 0)
{
foreach (var item in task2Add)
{
task2exe.Add(item.Key, item.Value);
}
}
// chiamo procedura esecutiva (diversa x ogni IOB)
taskDone = executeTasks(task2exe);
lgInfo($"Task2Exe S02: eseguiti {taskDone.Count} task");
// 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, string forceMach = "")
{
// legge da IO server ULTIMO valore CONTPEZZI al riavvio...
string currServerCount = "";
string lastIdxODL = "";
string calcUrl = "";
if (checkServerAlive)
{
// leggo PRIMA ODL ....
calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetCurrODL : urlGetCurrODL.Replace(cIobConf.codIOB, forceMach);
lastIdxODL = utils.callUrl(calcUrl);
lgTrace($"Lettura ODL dall'url {calcUrl} --> {lastIdxODL}");
// se ho valori in coda da trasmettere uso dati REDIS
if (forceCountRec)
{
// uso dati da TCiclo registrati...
calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCountRec : urlGetPzCountRec.Replace(cIobConf.codIOB, forceMach);
currServerCount = utils.callUrl(calcUrl);
lgInfo($"Lettura contapezzi da TCiclo dall'url {calcUrl} --> num pz: {currServerCount}");
}
else
{
// uso il contapezzi dichiarato dall'IOB stesso
calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCount : urlGetPzCount.Replace(cIobConf.codIOB, forceMach);
currServerCount = utils.callUrl(calcUrl);
lgInfo($"Lettura contapezzi dall'url {calcUrl} --> {currServerCount}");
}
// controllo: SE NON HO ODL...
if (string.IsNullOrEmpty(lastIdxODL) || lastIdxODL == "0")
{
// NON AGGIORNO
contapezziIOB = contapezziPLC;
lgError($"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;
lgError($"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);
lgInfo($"ODL | currIdxODL: {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#flusso#valore#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 = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterULog}";
}
catch
{ }
return answ;
}
/// <summary>
/// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
/// dtEve#flux#valReq#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 = $"{eventDT:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterFLog}";
}
catch
{ }
return answ;
}
/// <summary>
/// Fornisce il valore di UserLog e valore in formato valido x messa in coda nel formato:
/// dtEve#flusso#valReq#cont#matrOpr#label#valNum <paramref name="flusso">Flusso dati
/// (RC/RS/DI)</paramref><paramref name="valore">Valore da inviare
/// (valString</paramref><paramref name="matrOpr">Matricola operatore</paramref><paramref
/// name="label">Valore etichetta: causale scarto / tagCode</paramref><paramref
/// name="valNum">Valore numerico: esitoOk (0/1) / nuo scarti</paramref>
/// </summary>
public string qEncodeULog(string flusso, string valore, int matrOpr, string label, int valNum)
{
string answ = "";
try
{
answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{matrOpr}#{label}#{valNum}#{counterULog}";
}
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)
{
if (!cIobConf.disableStateCh)
{
readSemafori(ref currDispData);
}
else
{
// forzo lettura OK
currDispData.semIn = Semaforo.SV;
}
}
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 (32bit)
/// 2021.08.27: filtrati secondo i valori setup start/size RawDataInput
/// </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}");
// Do x scontato siano VALIDI i valori di RawDataInputStart / size --> filtro...
var filtRawData = new byte[RawDataInputSize];
Array.Copy(RawInput, RawDataInputStart, filtRawData, 0, RawDataInputSize);
int i = RawDataInputStart;
foreach (var item in filtRawData)
{
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>
/// Effettua salvataggio in LUT del valore ricevuto (valori string)
/// - non serve calcolo medie o altro
/// - accoda in out val e basta
/// </summary>
/// <param name="outVal">Array eventi da popolare</param>
/// <param name="valore">valore da salvare</param>
/// <param name="chiave">ID/chiave di riferimento</param>
/// <returns></returns>
public virtual void saveAlarmString(ref Dictionary<string, string> outVal, string valore, string chiave)
{
DateTime adesso = DateTime.Now;
//check obj preliminare
if (outVal == null)
{
outVal = new Dictionary<string, string>();
}
// default invio: blindato a 120 sec SE non trova conf x fluxLogResendPeriod
int vetoSendAlarms = 120;
if (fluxLogReduce)
{
vetoSendAlarms = 60 * fluxLogResendPeriod;
}
// verifico se sia scaduto OVVERO variato rispetto a prima oppure oltre tempo
bool scaduto = !LastTSSSend.ContainsKey(chiave) || (LastTSSSend[chiave].AddSeconds(vetoSendAlarms) < adesso);
bool cambiato = !LastTSS.ContainsKey(chiave) || !LastTSS[chiave].Equals(valore);
if (scaduto || cambiato)
{
outVal.Add(chiave, valore);
LastTSS[chiave] = valore;
LastTSSSend[chiave] = adesso;
}
}
/// <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)
{
upsertKey(item.Key, item.Value);
}
/// <summary>
/// Effettua salvataggio in LUT del valore ricevuto (valori numerici)
/// </summary>
/// <param name="outVal"></param>
/// <param name="valore"></param>
/// <param name="chiave"></param>
/// <returns></returns>
public virtual void saveValue(ref Dictionary<string, string> outVal, string chiave, double valore)
{
//check obj preliminare
if (outVal == null)
{
outVal = new Dictionary<string, string>();
}
bool scaduto = stackVal_TSVC(chiave, valore, DateTime.Now);
// recupero VC
valore = getVal_TSVC(chiave, scaduto);
// 2023.11.15: gestione deduplica (opzionale) fluxlog... in caso invalida scaduto...
if (fluxLogReduce && scaduto)
{
/*-------------------------------
* logica processing:
* - confronto con valore precedente (se non c'è allora registro questo)
* - se variato OLTRE deadband --> nulla cambia
* - se NON variato x deadband --> accodo SOLO SE scaduto tempo resend
* ------------------------------ */
DateTime adesso = DateTime.Now;
// cerco tra i veto...
if (fluxLogReduceVeto.ContainsKey(chiave))
{
// per prima cosa verifico che sia ancora valido il veto...
if (adesso < fluxLogReduceVeto[chiave])
{
// verifico valore precedente + deadband x decidere se sia davvero scaduto
if (fluxLogReduceLast.ContainsKey(chiave))
{
scaduto = Math.Abs(fluxLogReduceLast[chiave] - valore) > fluxLogRedDeadBand;
}
}
// altrimenti creo un nuovo veto lasciando inalterata la scadenza...
else
{
fluxLogReduceVeto[chiave] = adesso.AddMinutes(fluxLogResendPeriod);
}
}
// se non ci fosse metto veto con periodo std...
else
{
fluxLogReduceVeto.Add(chiave, adesso.AddMinutes(fluxLogResendPeriod));
if (fluxLogReduceLast.ContainsKey(chiave))
{
fluxLogReduceLast[chiave] = valore;
}
else
{
fluxLogReduceLast.Add(chiave, valore);
}
}
}
if (scaduto)
{
// limite a 3 digit x valore float
outVal.Add(chiave, $"{valore:N3}");
}
LastTSVC[chiave] = valore;
}
/// <summary>
/// Effettua salvataggio in LUT del valore ricevuto (valori string)
/// - non serve calcolo medie o altro
/// - accoda in out val e basta
/// </summary>
/// <param name="outVal">Array eventi da popolare</param>
/// <param name="chiave">ID/chiave di riferimento</param>
/// <param name="valore">valore da salvare</param>
/// <returns></returns>
public virtual void saveValueString(ref Dictionary<string, string> outVal, string chiave, string valore)
{
DateTime adesso = DateTime.Now;
//check obj preliminare
if (outVal == null)
{
outVal = new Dictionary<string, string>();
}
int maxVetoSeconds = 60;
// cerco VC...
if (TSVC_Data.ContainsKey(chiave))
{
maxVetoSeconds = TSVC_Data[chiave].Period;
}
// se ho attivo il veto invio fluxLogReduce metto periodo a minuti indicati...
if (fluxLogReduce)
{
maxVetoSeconds = fluxLogResendPeriod * 60;
}
// verifico se sia scaduto OVVERO variato rispetto a prima oppure oltre tempo
bool scaduto = !LastTSSSend.ContainsKey(chiave) || (LastTSSSend[chiave].AddSeconds(maxVetoSeconds) < adesso);
bool cambiato = !LastTSS.ContainsKey(chiave) || !LastTSS[chiave].Equals(valore);
if (scaduto || cambiato)
{
outVal.Add(chiave, valore);
LastTSS[chiave] = valore;
LastTSSSend[chiave] = adesso;
}
}
/// <summary>
/// Invio la variazione dello stato allarmi (se avvenuta)
/// </summary>
/// <param name="memAddr">COD memoria allarmi</param>
/// <param name="index">Indice memoria allarmi</param>
/// <param name="lastStatus">ultimo stato rilevato</param>
/// <param name="currStatus">stato corrente</param>
/// <param name="AlarmList">Lista allarmi validi per l'area</param>
/// <returns></returns>
public bool sendAlarmVariations(string memAddr, int index, uint lastStatus, uint currStatus, List<string> AlarmList)
{
bool fatto = false;
if (lastStatus != currStatus)
{
List<string> ActiveAlarmList = new List<string>();
// invio GET del MemoryAddress, del banco e del valore currStatus
lastUrl = $"{urlSendAlarm}?memAddr={memAddr}&index={index}&currStatus={currStatus}";
if (currStatus == 0)
{
ActiveAlarmList.Add("ALL OK");
}
else
{
// calcolo quali allarmi mostrare dato currStatus ed elenco allarmi
string bitMap = Convert.ToString(currStatus, 2);
int bitLen = bitMap.Length;
// ciclo x associare tutti gli allarmi
for (int i = 0; i < bitLen; i++)
{
// se è 1 --> aggiungo!
if (bitMap[bitLen - i - 1] == '1')
{
// se sono entro limiti
if (AlarmList.Count >= i)
{
ActiveAlarmList.Add($"{i:000}-{AlarmList.ElementAt(i)}");
}
//else
//{
// ActiveAlarmList.Add($"Unknown Bit.{i}");
//}
}
}
}
string rawData = JsonConvert.SerializeObject(ActiveAlarmList);
//string resp = utils.callUrlNow(lastUrl, recipeRows);
string resp = utils.callUrlAsync(lastUrl, rawData);
if (!string.IsNullOrEmpty(resp))
{
if (resp.ToUpper() == "OK")
{
// segnalo okReport
fatto = true;
}
}
else
{
lgError($"Errore in invio richiesta registrazione allarme | resp: {resp} | URL: {lastUrl}{Environment.NewLine}Payload:{Environment.NewLine}{rawData}");
}
}
return fatto;
}
/// <summary>
/// Invia una LISTA di valori
/// </summary>
/// <param name="tipoUrl"></param>
/// <param name="queueVal"></param>
public bool sendDataBlock(urlType tipoUrl, List<string> listQueueVal, bool force = false)
{
bool fatto = false;
// 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 || false)
{
// chiamo URL!
string answ = callUrlWithPayload(lastUrl, payload, doAsync);
// attesa opzionale se configurata
// loggo!
lgInfo($"[SEND payload] TipoURL: {tipoUrl} | {listQueueVal.Count} records --> {answ}");
// se "OK" verde, altrimenti errore --> ROSSO
if (answ.Contains("OK"))
{
fatto = true;
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);
return fatto;
}
/// <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!
lgDebug(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 timeout ({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
{
lgError(string.Format("[SERVER KO] {0}", queueVal));
}
}
else
{
currDispData.semOut = Semaforo.SV;
// loggo!
lgDebug(string.Format("{0} -> [SIM]", queueVal));
}
nSendOut++;
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>
/// Effettua impostazione del conteggio pezzi richiesti
/// </summary>
/// <returns></returns>
public virtual bool setPzComm(int pzReq)
{
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, DateTime dtLimit)
{
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) < dtLimit)
{
answ = true;
}
lgTrace($"stackVal_TSVC | {VCName} | {VCVal} | scaduta: {answ}");
}
return answ;
}
/// <summary>
/// Avvia l'adapter sulla porta richiesta
/// </summary>
/// <param name="resetQueue">indica se sia richeisto di SVUOTARE le code delle info</param>
public virtual void startAdapter(bool resetQueue)
{
DateTime adesso = DateTime.Now;
DateTime scaduto = adesso.AddMinutes(-10);
lgInfo("Starting adapter...");
maxJsonData = utils.CRI("maxJsonData");
maxJsonDataEv = utils.CRI("maxJsonDataEv");
parentForm.commPlcActive = false;
adpRunning = true;
dtAvvioAdp = adesso;
lastWatchDog = scaduto;
lastPING = scaduto;
lastReadPLC = scaduto;
lastDisconnCheck = scaduto;
TimingData.resetData();
// aggiungo altri defaults
setDefaults(resetQueue);
adpTryRestart = true;
// sistemo altri check avvio
queueInEnabCurr = false;
dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn);
lgInfoStartup($"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}");
lgDebug("startAdapter completed");
}
/// <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 delle info</param>
public virtual void stopAdapter(bool tryRestart, bool forceDequeue)
{
// controllo che non ci sia redis queue altrimenti NON forza svuotamento...
if (forceDequeue && !cIobConf.EnableRedisQueue)
{
// svuoto le code dei valori letti e non ancora trasmessi...
parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda segnali...", true);
while (QueueIN.Count > 0)
{
// INVIO COMUNQUE...!!!
string valore = "";
QueueIN.TryDequeue(out valore);
sendToMoonPro(urlType.SignIN, valore);
}
parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda FluxLOG...", true);
// 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 DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue);
//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);
}
}
// svuoto coda ULog
while (QueueULog.Count > 0)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueULog.Count > maxJsonData)
{
string currVal = "";
// prendoi primi maxJsonDataValori
for (int i = 0; i < maxJsonData; i++)
{
QueueULog.TryDequeue(out currVal);
listaValori.Add(currVal);
}
sendDataBlock(urlType.ULog, listaValori);
}
else
{
// invio in blocco
listaValori = QueueULog.ToList();
// invio
sendDataBlock(urlType.ULog, listaValori);
// svuoto!
QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue);
//QueueULog = new ConcurrentQueue<string>();
}
}
}
parentForm.displayTaskAndLog("[STOP] Stopping adapter...", true);
adpTryRestart = false;
parentForm.displayTaskAndLog("[STOP] Stopping adapter - last periodic data read...", true);
// chiudo la connessione all'adapter...
tryDisconnect();
dtStopAdp = DateTime.Now;
adpTryRestart = tryRestart;
adpRunning = false;
// chiudo!
parentForm.displayTaskAndLog("Adapter Stopped.", true);
DateTime adesso = DateTime.Now;
lastReadPLC = adesso;
lastPING = adesso;
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 DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue);
//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;
queueInEnabCurr = true;
}
/// <summary>
/// Metodo base disconnessione...
/// </summary>
public virtual void tryDisconnect()
{
queueInEnabCurr = false;
}
/// <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(string forceMachName)
{
// 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}";
if (!string.IsNullOrEmpty(forceMachName))
{
lastUrl = lastUrl.Replace(cIobConf.codIOB, forceMachName);
}
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($"SEND incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}");
// invio conferma contapezzi..
string fullUrl = $"{urlSetPzCount}{contapezziIOB}";
if (!string.IsNullOrEmpty(forceMachName))
{
fullUrl = fullUrl.Replace(cIobConf.codIOB, forceMachName);
}
string retVal = utils.callUrl(fullUrl);
// verifica se tutto OK
if (retVal != contapezziIOB.ToString())
{
// errore salvataggio contapezzi
lgError($"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
{
lgError("Impossibile trySendPzCountBlock: IobOnline è false");
}
}
else
{
lgError("Impossibile trySendPzCountBlock: MPOnline è false");
}
}
/// <summary>
/// Inserimento/aggiornamento chiavi/valore in currProdData
/// </summary>
/// <param name="chiave"></param>
/// <param name="valore"></param>
public void upsertKey(string chiave, string valore)
{
if (currProdData.ContainsKey(chiave))
{
currProdData[chiave] = valore;
}
else
{
currProdData.Add(chiave, valore);
}
// salvo in redis...
redisMan.redSaveHashDict(rKeyCurrProdData, currProdData);
}
/// <summary>
/// Formatta URL x invio in DataBlock Json dei dati FLog / eventi
/// </summary>
/// <param name="tipoUrl"></param>
/// <returns></returns>
public virtual string urlDataBlock(urlType tipoUrl)
{
// verifico la parte di link "tipoComando"
string tipoComando = "";
switch (tipoUrl)
{
case urlType.FLog:
tipoComando = cIobConf.serverData.CMDFLOG_JSON;
break;
case urlType.SignIN:
tipoComando = cIobConf.serverData.CMDBASE_JSON;
break;
case urlType.RawTransf:
tipoComando = cIobConf.serverData.CMDRAWTRANSF_JSON;
break;
case urlType.ULog:
tipoComando = cIobConf.serverData.CMDULOG_JSON;
break;
default:
break;
}
// URL base x input
string answ = $@"{cIobConf.serverData.TRANSP}://{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 = $@"{cIobConf.serverData.TRANSP}://{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>
/// URL per recupero dati ODL alla data...
/// </summary>
public string urlGetOdlAtDate(DateTime dtRif)
{
string answ = "";
try
{
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getOdlAtDate/{cIobConf.codIOB}?dateRif={dtRif:yyyyMMdd}";
}
catch (Exception exc)
{
lgError(exc, $"Errore in composizione urlGetOdlAtDate per {dtRif}{Environment.NewLine}{exc}");
}
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 = $@"{cIobConf.serverData.TRANSP}://{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;
}
/// <summary>
/// Fornisce URL di tipo UserLog
/// </summary>
/// <param name="queueVal">valore salvato in coda nel formato dtEve#flux#valore#counter</param>
/// <returns></returns>
public string urlULog(string queueVal)
{
// URL base x input
string answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDULOG}";
// 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;
}
#endregion Public Methods
#region Internal Properties
internal bool disDynDataRangeCheck { get; set; } = false;
#endregion Internal Properties
#region Protected Fields
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 = -1;
/// <summary>
/// Cod grupo IOB x creazione PODL al volo
/// </summary>
protected string CodGruppoIob = "ND-00";
/// <summary>
/// Num errori check alive
/// </summary>
protected int currAliveErrors = 0;
/// <summary>
/// Dizionario valori impostati x produzione
/// </summary>
protected Dictionary<string, string> currProdData = new Dictionary<string, string>();
/// <summary>
/// num corrente errori read PLC
/// </summary>
protected int currReadErrors = 0;
/// <summary>
/// num errori send
/// </summary>
protected int currSendErrors = 0;
/// <summary>
/// Fattore di demoltiplicazione dei DynData x ridurre campionamento
/// </summary>
protected int demFactDynData = 1;
/// <summary>
/// Boolean x indicare contapezzi disabilitato forzatamente da IOB
/// </summary>
protected bool disablePzCountByIob = false;
/// <summary>
/// Determina se sia prevista gestione PODL (creazione/avvio/chiusura) come Soitaab
/// </summary>
protected bool EnabelPodlManFull = false;
/// <summary>
/// Boolean x indicare contapezzi abilitato a livello di conf apoplicazione
/// </summary>
protected bool enablePzCountByApp = true;
/// <summary>
/// Boolean x indicare contapezzi abilitato forzatamente da IOB
/// </summary>
protected bool enablePzCountByIob = true;
/// <summary>
/// Abilitazione gestione slow data
/// </summary>
protected bool enableSlowData = false;
/// <summary>
/// dizionario (opzionale) xz decodifica file da importare
/// </summary>
protected Dictionary<string, int> FileDecod = new Dictionary<string, int>();
/// <summary>
/// DeadBand x riduzione dati FluxLog (se 0 non gestita)
/// </summary>
protected double fluxLogRedDeadBand = 0;
/// <summary>
/// Determina se sia gestita riduzione dati FluxLog
/// </summary>
protected bool fluxLogReduce = false;
/// <summary>
/// Dizionario dei valori FluxLog ultimi verificati x veto
/// </summary>
protected Dictionary<string, double> fluxLogReduceLast = new Dictionary<string, double>();
/// <summary>
/// Dizionario dei valori FluxLog ultimi tipo STRING verificati x veto
/// </summary>
protected Dictionary<string, string> fluxLogReduceLastString = new Dictionary<string, string>();
/// <summary>
/// Dizionario dei veto send x ogni variabile quando non variata
/// </summary>
protected Dictionary<string, DateTime> fluxLogReduceVeto = new Dictionary<string, DateTime>();
/// <summary>
/// Finestra in minuti x invio dati FluxLog quando invariati
/// </summary>
protected int fluxLogResendPeriod = 60;
/// <summary>
/// indica che è richeisto forzatamente reset contapezzi
/// </summary>
protected bool forcePzReset = false;
/// <summary>
/// indica che è richeisto forzatamente reset contapezzi
/// </summary>
protected DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1);
/// <summary>
/// Determina se siano gestite le ricette
/// </summary>
protected bool hasRecipe = false;
/// <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>
/// Dizionario ULTIMI valori impostati x produzione
/// </summary>
protected Dictionary<string, string> lastProdData = new Dictionary<string, string>();
/// <summary>
/// Ultimo invio contapezzi (x invio delayed)
/// </summary>
protected DateTime lastPzCountSend;
/// <summary>
/// Dizionario ultimi valori (string) delle TSS
/// </summary>
protected Dictionary<string, string> LastTSS = new Dictionary<string, string>();
/// <summary>
/// Dizionario ultimi valori (string) delle TSS
/// </summary>
protected Dictionary<string, DateTime> LastTSSSend = new Dictionary<string, DateTime>();
/// <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>
/// Elenco parametri calcolati da inviare alla macchina (es ricetta con traduzione, RAMA/TFT)
/// </summary>
protected List<objItem> ListaCalcParams = new List<objItem>();
/// <summary>
/// Num massimo di errori in funzionalità check alive
/// </summary>
protected int maxAliveErrors = utils.CRI("maxAliveErrors");
/// <summary>
/// Soglia massima errori prima della disconnessione automatica in check
/// </summary>
protected int maxErroriCheck = utils.CRI("maxErroriCheck");
/// <summary>
/// Quantità massima per singola ricetta (per determinare num ricette da inviare)
/// </summary>
protected int maxQtyPerFile = 0;
/// <summary>
/// Num massimo di errori di rete read (dal PLC)
/// </summary>
protected int maxReadErrors = utils.CRI("maxReadErrors");
/// <summary>
/// Num massimodi errori di rete send al server
/// </summary>
protected int maxSendErrors = utils.CRI("maxSendErrors");
/// <summary>
/// Numero massimo di tentativi x test ping preliminare
/// </summary>
protected int maxTryPing = 1;
protected string mem2trace = "";
/// <summary>
/// indica se serva refresh parametri e quindi PLC...
/// </summary>
protected bool needRefresh = true;
/// <summary>
/// Indica se usare la parte numerica di un articolo come codice INT
/// </summary>
protected bool numArtCharTrim = false;
/// <summary>
/// Variabile numero errori vari (in lettura) --&gt; se supera soglia maxErroriCheck --&gt; disconnette
/// </summary>
protected int numErroriCheck = 0;
/// <summary>
/// Timeout x ping al server
/// </summary>
protected int pingServerMsTimeout = utils.CRI("pingMsTimeout");
/// <summary>
/// Ritardo minimo x invio contapezzi
/// </summary>
protected int pzCountDelay;
/// <summary>
/// Periodo di campionamento x refresh info
/// </summary>
protected int samplePeriod = 1000;
/// <summary>
/// Sample Period base x campionamenti tpo OCP-UA
/// </summary>
protected int samplePeriodBase = 600;
/// <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>
/// Indica se usare archivio locale ricette vs scarico http/REST
/// </summary>
protected bool useLocalRecipe = true;
/// <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>();
/// <summary>
/// Durata in secondi del divieto accodamento segnali IN alla fase di startup
/// </summary>
protected int vetoQueueIn = 1;
/// <summary>
/// Periodo wathdog di default (2 sec se non specificato)
/// </summary>
protected int watchDogPeriod = 2;
#endregion Protected Fields
#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>
/// Indica il counter della keyReq richiesta attiva
/// - gestito tramite Redis
/// - a scadenza 25h
/// - incrementato ogni invio di auto ODL
/// </summary>
protected int countKeyRichiesta
{
get
{
// calcolo keyReq odierna
int answ = redisMan.getKReqCount(dailyKey);
return answ;
}
set
{
// calcolo keyReq odierna
redisMan.setKReqCount(dailyKey, value);
}
}
protected string dailyKey
{
get
{
return DateTime.Today.ToString("yyMMdd");
}
}
protected Dictionary<string, string> DictNumArt { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Folder in cui è presente il tool import excel + file json di conf
/// </summary>
protected string exclToolDirPath { get; set; } = "";
/// <summary>
/// Folder in cui salvare i file importati (es excel)
/// </summary>
protected string fileArchiveFolder { get; set; } = "";
/// <summary>
/// Folder da cui importare i file (es excel)
/// </summary>
protected string fileImportFolder { get; set; } = "";
/// <summary>
/// Tipologia di files da importare (default excel)
/// </summary>
protected string fileImportType { get; set; } = "*.xslx";
/// <summary>
/// Dati fluxLog serializzati in redis
/// </summary>
protected List<GenLogRow> fluxLogData
{
get
{
List<GenLogRow> answ = new List<GenLogRow>();
string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog");
var rawData = redisMan.getRSV(redKeyFLog);
if (!string.IsNullOrEmpty(rawData))
{
answ = JsonConvert.DeserializeObject<List<GenLogRow>>(rawData);
}
return answ;
}
set
{
string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog");
string fluxLogRaw = JsonConvert.SerializeObject(value);
redisMan.setRSV(redKeyFLog, fluxLogRaw);
}
}
/// <summary>
/// Variabile di appoggio GLOBALE x indicare che si può forzare reset contapezzi con
/// macchina in RUN
/// </summary>
protected bool forceResetInRun { get; set; } = false;
/// <summary>
/// Variabile di appoggio GLOBALE x indicare macchina RUN (es x gestione su reset pezzi)
/// </summary>
protected bool isRunning { get; set; } = false;
/// <summary>
/// Variabile isRunning (NON realtime)
/// </summary>
protected bool isRunningState
{
get
{
bool answ = false;
string cStatus = redisMan.getRSV(redKeyProdRunState);
bool.TryParse(cStatus, out answ);
return answ;
}
set
{
redisMan.setRSV(redKeyProdRunState, $"{value}");
}
}
protected string lastArtDescr
{
get => redisMan.getRSV(redKeyProdLastArt);
set => redisMan.setRSV(redKeyProdLastArt, value);
}
/// <summary>
/// Elenco Articoli (x invio verso macchina)
/// </summary>
protected List<ArtRow> ListaArticoli { get; set; } = new List<ArtRow>();
/// <summary>
/// Elenco Job di produzione (x invio verso macchina)
/// </summary>
protected List<JobRow> ListaJobs { get; set; } = new List<JobRow>();
/// <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>
/// Coda massima ammessa per FLog (se = 0 disattivata...)
/// </summary>
protected int maxQueueRawTransf { get; set; } = utils.CRI("maxQueueRawTransf");
/// <summary>
/// Valore MINIMO limite x decidere invio di dati come array Json
/// </summary>
protected int minJsonData { get; set; } = utils.CRI("minJsonData");
protected string nextKeyRich
{
get
{
return $"{dailyKey}{countKeyRichiesta:00}";
}
}
/// <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>
/// Dizionario condizioni di check BIT da usare x valori controllo (es ModBus TCP Imax)
/// </summary>
protected Dictionary<string, BitConditionCheck> OptCheckCondBit { get; set; } = new Dictionary<string, BitConditionCheck>();
/// <summary>
/// Dizionario condizioni di check INT da usare x valori controllo bitmap (es ModBus TCP Zetapack)
/// </summary>
protected Dictionary<string, IntConditionCheck> OptCheckCondInt { get; set; } = new Dictionary<string, IntConditionCheck>();
/// <summary>
/// Elenco di valori da tradurre da valori BIT (es ModBus TCP FIMAT)
/// NB: potrebbero essere contemporaneamente attivi + valori e + traduzioni
/// </summary>
protected Dictionary<string, string> OptVar2TranslBit { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Elenco di valori da tradurre da valori INT esclusivi (es ModBus TCP FIMAT)
/// </summary>
protected Dictionary<string, string> OptVar2TranslInt { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Elenco dei path utili x processing
/// </summary>
protected Dictionary<string, string> pathList { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Gestione archivio serializzato PODL inviati (tramite file temp locale/REDIS)
/// </summary>
protected Dictionary<int, PODLModel> POdlSentFileArch
{
get
{
Dictionary<int, PODLModel> answ = new Dictionary<int, PODLModel>();
string redKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:POdlSent");
string rawData = redisMan.getRSV(redKey);
if (!string.IsNullOrEmpty(rawData))
{
try
{
answ = JsonConvert.DeserializeObject<Dictionary<int, PODLModel>>(rawData);
lgInfo($"Rilettura status POdlSentFileArch: trovati {answ.Count} record");
}
catch (Exception exc)
{
lgError($"Errore in deserializzazione POdlSentFileArch{Environment.NewLine}{exc}");
}
}
return answ;
}
set
{
string rawVal = JsonConvert.SerializeObject(value);
string redKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:POdlSent");
redisMan.setRSV(redKey, rawVal);
lgDebug($"Salvataggio status POdlSentFileArch | {value.Count} record");
}
}
/// <summary>
/// Elenco corrente (in memory) PODL inviati all'impianto
/// chiave: idxPODL
/// valore: record PODL
/// </summary>
protected Dictionary<int, PODLModel> POdlSentList { get; set; } = new Dictionary<int, PODLModel>();
/// <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#valReq#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;
}
}
protected bool queueInEnabCurr
{
get => qInEnabCurr;
set
{
qInEnabCurr = value;
lgInfo($"SET queueInEnabCurr: {value} | {DateTime.Now:HHmmss}");
}
}
/// <summary>
/// Definizioni x replace in file ricette
/// </summary>
protected Dictionary<string, string> RecipeReplRules { get; set; } = new Dictionary<string, string>();
protected string redKeyLogfileAct
{
get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act");
}
protected string redKeyLogfileLast
{
get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Last");
}
protected string redKeyProdLastArt
{
get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:prodDecod:lastArtDesc");
}
protected string redKeyProdRunState
{
get => redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:prodDecod:isRunningState");
}
protected Random rndGen { get; set; } = new Random();
/// <summary>
/// Indica se vada inviata la keyReq richiesta con lo split/autoODL
/// </summary>
protected bool sendKeyRichiesta { get; set; } = false;
/// <summary>
/// test ping all'indirizzo PLC/CNC impostato nei parametri
/// </summary>
/// <returns></returns>
protected IPStatus testPingMachine
{
get
{
IPStatus answ = IPStatus.Unknown;
// se disabilitato salto...
if (pingDisabled)
{
answ = IPStatus.Success;
}
else
{
IPAddress address;
PingReply reply;
using (Ping pingSender = new Ping())
{
address = IPAddress.Loopback;
int pingMsTimeout = cIobConf.pingMsTimeout;
IPAddress.TryParse(cIobConf.cncPingAddr, out address);
try
{
// se != null --> uso address...
if (address != null)
{
reply = pingSender.Send(address, pingMsTimeout);
}
else
{
reply = pingSender.Send(cIobConf.cncIpAddr, pingMsTimeout);
}
}
catch
{
reply = pingSender.Send(IPAddress.Loopback, 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 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 + '#' + valReq + '#' + 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>
/// Classe di base implementazione traduzione di una LUT da memoria come valore BIT a valore
/// esplicito x FLog
/// </summary>
protected virtual void checkTranslateBit()
{
// verifico se devo processare decodifica di qualche valore...
if (OptVar2TranslBit != null && OptVar2TranslBit.Count > 0)
{ }
}
/// <summary>
/// Classe di base implementazione traduzione di una LUT da memoria come valore INT a valore
/// esplicito x FLog
/// </summary>
protected virtual void checkTranslateInt()
{
if (OptVar2TranslInt != null && OptVar2TranslInt.Count > 0)
{ }
}
/// <summary>
/// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile
/// </summary>
protected void checkVetoQueueIn()
{
queueInEnabCurr = dtVetoQueueIN < DateTime.Now;
}
/// <summary>
/// Conversione string row in log generico
/// </summary>
/// <param name="dailyLog"></param>
/// <returns></returns>
protected virtual GenLogRow convertToMachineLog(string dailyLog)
{
GenLogRow answ = new GenLogRow();
if (!string.IsNullOrEmpty(dailyLog))
{
// preventivamente: doppio ";;" --> ";"
dailyLog = dailyLog.Replace(";;", ";");
var sSplit = dailyLog.Split(';');
answ.dtRif = DateTime.ParseExact($"{sSplit[0]} {sSplit[1]}", "yyyy-M-d HH:mm:ss", null);
answ.valString = $"{sSplit[2]};{sSplit[3]}";
}
return answ;
}
/// <summary>
/// Restituisce stato allarmi in formato byte[]
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
protected byte[] currAlarmsState(BaseAlarmConf item)
{
byte[] answ = new byte[item.size];
int currStatus = 0;
int[] listInt = new int[2];
// banchi in array Int16 --> scompongo
for (int i = 0; i < item.size / 2; i++)
{
currStatus = getAlarmStatus(item);
// aggiornamento blink counters dato nuovo valore
item.checkBlinkCounter(i, (uint)currStatus);
// calcolo indice errori
if ((uint)(item.alarmsMask[i] & currStatus) > 0)
{
var currByte = BitConverter.GetBytes(currStatus);
// salvo nell'array di byte
Buffer.BlockCopy(currByte, 0, answ, i * 2, 2);
}
}
return answ;
}
/// <summary>
/// Recupera ODL attivo su impianto
/// </summary>
protected int CurrOdl()
{
int answ = 0;
string sCurrODL = utils.callUrl(urlGetCurrODL);
int.TryParse(sCurrODL, out answ);
return answ;
}
/// <summary>
/// Aggiunge o aggiorna kvp ad un Dict
/// </summary>
/// <param name="currDict"></param>
/// <param name="newKey"></param>
/// <param name="newVal"></param>
protected void DictUpsert(ref Dictionary<string, string> currDict, string newKey, string newVal)
{
if (currDict.ContainsKey(newKey))
{
currDict[newKey] = newVal;
}
else
{
currDict.Add(newKey, newVal);
}
}
/// <summary>
/// Imposta eventuali altri valori default
/// </summary>
protected 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;
}
string s_maxErrCheck = getOptPar("MAX_ERR_CHECK");
if (!string.IsNullOrEmpty(s_maxErrCheck))
{
int numErr = 50;
int.TryParse(s_maxErrCheck, out numErr);
maxErroriCheck = numErr;
}
string s_watchDogPer = getOptPar("WATCHDOG_PERIOD");
if (!string.IsNullOrEmpty(s_watchDogPer))
{
int numPer = 3;
int.TryParse(s_watchDogPer, out numPer);
watchDogPeriod = numPer;
}
// sistemo conf folder acquisizione files
fileImportFolder = getOptPar("FILE_IMPORT_FOLDER");
fileImportType = getOptPar("FILE_IMPORT_TYPE");
fileArchiveFolder = getOptPar("FILE_ARCHIVE_FOLDER");
// fisso tool dir
exclToolDirPath = getOptPar("EXCL_TOOL_PATH");
}
/// <summary>
/// Imposta la memoria PLC in modo forzato (es x oggetti gestiti da FTP come setComm)
/// </summary>
protected virtual void forceMemMap()
{ }
/// <summary>
/// Implementazione di riferimento della verifica stato allarmi
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
protected virtual int getAlarmStatus(BaseAlarmConf item)
{
int answ = 0;
return answ;
}
/// <summary>
/// Implementazione di riferimento della verifica stato allarmi formato UInt
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
protected virtual uint getAlarmStatusUInt(BaseAlarmConf item)
{
uint answ = 0;
return answ;
}
/// <summary>
/// Recupera valore da dizionario CurrProdData o restituisce val default
/// </summary>
/// <param name="key">Chiave richiesta</param>
/// <param name="defVal">Valore di default</param>
/// <returns></returns>
protected string getCurrProdData(string key, string defVal)
{
string answ = "";
if (currProdData.ContainsKey(key))
{
answ = string.IsNullOrEmpty(currProdData[key]) ? defVal : currProdData[key];
}
return answ;
}
/// <summary>
/// Fornisce formato valido x messa in coda nel formato dtEve#valReq#cont
/// </summary>
protected string getEncodSigLog(DateTime DtEvent, int val2log, int counter)
{
string answ = "";
try
{
answ = $"{DtEvent:yyyyMMddHHmmssfff}#{val2log:X2}#{counter}";
}
catch
{ }
return answ;
}
/// <summary>
/// Recupero dati da FluxLog
/// </summary>
/// <param name="resultSet"></param>
/// <param name="codFlux"></param>
/// <returns></returns>
protected string getFluxVal(DossierFluxLogDTO resultSet, string codFlux)
{
string answ = "";
var searchRec = resultSet.ODL.Where(x => x.CodFlux == codFlux).FirstOrDefault();
if (searchRec != null)
{
answ = !string.IsNullOrEmpty(searchRec.ValoreEdit) ? searchRec.ValoreEdit : searchRec.Valore;
}
return answ;
}
/// <summary>
/// Recupero dati da FluxLog formato double
/// </summary>
/// <param name="resultSet"></param>
/// <param name="codFlux"></param>
/// <returns></returns>
protected double getFluxValDouble(DossierFluxLogDTO resultSet, string codFlux)
{
double answ = 0;
var style = NumberStyles.AllowDecimalPoint;
var culture = CultureInfo.InvariantCulture;
string rawVal = getFluxVal(resultSet, codFlux);
if (rawVal.Contains(","))
{
rawVal = rawVal.Replace(",", ".");
}
if (!string.IsNullOrEmpty(rawVal))
{
double.TryParse(rawVal, style, culture, out answ);
}
return answ;
}
/// <summary>
/// Recupero dati da FluxLog formato INT
/// </summary>
/// <param name="resultSet"></param>
/// <param name="codFlux"></param>
/// <returns></returns>
protected int getFluxValInt(DossierFluxLogDTO resultSet, string codFlux)
{
int answ = 0;
double dVal = 0;
var style = NumberStyles.AllowDecimalPoint;
var culture = CultureInfo.InvariantCulture;
string rawVal = getFluxVal(resultSet, codFlux);
if (rawVal.Contains(","))
{
rawVal = rawVal.Replace(",", ".");
}
if (!string.IsNullOrEmpty(rawVal))
{
double.TryParse(rawVal, style, culture, out dVal);
answ = (int)dVal;
}
return answ;
}
/// <summary>
/// Converte un file di log della macchina in un dizionario
/// </summary>
/// <param name="dailyLog"></param>
/// <returns></returns>
protected List<GenLogRow> getGenLogFromMachineLog(string dailyLog)
{
List<GenLogRow> result = new List<GenLogRow>();
//se ho valori.. faccio split!
if (!string.IsNullOrEmpty(dailyLog))
{
var rowList = dailyLog.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
if (rowList != null && rowList.Length > 0)
{
result = rowList
.Where(x => !string.IsNullOrEmpty(x))
.Select(x => convertToMachineLog(x))
.ToList();
}
}
return result;
}
/// <summary>
/// Effettua traduzione da tabella listValue per chiave
/// </summary>
/// <param name="factTable"></param>
/// <param name="key"></param>
/// <returns></returns>
protected string getLV(Dictionary<string, string> currDict, string key)
{
string answ = currDict.ContainsKey(key) ? currDict[key] : key;
return answ;
}
/// <summary>
/// Recupera valore numerico ARTICOLO x salvataggio valori INT
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected string getNumArt(string value)
{
string answ = "";
// cerco in dizionario corrente...
if (DictNumArt.Count > 0 && DictNumArt.ContainsKey(value))
{
answ = DictNumArt[value];
}
// altrimenti chiamo servizio
else
{
// chiamo MP-IO server
if (checkServerAlive)
{
string url2call = $"{urlGetNumArt}?CodArt={value}";
if (verboseLog)
{
lgInfo("chiamata URL " + url2call);
}
var rawData = utils.callUrlNow(url2call);
// deserializzo e recupero KVP...
var dictArtSrv = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawData);
// se fosse una chiamata con valore vuoto --> salvo intera tabella...
if (string.IsNullOrEmpty(value))
{
DictNumArt = dictArtSrv;
}
// altrimenti aggiungo a dizionario
else
{
foreach (var item in dictArtSrv)
{
if (!DictNumArt.ContainsKey(item.Key))
{
DictNumArt.Add(item.Key, item.Value);
}
}
}
// ora cerco valore e restituisco
if (DictNumArt.ContainsKey(value))
{
answ = DictNumArt[value];
}
}
}
return answ;
}
/// <summary>
/// Recupera valore numerico COMMESSA x salvataggio valori INT
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected string getNumComm(string value)
{
string answ = "";
// chiamo MP-IO server
if (checkServerAlive)
{
string url2call = $"{urlGetNumComm}?CodXdl={value}";
if (verboseLog)
{
lgInfo("chiamata URL " + url2call);
}
// è direttamente il risultato!
answ = utils.callUrlNow(url2call);
}
// restituisco
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 == "[]")
{
answ = utils.callUrlNow(url2call);
}
return answ;
}
/// <summary>
/// Recupera file log da analizzare
/// </summary>
/// <returns></returns>
protected virtual bool getRemoteLog()
{
bool fatto = false;
return fatto;
}
/// <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>
/// Verifica presenza eventuali allarmi
/// </summary>
/// <returns></returns>
protected virtual bool hasAlarms()
{
bool answ = false;
// 2023.12.20: aggiunta gestione secondo tipo allarmi come elenco OPC-UA (BLM/Adige)
if (alarmType == AlarmBlockType.Bitmap)
{
int numErrors = 0;
int currStatus = 0;
ushort full16 = 65535;
int[] listInt = new int[2];
if (alarmMaps != null)
{
// leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme...
foreach (var item in alarmMaps)
{
// banchi in array Int16 --> scompongo
for (int i = 0; i < item.size / 2; i++)
{
currStatus = getAlarmStatus(item);
// ora filtro x l'i-esimo banco a 16 bit...
if (i == 0)
{
currStatus = (ushort)(currStatus & full16);
}
else
{
var temp = currStatus >> 16;
currStatus = (ushort)temp;
}
// aggiornamento blink counters dato nuovo valore
item.checkBlinkCounter(i, (uint)currStatus);
// calcolo indice errori
if ((uint)(item.alarmsMask[i] & currStatus) > 0)
{
numErrors++;
}
// verifico SE sia variato... confronto allarmi filtrato stile blink per
// bit status:
// - allarmi che iniziano per # IGNORATI
// - altri allarmi con un countdown da MAX_COUNTER_BLINK a 0 per il fronte
// di discesa
if (item.isChanged(i, (uint)currStatus))
{
// registro gli allarmi attivi e trasmetto...
if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], (uint)(item.alarmsMask[i] & currStatus), item.messages))
{
// se inviato --> salvo stato da current...
item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus));
}
}
}
}
}
answ = numErrors > 0;
}
// gestione allarmi come elenco
else if (alarmType == AlarmBlockType.ActiveList)
{
// verifico SE ho allarmi
if (alarmMaps != null && alarmMaps.Count > 0)
{
// ciclo x ogni blocco
foreach (var item in alarmMaps)
{
// cerco se sia superiore al livello minimo da conf
if (item.blockLevel >= alarmLevelMin)
{
// controllo variabile counter SE > 0...
int numCount = getAlarmStatus(item);
answ = numCount > 0;
}
}
}
}
return answ;
}
/// <summary>
/// Recupera da server set di dati specifici x IOB
/// </summary>
/// <returns></returns>
protected virtual bool iobGetDataFromServer()
{
return false;
}
/// <summary>
/// Effettua upload verso server FTP della macchina dei files nella folder indicata
/// </summary>
/// <param name="folderDir"></param>
/// <returns></returns>
protected virtual bool iobSendFTP(string folderDir)
{
bool answ = false;
//leggo CONF
string ftpServ = getOptPar("FTP_SERVER");
string ftpUser = getOptPar("FTP_USER");
string ftpPass = getOptPar("FTP_PWD");
string ftpCert = getOptPar("FTP_CERT");
string doSkip = getOptPar("FTP_SKIP");
string remDir = getOptPar("FTP_REM_DIR");
string locDir = getOptPar("FTP_LOC_DIR");
// procedo SE TROVO conf...
if (string.IsNullOrEmpty($"{ftpServ}{ftpUser}{ftpPass}"))
{
lgError($"Impossibile eseguire iobSendFTP x mancanza parametri | ftpServ: {ftpServ} | ftpUser: {ftpUser} | ftpPass: {ftpPass}");
}
else
{
bool ftpSkip = false;
bool.TryParse(doSkip, out ftpSkip);
var ftpClient = new Manager(ftpServ, ftpUser, ftpPass, ftpCert, ftpSkip);
var testServer = ftpClient.ServerOk();
if (testServer)
{
lgInfo($"FTP: server found at {ftpServ}");
var srvType = ftpClient.ServerType();
lgInfo($"FTP Server type: {srvType}");
var preTest = ftpClient.DirExists(remDir);
if (!preTest)
{
var dirCreate = ftpClient.CreateDir(remDir);
lgInfo($"FTP: created remote dir {remDir}");
}
// test directory...
string basePath = Application.StartupPath;
string localPath = Path.Combine(basePath, locDir);
lgInfo($"basePath: {basePath} | localPath: {localPath}");
var fileList = Directory.GetFiles(localPath, "*.csv");
int numfile = 0;
foreach (var file in fileList)
{
string fName = file.Split('\\').Last();
string remName = $"{remDir}\\{fName}";
ftpClient.SendFile(file, remName);
numfile++;
}
var dirUploaded = (numfile == fileList.Count());
if (dirUploaded)
{
lgInfo($"FTP: uploaded dir content {locDir} --> {remDir}");
}
// se ok --> sposto invio
DateTime adesso = DateTime.Now;
string archBasePath = Path.Combine(basePath, "DATA", "HIST", $"{adesso:yyyy}");
baseUtils.checkDir(archBasePath);
string archPath = Path.Combine(archBasePath, $"{adesso:MMddHHmmss}");
baseUtils.checkDir(archPath);
lgInfo($"Richiesta Dir Move | {localPath} | {archPath}");
foreach (var item in fileList)
{
string fName = item.Split('\\').Last();
string destName = $"{archPath}\\{fName}";
File.Move(item, destName);
}
//Directory.Move(localPath, archBasePath);
lgInfo($"FTP: Archived dir content {localPath} --> {archPath}");
}
}
return answ;
}
/// <summary>
/// Prepara files CSV da inviare alla macchina
/// </summary>
protected virtual bool iobWriteLocalCSV()
{
bool answ = false;
// salvo articoli
string locDir = getOptPar("FTP_LOC_DIR");
string sAddHeader = getOptPar("CSV_ADD_HEADER");
bool addHeader = false;
if (!string.IsNullOrEmpty(sAddHeader))
{
bool.TryParse(sAddHeader, out addHeader);
}
string basePath = Application.StartupPath;
string tempDir = Path.Combine(basePath, locDir);
lgInfo($"iobWriteLocalCSV | locDir: {locDir} | sAddHeader: {sAddHeader} | tempDir: {tempDir}");
baseUtils.checkDir(tempDir);
string filePath = Path.Combine(tempDir, "articoli.csv");
answ = DataExport.SaveToCsv(ListaArticoli, filePath, addHeader);
if (answ)
{
lgInfo($"CSV: saved ART file as articoli.csv at {filePath}");
// salvo PODL
string csvName = $"{DateTime.Now:dd-MM-yyyy}.csv";
filePath = Path.Combine(tempDir, $"{csvName}");
answ = DataExport.SaveToCsv(ListaJobs, filePath, addHeader);
if (answ)
{
lgInfo($"CSV: saved PODL file {csvName} at {filePath}");
}
}
return answ;
}
/// <summary>
/// Effettua traduzione ITEM da LUT parametrica (keyReq: tipo+id) del file di conf, se non
/// trovo uso keyReq
/// </summary>
/// <param name="tipo"></param>
/// <param name="id"></param>
/// <returns></returns>
protected virtual string itemTranslation(string tipo, string id)
{
string answ = "";
if (cIobConf.itemTranslation != null)
{
string lemma = id;
if (!string.IsNullOrEmpty(tipo))
{
lemma = $"{tipo}_{id}";
}
// cerco nel dizionario delle traduzioni SE esiste un valore e prendo quello,
// altrimenti uso il lemma...
if (cIobConf.itemTranslation.ContainsKey(lemma))
{
answ = cIobConf.itemTranslation[lemma];
}
else
{
answ = lemma;
}
}
return answ;
}
/// <summary>
/// Effettua traduzione ITEM da LUT parametrica (keyReq: lemma) del file di conf, se non
/// trovo uso keyReq
/// </summary>
/// <param name="lemma"></param>
/// <returns></returns>
protected virtual string itemTranslation(string lemma)
{
string answ = "";
if (cIobConf.itemTranslation != null)
{
if (cIobConf.itemTranslation.ContainsKey(lemma))
{
answ = cIobConf.itemTranslation[lemma];
}
else
{
answ = lemma;
}
}
return answ;
}
/// <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);
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("loadMemConf.01");
lgInfoStartup("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");
}
lgInfo("loadMemConf.02");
// gruppo macchina
CodGruppoIob = getOptPar("CodGruppoIob");
CodGruppoIob = string.IsNullOrEmpty(CodGruppoIob) ? "ND-00" : CodGruppoIob;
// gestione completa PODL
string sEnabelPodlManFull = getOptPar("EnabelPodlManFull");
if (!string.IsNullOrEmpty(sEnabelPodlManFull))
{
bool.TryParse(sEnabelPodlManFull, out EnabelPodlManFull);
}
// abilitazione invio WDST disabilitazione controllo range valori DynData
if (!string.IsNullOrEmpty(getOptPar("DISABLE_SEND_WDST")))
{
bool checkDisWdst = false;
bool.TryParse(getOptPar("DISABLE_SEND_WDST"), out checkDisWdst);
disableWdst = checkDisWdst;
}
// disabilitazione controllo range valori DynData
if (!string.IsNullOrEmpty(getOptPar("disDynDataRangeCheck")))
{
bool checkDis = false;
bool.TryParse(getOptPar("disDynDataRangeCheck"), out checkDis);
disDynDataRangeCheck = checkDis;
}
lgInfo("loadMemConf.03");
// inizializzo LUT decodifica PARAMETRI
string jsonParams = getOptPar("PARAM_CONF");
if (!string.IsNullOrEmpty(jsonParams))
{
string jsonFileName = $"{Application.StartupPath}\\DATA\\CONF\\{jsonParams}";
lgInfoStartup($"Apertura file {jsonFileName}");
StreamReader reader = new StreamReader(jsonFileName);
string jsonData = reader.ReadToEnd();
if (!string.IsNullOrEmpty(jsonData))
{
lgInfoStartup($"File json PARAMETRI composto da {jsonData.Length} caratteri");
lgInfo("loadMemConf.04");
try
{
memMap = JsonConvert.DeserializeObject<plcMemMapExt>(jsonData);
setupMemMap();
setupOptMemPar();
setupFileDecod();
setupSpecialParams();
lgInfo("loadMemConf.05");
}
catch (Exception exc)
{
lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}");
}
}
else
{
lgError("Errore in loadMemConf: file json vuoto!");
}
reader.Dispose();
}
else
{
lgInfoStartup("loadMemConf: non trovata opzione PARAM_CONF in file INI");
}
lgInfo("loadMemConf.06");
// inizializzo LUT decodifica ALLARMI
string jsonAlarms = getOptPar("ALARM_CONF");
if (!string.IsNullOrEmpty(jsonAlarms))
{
string jsonFileName = $"{Application.StartupPath}\\DATA\\CONF\\{jsonAlarms}";
lgInfoStartup($"Apertura file {jsonFileName}");
StreamReader reader = new StreamReader(jsonFileName);
string jsonData = reader.ReadToEnd();
reader.Dispose();
if (!string.IsNullOrEmpty(jsonData))
{
lgInfoStartup($"File json ALLARMI composto da {jsonData.Length} caratteri");
try
{
lgInfo("loadMemConf | Inizio setup allarmi");
alarmMaps = JsonConvert.DeserializeObject<List<BaseAlarmConf>>(jsonData);
setupAlarmMap();
// leggo il tipo gestione allarmi..
string sAlarmType = getOptJsonKVP("alarmType");
if (!string.IsNullOrEmpty(sAlarmType))
{
alarmType = (AlarmBlockType)Enum.Parse(typeof(AlarmBlockType), sAlarmType);
}
string sAlarmLMin = getOptJsonKVP("alarmLevelMin");
if (!string.IsNullOrEmpty(sAlarmLMin))
{
alarmLevelMin = (AlarmLevel)Enum.Parse(typeof(AlarmLevel), sAlarmLMin);
}
lgInfo("loadMemConf | Fine setup allarmi");
}
catch (Exception exc)
{
lgError($"Eccezione in decodifica conf json degli ALLARMI{Environment.NewLine}{exc}");
}
}
else
{
lgError("Errore in loadMemConf: file ALLARMI json vuoto!");
}
}
else
{
lgInfoStartup("loadMemConf: non trovata opzione ALARM_CONF in file INI");
}
// loggo
lgInfo("loadMemConf.07");
lgInfoStartup("DONE loadMemConf");
}
/// <summary>
/// Recupera elenco PODL assegnabili
/// </summary>
/// <returns></returns>
protected List<PODLModel> MachineNextPodl()
{
List<PODLModel> reqPOdlList = new List<PODLModel>();
// per prima cosa recupero elenco PODL da gestire....
var rawListPODL = callUrl(urlGetCurrPODL, false);
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
reqPOdlList = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
}
return reqPOdlList;
}
/// <summary>
/// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
/// NB: deve resettare reqValue
/// </summary>
/// <param name="updatedPar"></param>
protected virtual void plcWriteParams(ref List<objItem> updatedPar)
{
// non faccio nulla di base...
}
/// <summary>
/// Effettua sync dati da e verso impianto
/// </summary>
protected virtual void processDataSync()
{
}
/// <summary>
/// Effettua eventuale file import, archiviando file importati
/// - es gestione file excel di Giacovelli
/// - es gestione ritorno ricette FIMAT
/// </summary>
/// <returns></returns>
protected virtual bool processFileImport()
{
bool answ = false;
/* ------------------------------------------
* esecuzione cablata, si potrebbe costruire una cosa più flessibile tramite conf:
* Oggetto MultiStepFileImport
* {
* StepName: Step01
* ProcType: Excel2Json
* FolderIn: xxx
* FolderOut: xxx
* FolderErr: xxx
* },{
* StepName: Step02
* ProcType: Json2RegGiac
* FolderIn: xxx
* FolderOut: xxx
* FolderErr: xxx
* }
* ------------------------------------------*/
if (!string.IsNullOrEmpty(fileImportFolder))
{
// verifico esistenza folders...
if (Directory.Exists(fileImportFolder))
{
string errore = "";
// verifico archivio
string dirArchive = Path.Combine(fileImportFolder, "archive");
baseUtils.checkDir(dirArchive);
string dirConvert = Path.Combine(fileImportFolder, "converted");
baseUtils.checkDir(dirConvert);
Dictionary<string, TimeSpan> statsColl = new Dictionary<string, TimeSpan>();
// cerco files da convertire
var listFiles2Conv = Directory.GetFiles(fileImportFolder, fileImportType);
// se li trovo --> chiamo import!
if (listFiles2Conv != null && listFiles2Conv.Length > 0)
{
// leggo file conf di riferimento
FileProcMan fpm = new FileProcMan(dirArchive, dirConvert, exclToolDirPath, "ExcImport.exe", "RefExcConv.json", "DB Loco");
int idxODL = 0;
foreach (var fileItem in listFiles2Conv)
{
// calcolo ODL x il file...
string fileName = Path.GetFileNameWithoutExtension(fileItem);
DateTime dataDoc = DateTime.Today;
DateTime.TryParse(fileName, out dataDoc);
// cerco lotto x giornata...
string sIdxODL = utils.callUrl(urlGetOdlAtDate(dataDoc));
int.TryParse(sIdxODL, out idxODL);
// chiamo conversione
TimeSpan timeElaps = fpm.doProcess(fileItem, idxODL);
statsColl.Add($"ExcImport conversion executed for {fileItem}", timeElaps);
lgTrace(($"ExcImport conversion executed for {fileItem} | {timeElaps.Milliseconds}ms"));
}
}
// cerco i file json x invio
var listFiles2Upload = Directory.GetFiles(dirConvert, "*.json");
// se li trovo --> chiamo import!
if (listFiles2Upload != null && listFiles2Upload.Length > 0)
{
foreach (var fileItem in listFiles2Upload)
{
// leggo il file json
Dictionary<int, BatchRec> list2Send = new Dictionary<int, BatchRec>();
string rawData = File.ReadAllText(fileItem);
if (!string.IsNullOrEmpty(rawData))
{
var convData = JsonConvert.DeserializeObject<Dictionary<int, BatchRec>>(rawData);
if (convData != null)
{
list2Send = convData;
// ora accodo contenuto file json...
accodaRawData(rawTransfType.RegGiacenze, list2Send);
}
}
// processo invio: provo forzare send...
answ = svuotaCodaRawTransf(true);
if (!answ)
{
errore = $"Errore in fase di invio dati svuotaCodaRawTransf x {fileItem}";
}
// archivio il file
else
{
// sposto file...
File.Move(fileItem, $"{dirArchive}/{Path.GetFileName(fileItem)}");
// fixme todo !!! eliminare json?!?!?
}
// log eventuali errori
if (!answ)
{
string dirError = $"{fileImportFolder}/errors";
baseUtils.checkDir(dirError);
// sposto file...
File.Move(fileItem, $"{dirError}/{Path.GetFileName(fileItem)}");
// segno errore!
string fileError = $"{fileImportFolder}/errors.log";
File.AppendAllText(fileError, errore);
}
}
}
}
}
return answ;
}
/// <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>();
string currOut = "";
// 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;
currOut = $" | Parameter {item.uid} | {item.value} --> {item.reqValue}";
// sistemo valori
item.value = item.reqValue;
lgInfo($"Richiesta update parametro {currOut}");
// salvo in lista da ritrasmettere
updatedPar.Add(item);
}
else
{
currOut = $" | Error: parameter {item.uid} not found";
lgError($"Errore {currOut}");
}
// accodo in stringa taskVal...
answ += currOut;
}
// richiamo scrittura parametri su PLC
plcWriteParams(ref updatedPar);
// invio su cloud parametri!
string rawData = JsonConvert.SerializeObject(updatedPar);
utils.callUrl($"{urlUpdateWriteParams}", rawData);
lgInfo($"Notifica a server scrittura {updatedPar.Count} parametri");
}
}
catch (Exception exc)
{
lgError($"Eccezione in processMemWriteRequests:{Environment.NewLine}{exc}");
}
}
else
{
lgError("Non è stata ricevuta risposta x task da eseguire");
}
return answ;
}
/// <summary>
/// Cerca di recuperare i file generati dall'impianto in merito al processing degli ordini
/// </summary>
/// <returns></returns>
/// <summary>
/// Processing di dati "OtherInfo" da implementare caso x caso (qui riportato caso FIMAT ricette...)
/// </summary>
/// <param name="keyReq"></param>
/// <param name="valReq"></param>
protected virtual bool processOtherInfo(string keyReq, string valReq)
{
bool answ = false;
// test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT...
if (hasRecipe)
{
string archBasePath = Path.Combine(pathList["path-locBase"], pathList["path-03-Recv"]);
string reportBasePath = pathList["path-outReport"];
baseUtils.checkDir(reportBasePath);
// verifico x cosa è stato richiesto attività (che esista la folder...)
string folderPath = Path.Combine(archBasePath, keyReq);
if (Directory.Exists(folderPath))
{
// calcolo il nome zip di destinazione finale
int anno = DateTime.Today.Year;
int.TryParse(keyReq.Substring(0, 4), out anno);
string zipDir = Path.Combine(archBasePath, $"{anno}");
baseUtils.checkDir(zipDir);
string zipName = Path.Combine(zipDir, $"{keyReq}.zip");
// gestione report OUT
string reportPath = Path.Combine(reportBasePath, $"{keyReq}");
// verifico il tipo di attività e procedo
switch (valReq)
{
case "Arch":
// solo archiviazione ZIP della folder...
answ = doZipArchiveFolder(folderPath, zipName);
// se fatto --> elimino record da REDIS (locale e remoto)
if (answ)
{
RecipeRemoveWeekStatus(keyReq);
}
break;
case "SendArch":
// preparazione file di resoconto contumi
answ = RecipeDoConsumeReport(folderPath, reportPath);
if (answ)
{
// infine archiviazione ZIP della folder...
bool okArchive = doZipArchiveFolder(folderPath, zipName);
// se fatto --> elimino record da REDIS (locale e remoto)
if (okArchive)
{
RecipeRemoveWeekStatus(keyReq);
}
}
break;
default:
break;
}
}
}
return answ;
}
protected virtual bool processRecipeFileRet()
{
bool answ = false;
// test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT...
if (hasRecipe)
{
// recupera i NUOVI file e li sposta in folder locale temp
string remoPath = Path.Combine(pathList["path-locBase"], pathList["path-05-remExe"]);
string archBasePath = Path.Combine(pathList["path-locBase"], pathList["path-03-Recv"]);
string tempPath = Path.Combine(archBasePath, "TEMP");
baseUtils.checkDir(tempPath);
bool okRetrieve = RecipeTaskDoneRetrieve(remoPath, tempPath);
// ora cerca nella folder locale e processa i files...
bool okCheck = RecipeDoCheckFileProc(tempPath, archBasePath);
// verifica se sia da creare/inviare il record settimanale di consumo (dopo la
// mezzanotte del lunedì inizio settimana)
bool okSent = RecipeDoProcCons();
}
return answ;
}
/// <summary>
/// Sync archivio ricette (Macchina -- MES)
/// </summary>
/// <returns></returns>
protected virtual bool processRecipeSyncArch()
{
bool answ = false;
// test file import da conf... se hasRecipe=true --> modalità Tenditalia/FIMAT...
if (hasRecipe)
{
DateTime adesso = DateTime.Now;
// verifico veto sync ricette (x non ripetere troppo spesso)
if (adesso > dtVetoCheckSyncRecipe)
{
// effettua sync eventuali NUOVI file ricette
bool okSync = RecipeDoSyncRecipe();
// imposto veto a 1h...
dtVetoCheckSyncRecipe = adesso.AddHours(1);
}
}
return answ;
}
/// <summary>
/// Effettua task òettura a bassa velocità
/// </summary>
/// <returns></returns>
protected virtual bool processSlowDataRead()
{
bool answ = false;
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>
/// Registra i file in directory suddivise x anno/settimana e restitusice elenco
/// </summary>
/// <param name="recipeFile">Percorso file XML</param>
/// <param name="archBasePath">Percorso base archivi</param>
/// <returns></returns>
protected List<string> RecipeDoArchiveWeek(string recipeFile, string archBasePath)
{
List<string> weeksProc = new List<string>();
// init var
int week = 0;
string archPath = "";
Calendar cal = new CultureInfo("it-IT").Calendar;
// leggo contenuto XML
string rawData = File.ReadAllText(recipeFile);
// deserializza file XML x recuperare righe consumo
XmlSerializer serializer = new XmlSerializer(typeof(ARecipe));
using (StringReader reader = new StringReader(rawData))
{
try
{
var currRecipe = (ARecipe)serializer.Deserialize(reader);
if (currRecipe != null)
{
// recupero data-ora ricetta da campo string
DateTime recExeDt = DateTime.Today;
DateTime.TryParse(currRecipe.DesRecipe.DesData.Date, out recExeDt);
// calcolo week da data-ora file...
week = cal.GetWeekOfYear(recExeDt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
// verifico se ci sia la settimana indicata in elenco...
string currWeek = $"{recExeDt:yyyy}-{week:00}";
if (!weeksProc.Contains(currWeek))
{
weeksProc.Add(currWeek);
}
// sposto file x in area week
string rName = Path.GetFileName(recipeFile);
archPath = Path.Combine(archBasePath, $"{recExeDt:yyyy}-{week:00}");
baseUtils.checkDir(archPath);
string destPath = Path.Combine(archPath, rName);
// sposto files
File.Move(recipeFile, destPath);
}
}
catch (Exception exc)
{
lgError($"Eccezione in RecipeDoArchiveWeek{Environment.NewLine}{exc}");
}
}
return weeksProc;
}
/// <summary>
/// Verifica i file recuperati
/// - PODL da inviare a MAPO
/// - accumulo consumo materiali
/// </summary>
/// <param name="localPath"></param>
/// <param name="archBasePath"></param>
/// <returns></returns>
protected virtual bool RecipeDoCheckFileProc(string localPath, string archBasePath)
{
bool answ = false;
List<string> weekProcNew = new List<string>();
// recupero elenco files...
var fileList = Directory.GetFiles(localPath);
if (fileList != null && fileList.Count() > 0)
{
foreach (var recipeFile in fileList)
{
// processo eventuale apertura/chiusura PODL
bool okPOdl = RecipeDoProcessPODL(recipeFile);
// processa i file archiviando x settimana
var procWeeks = RecipeDoArchiveWeek(recipeFile, archBasePath);
// aggiungo all'elenco settimane SE mancassero le sett inserite
foreach (var item in procWeeks)
{
if (!weekProcNew.Contains(item))
{
weekProcNew.Add(item);
}
}
}
if (weekProcNew.Count > 0)
{
// predispongo azioni di base ammesse
Dictionary<string, string> redHashWeek = new Dictionary<string, string>();
Dictionary<string, string> stdActList = new Dictionary<string, string>();
stdActList.Add("Arch", "Archivia");
stdActList.Add("SendArch", "Invia + Archivia");
List<PeriodInfo> weekHashUpdate = new List<PeriodInfo>();
// aggiorna in REDIS (hashList) status delle settimane coinvolte
foreach (var cWeek in weekProcNew)
{
string weekPath = Path.Combine(archBasePath, $"{cWeek}");
var weekFileList = Directory.GetFiles(weekPath, "*.xml");
PeriodInfo cPerInfo = new PeriodInfo()
{
NumFilesReceived = weekFileList.Count(),
CurrStatus = "Ricevute",
ActionList = stdActList
};
string rawWeek = JsonConvert.SerializeObject(cPerInfo);
redHashWeek.Add(cWeek, rawWeek);
}
// salvo in redis...
string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats");
var okHashDict = redisMan.redSaveHashDict(fullKey, redHashWeek);
// invio ANCHE in MP-IO l'update delle info...
string remUrl = urlSetHashDict;
string dictPayload = JsonConvert.SerializeObject(redHashWeek);
callUrlWithPayload(remUrl, dictPayload, false);
}
}
return answ;
}
/// <summary>
/// Verifica se si debba registrare/inviare il report periodico di consumo indicato
/// </summary>
/// <returns></returns>
protected bool RecipeDoProcCons()
{
bool answ = false;
// calcolo quale sia la settimana corrente...
int week = 0;
DateTime adesso = DateTime.Now;
Calendar cal = new CultureInfo("it-IT").Calendar;
// calcolo week da data-ora file...
week = cal.GetWeekOfYear(adesso, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
// verifico se ci sia la settimana indicata in elenco...
string currWeek = $"{adesso:yyyy}-{week:00}";
// recupero elenco delle settimane da processare da redis/WeekStats
string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats");
Dictionary<string, string> currStats = redisMan.redGetHashDict(fullKey);
if (currStats != null && currStats.Count > 0)
{
// definisco il DICT delle settimane da processare (= settimane passate, NON la corrente...)
foreach (var weekData in currStats)
{
if (weekData.Key != currWeek)
{
// ...x ogni settimana PASSATA effettua SendArch... --> lancia un task!
processOtherInfo(weekData.Key, "SendArch");
}
}
answ = true;
}
return answ;
}
/// <summary>
/// Acquisisce eventuali nuove ricette
/// </summary>
/// <returns></returns>
protected bool RecipeDoSyncRecipe()
{
bool answ = false;
DateTime asdesso = DateTime.Now;
try
{
string recLocalArchPath = Path.Combine(pathList["path-locBase"], pathList["path-00-Arch"]);
string recRemArchPath = pathList["path-06-remRec"];
// leggo da redis (se disponibile) elenco hast file/MD5....
string fullKey = redisMan.redHash($"IOB:Remote:{cIobConf.codIOB}:FileCheck");
Dictionary<string, string> dictRecMd5 = redisMan.redGetHashDict(fullKey);
// leggo la directory remota...
var remoteList = Directory.GetFiles(recRemArchPath);
string fileName = "";
string newFilePath = "";
bool doAcquire = false;
int recNum = 0;
int newNum = 0;
string currMd5;
foreach (var remoteFile in remoteList)
{
recNum = 0;
doAcquire = false;
// verifico MD5...
currMd5 = baseUtils.GetFileMd5(remoteFile);
fileName = Path.GetFileName(remoteFile);
// verifo se c'è il file...
if (!dictRecMd5.ContainsKey(fileName))
{
dictRecMd5.Add(fileName, currMd5);
doAcquire = true;
}
else
{
//// verifico SE sia modificato da meno di 30 gg...
//DateTime lastMod = File.GetLastWriteTime(remoteFile);
if (dictRecMd5[fileName] != currMd5)
{
dictRecMd5[fileName] = currMd5;
doAcquire = true;
}
}
// se devo aggiungere processo --> fix numRicetta + 10'000
if (doAcquire)
{
answ = true;
// leggo contenuto...
string recContent = File.ReadAllText(remoteFile);
// calcolo nuovo nome
var namePart = fileName.Split('.');
int.TryParse(namePart[0], out recNum);
newNum = recNum + 10000;
// modifico ricetta
recContent = recContent.Replace($"<RecName>{namePart[0]}</RecName>", $"<RecName>{newNum}</RecName>");
// salvo ricetta modificata in archivio
newFilePath = Path.Combine(recLocalArchPath, $"{newNum}.xml");
// elimino eventuale file precedente...
if (File.Exists(newFilePath))
{
File.Delete(newFilePath);
}
File.WriteAllText(newFilePath, recContent);
}
}
// se ho importato qualcosa...
if (answ)
{
// salvo hash dei files...
redisMan.redSaveHashDict(fullKey, dictRecMd5);
}
}
catch (Exception exc)
{
lgError($"Eccezione durante RecipeDoSyncRecipe{Environment.NewLine}{exc}");
}
return answ;
}
/// <summary>
/// restitusice i record di consumo dei materiali associati al file:
/// </summary>
/// <param name="recipeFile">Percorso file XML</param>
/// <returns></returns>
protected List<ConsRec> RecipeGetCons(string recipeFile)
{
List<ConsRec> listConsumi = new List<ConsRec>();
// leggo contenuto XML
string rawData = File.ReadAllText(recipeFile);
// deserializza file XML x recuperare righe consumo
XmlSerializer serializer = new XmlSerializer(typeof(ARecipe));
using (StringReader reader = new StringReader(rawData))
{
try
{
var currRecipe = (ARecipe)serializer.Deserialize(reader);
if (currRecipe != null)
{
if (currRecipe.ColRecipe != null && currRecipe.ColRecipe.Count > 0)
{
List<ColData> RigheConsumi = new List<ColData>();
foreach (var rigaComp in currRecipe.ColRecipe)
{
ColData datiComp = rigaComp.ColData;
RigheConsumi.Add(datiComp);
}
// recupero data-ora ricetta da campo string
DateTime recExeDt = DateTime.Today;
DateTime.TryParse(currRecipe.DesRecipe.DesData.Date, out recExeDt);
// converto le righe di consumo ColData in ConsRec
listConsumi = RigheConsumi.Select(x => new ConsRec()
{
FileRef = recipeFile,
DtRif = recExeDt,
ColourCode = x.ColourCode,
Description = x.Description,
WeightGrTot = x.Weightgrtotal
}).ToList();
}
}
}
catch (Exception exc)
{
lgError($"Eccezione in RecipeGetCons{Environment.NewLine}{exc}");
}
}
return listConsumi;
}
/// <summary>
/// Prepara la ricetta indicata partendo dai dati della ricetta template + dati ODL di riferimento
/// </summary>
/// <param name="recFilePath">Path del file template della ricetta</param>
/// <param name="podlReq">Record del PODL da inviare</param>
/// <returns>Path della ricetta da inviare</returns>
protected virtual bool RecipePrepare(string tempPath, string recFilePath, PODLModel podlReq)
{
bool fatto = false;
// leggo il file template ricetta
var rawData = File.ReadAllText(recFilePath);
string fixContent = rawData;
// eseguo sostituzioni
foreach (var rule in RecipeReplRules)
{
string replVal = rule.Value;
// calcolo la sostituzione per i valori SPECIFICI...
if (replVal.Contains("{{PODL}}"))
{
replVal = replVal.Replace("{{PODL}}", $"PODL{podlReq.IdxPromessa:00000000}");
}
if (replVal.Contains("{{Qty}}"))
{
replVal = replVal.Replace("{{Qty}}", $"{podlReq.NumPezzi}");
}
if (replVal.Contains("{{Note}}"))
{
replVal = replVal.Replace("{{Note}}", $"{podlReq.Note}");
}
fixContent = fixContent.Replace(rule.Key, replVal);
}
// scrivo file (secondo quantità...)!
string destPath = Path.Combine(tempPath, $"PODL{podlReq.IdxPromessa:00000000}.xml");
if (podlReq.NumPezzi <= maxQtyPerFile)
{
File.WriteAllText(destPath, fixContent);
fatto = true;
}
else
{
int numReq = (int)Math.Ceiling((double)podlReq.NumPezzi / maxQtyPerFile);
// splitto e scrivo...
for (int i = 1; i <= numReq; i++)
{
destPath = Path.Combine(tempPath, $"PODL{podlReq.IdxPromessa:00000000}-{i}.xml");
File.WriteAllText(destPath, fixContent);
}
fatto = true;
}
return fatto;
}
/// <summary>
/// Prepara le ricette dato path temp scaricando elenco PODL
/// </summary>
/// <param name="tempPath">
/// Percorso temp di appoggio x preparare ricette (compreso nome macchina)
/// </param>
/// <param name="useLocal">
/// Indica se usare le copie locali delle ricette oppure richiedere da remoto (REST get)
/// </param>
/// <returns></returns>
protected virtual bool RecipeReqWriteLocal(string tempPath, bool useLocal)
{
bool fatto = false;
lgTrace($"RecipeReqWriteLocal start | tempPath {tempPath} | useLocal {useLocal}");
List<PODLModel> reqPOdlList = new List<PODLModel>();
// per prima cosa recupero elenco PODL da gestire....
var rawListPODL = callUrl(urlGetCurrPODL, false);
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
reqPOdlList = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
}
// proseguo se ne ho trovati...
if (reqPOdlList.Count > 0)
{
// in questo elenco devo considerare solo PODL ATTIVI...
List<PODLModel> actPOdlList = reqPOdlList.Where(x => x.Attivabile).ToList();
// da questo elenco,se è rimasto qualcosa, ciclo x ricette da inviare
foreach (var actPodlReq in actPOdlList)
{
// devo escludere i PODL già gestiti
if (!POdlSentList.ContainsKey(actPodlReq.IdxPromessa))
{
string recFilePath = "";
try
{
// calcolo path ricetta...
recFilePath = Path.Combine(pathList["path-locBase"], pathList["path-00-Arch"], actPodlReq.Recipe);
// preparo file ricetta x PODL
bool fileOk = RecipePrepare(tempPath, recFilePath, actPodlReq);
if (fileOk)
{
// aggiungo PODL ad elenco dei processati
POdlSentList.Add(actPodlReq.IdxPromessa, actPodlReq);
// indico okReport (almeno per 1 è ok...)
fatto = true;
}
}
catch (Exception exc)
{
lgError($"recipeReqWriteLocal | Errore durante ciclo verifica ricetta | idxPODL {actPodlReq.IdxPromessa} | recFilePath {recFilePath}{Environment.NewLine}{exc}");
}
}
}
// salvo su file elenco PODL gestiti/inviati
POdlSentFileArch = POdlSentList;
lgInfo($"RecipeReqWriteLocal | reqPOdlList: {reqPOdlList.Count} | actPOdlList: {actPOdlList.Count} | preparate: {POdlSentList.Count}");
}
return fatto;
}
/// <summary>
/// Effettua invio delle ricette alla macchina
/// </summary>
/// <param name="localPath">Area dove si trovano i fiel da trasmettere</param>
/// <param name="localArch">Area archivio</param>
/// <param name="remotePath">Area remota dove inviare files</param>
/// <returns></returns>
protected bool RecipeSend(string localPath, string localArch, string remotePath)
{
bool fatto = false;
lgTrace($"RecipeSend start | localPath {localPath} | localArch {localArch} | remotePath {remotePath}");
// recupero elenco files...
var fileList = Directory.GetFiles(localPath);
string archName = "";
string destName = "";
if (fileList != null && fileList.Count() > 0)
{
int nMove = 0;
foreach (var file in fileList)
{
string fileName = Path.GetFileName(file);
archName = Path.Combine(localArch, fileName);
destName = Path.Combine(remotePath, fileName);
lgInfo($"RecipeSend | fileName: {fileName} | archName: {archName} | destName: {destName} ");
File.Copy(file, destName, true);
File.Move(file, archName);
nMove++;
}
fatto = true;
lgInfo($"RecipeSend | trovate {fileList.Count()} file | {nMove} trasferiti");
}
return fatto;
}
/// <summary>
/// Effettua recupero in locale dei task eseguiti dalla macchina
/// </summary>
/// <param name="remotePath">Area remota da dove recuperare files</param>
/// <param name="localPath">Area dove parcheggiare temporaneamente i fiels x processing</param>
/// <returns></returns>
protected virtual bool RecipeTaskDoneRetrieve(string remotePath, string localPath)
{
bool fatto = false;
// recupero elenco files...
var fileList = Directory.GetFiles(remotePath);
string destName = "";
if (fileList != null && fileList.Count() > 0)
{
foreach (var file in fileList)
{
string fileName = Path.GetFileName(file);
destName = Path.Combine(localPath, fileName);
lgInfo($"Recupero Task Eseguiti | fileName: {fileName} | destName: {destName} ");
File.Move(file, destName);
}
fatto = true;
}
return fatto;
}
/// <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>
/// Processa chiusura ODL
/// </summary>
/// <param name="idxOdl">Idx dell'ODL da chiudere</param>
/// <param name="dtRif">DataOra di riferimento evento stop</param>
protected bool SendCloseOdl(int idxOdl, DateTime dtRif)
{
bool answ = false;
DateTime dtCurr = DateTime.Now;
string resp = callUrl($"{urlODLClose}{idxOdl}&dtEve={dtRif}&dtCurr={dtCurr}", false);
answ = resp == "OK";
return answ;
}
/// <summary>
/// Processa chiusura PODL
/// </summary>
/// <param name="idxPOdl">Idx del PODL da chiudere</param>
/// <param name="dtRif">DataOra di riferimento evento stop</param>
protected bool SendClosePOdl(int idxPOdl, DateTime dtRif)
{
bool answ = false;
DateTime dtCurr = DateTime.Now;
string resp = callUrl($"{urlPODLClose}{idxPOdl}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false);
answ = resp == "OK";
return answ;
}
/// <summary>
/// Invia informazioni associazione IOB 2 machine
/// </summary>
protected void SendM2IOB()
{
if (checkServerAlive)
{
lgInfo("chiamata URL " + urlSetM2IOB);
utils.callUrlNow(urlSetM2IOB);
}
}
/// <summary>
/// Effettua invio al server IO dei dati di IOB + macchina:
/// - dati dell'IOB (IdxMacchina, IobName, IobIp, Tipo, counter...) "classici"
/// - dati di configurazione IOB "extra", ad esempio
/// - IobType (FANUC, Siemens, ModBus...)
/// - MachineIp
/// - MachinePort
/// - Abilitazione gestione FOLDER di ritorno x SPEC (es x FTP)
/// </summary>
protected virtual void SendMachineConf()
{
// preparo dizionario da inviare
Dictionary<string, string> currDict = new Dictionary<string, string>();
if (cIobConf != null)
{
bool hasPzCount = (enablePzCountByApp || enablePzCountByIob) && !disablePzCountByIob;
// creo obj dati necessari...
currDict.Add("IobName", cIobConf.codIOB);
currDict.Add("IobType", $"{cIobConf.tipoIob}");
currDict.Add("MachIp", cIobConf.cncIpAddr);
currDict.Add("MachPort", cIobConf.cncPort);
currDict.Add("Vendor", cIobConf.vendor);
currDict.Add("Model", cIobConf.model);
currDict.Add("IobVersion", cIobConf.versIOB);
if (cIobConf.optPar != null)
{
var ordPar = cIobConf.optPar.OrderBy(x => x.Key).ToList();
foreach (var item in ordPar)
{
currDict.Add($"OP_{item.Key}", item.Value);
}
}
// invio e salvo...
string remUrl = urlSaveMachIobConf;
string dictPayload = JsonConvert.SerializeObject(currDict);
callUrlWithPayload(remUrl, dictPayload, false);
lgTrace("Invio MachineConf effettuato");
}
}
/// <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)
{
// salvo comunque in fluxLog...
accodaFLog($"{paramName}|{paramValue}", qEncodeFLog(paramName, 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}");
}
/// <summary>
/// Invio contapezzi alla dataora indicata
/// </summary>
/// <param name="numPz">Num pezzi da registrare</param>
/// <param name="dtRif">DataOra di riferimento evento</param>
protected bool SendPzIncrAtDate(int numPz, DateTime dtRif)
{
bool answ = false;
DateTime dtCurr = DateTime.Now;
// chiamata avvio PODL (a posteriori)
string resp = callUrl($"{urlAddPzCountAtDate}{numPz}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false);
answ = resp == "OK";
return answ;
}
/// <summary>
/// Processa avvio PODL 8ed eventuale chiusura precedente)
/// </summary>
/// <param name="idxPOdl">Idx del PODL da avviare</param>
/// <param name="dtRif">DataOra di riferimento evento start</param>
protected bool SendStartPodl(int idxPOdl, DateTime dtRif)
{
bool answ = false;
DateTime dtCurr = DateTime.Now;
// chiamata avvio PODL (a posteriori)
string resp = callUrl($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtRif:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}", false);
answ = resp == "OK";
return answ;
}
/// <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();
}
/// <summary>
/// setup gestione allarmi da conf
/// </summary>
protected void setupAlarmMap()
{
// indico quanti allarmi
foreach (var item in alarmMaps)
{
item.setupData();
// loggo
lgDebug($"Decodifica aree alarmMap: {item.description} | {item.memAddr} x {item.size} byte | {item.messages.Count} messaggi allarme");
}
// invio oggetto alarmMap al server x successiva decodifica
// FIXME TODO FARE !!!! invio PUT del file *_alarm.json
}
/// <summary>
/// Setup parametri decode file excel (opzionale)
/// </summary>
protected virtual void setupFileDecod()
{
// verifica se siano necessari configuraizoni speciali dalla excDecod:
// es: lettura/invio file excel
if (memMap.fileDecod != null && memMap.fileDecod.Count > 0)
{
FileDecod = memMap.fileDecod;
}
}
/// <summary>
/// setup parametri da file di conf
/// </summary>
protected void setupMemMap()
{
bool serverOk = testPingServer == IPStatus.Success;
lgDebug($"setupMemMap | trovati {memMap.mMapRead.Count} parametri Read (TSVC)");
lgDebug($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write");
if (utils.CRB("verbose"))
{
string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented);
lgDebug($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}");
}
// se ho variabili read --> genero dati TSVC...
if (memMap.mMapRead.Count > 0)
{
TSVC_Data.Clear();
LastTSVC.Clear();
VCData currConf;
int periodo = 0;
VC_func funz = VC_func.POINT;
// accodo nella conf...
foreach (var item in memMap.mMapRead)
{
funz = item.Value.func;
periodo = item.Value.period;
currConf = new VCData()
{
Funzione = funz,
Period = periodo,
UM = item.Value.unit,
DTStart = DateTime.Now.AddHours(-1),
dataArray = new List<double>()
};
TSVC_Data.Add(item.Key, currConf);
}
// documento...
foreach (var item in TSVC_Data)
{
lgTrace($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}");
// salvo i valori PREC...
LastTSVC.Add(item.Key, 0);
}
}
// 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, Formatting.Indented);
// controllo ping al server...
if (serverOk)
{
var resp = utils.callUrlNow($"{urlSaveMemMap}", rawData);
}
else
{
lgInfo($"Mancata risposta ping da server, saltato step invio memMap a {urlSaveMemMap}");
}
// salvo ANCHE come parametri i valori...
objItem currItem = new objItem();
List<objItem> allParam = new List<objItem>();
string currValore = "";
// valori WRITE
foreach (var item in memMap.mMapWrite)
{
currValore = "";
//se ho valori current li impiego...
if (currProdData.ContainsKey(item.Value.name))
{
currValore = currProdData[item.Value.name];
item.Value.value = currValore;
}
currItem = new objItem()
{
uid = item.Value.name,
name = item.Value.name,
description = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name,
writable = true,
UM = item.Value.unit,
valMin = item.Value.minVal,
valMax = item.Value.maxVal,
value = currValore,
displOrdinal = item.Value.displOrdinal
};
allParam.Add(currItem);
}
// valori READ
foreach (var item in memMap.mMapRead)
{
// se non ci fosse aggiungo
var trovato = allParam.FirstOrDefault(x => x.uid == item.Value.name);
if (trovato == null)
{
currValore = "";
// se ho valori current li impiego...
if (currProdData.ContainsKey(item.Value.name))
{
currValore = currProdData[item.Value.name];
item.Value.value = currValore;
}
currItem = new objItem()
{
uid = item.Value.name,
name = item.Value.name,
description = !string.IsNullOrEmpty(item.Value.description) ? item.Value.description : item.Value.name,
writable = false,
UM = item.Value.unit,
valMin = item.Value.minVal,
valMax = item.Value.maxVal,
value = currValore,
displOrdinal = item.Value.displOrdinal
};
allParam.Add(currItem);
}
}
// invio su cloud parametri SE sono connesso alla macchina... in pratica reset parametri...
if (serverOk)
{
string tipoCall = urlSaveAllParams;
rawData = JsonConvert.SerializeObject(allParam, Formatting.Indented);
if (serverOk)
{
// verifica se sia un IOB "parziale")
if (cIobConf.disableExeTask || cIobConf.disableStateCh)
{
// versione upsert
tipoCall = urlUpdateWriteParams;
}
var resp = utils.callUrl($"{tipoCall}", rawData);
}
else
{
lgInfo($"Mancata risposta ping server, non effettuata chiamata {tipoCall}");
}
}
lgDebug($"setupMemMap | salvata conf memoria R/W");
}
}
/// <summary>
/// Setup parametri memoria opzionali
/// es: BitCond e IntCond x ricerca valori target
/// </summary>
protected virtual void setupOptMemPar()
{
// verifica se siano necessari configuraizoni speciali dalla optMemPar:
// es: per ricerca condizioni status bolleane come ModbusTCP...
if (memMap.optMemPar != null && memMap.optMemPar.Count > 0)
{
// cerco condizioni BIT speciali x Auto, Estop, Work.. devono essere *BitCond
var listPar2add = memMap.optMemPar.Where(x => x.Key.EndsWith("BitCond")).ToList();
if (listPar2add != null && listPar2add.Count > 0)
{
foreach (var item in listPar2add)
{
addCheckConditionBit(item.Key, item.Value);
}
}
// cerco condizioni INT speciali x Auto, Estop, Work.. devono essere *IntCond
var listParInt2add = memMap.optMemPar.Where(x => x.Key.EndsWith("IntCond")).ToList();
if (listParInt2add != null && listParInt2add.Count > 0)
{
foreach (var item in listParInt2add)
{
addCheckConditionInt(item.Key, item.Value);
}
}
// cerco valori *2Translate
var listVal2Translate = memMap.optMemPar.Where(x => x.Key.Contains("2Translate")).ToList();
if (listVal2Translate != null && listVal2Translate.Count > 0)
{
// cerco solo valori BIT...
var listValBit = listVal2Translate.Where(x => x.Key.StartsWith("VarBit2Translate")).ToList();
if (listValBit != null && listValBit.Count > 0)
{
foreach (var item in listValBit)
{
addVal2TranslBit(item.Value);
}
}
// ...e poi valori INT...
var listValInt = listVal2Translate.Where(x => x.Key.StartsWith("VarInt2Translate")).ToList();
if (listValInt != null && listValInt.Count > 0)
{
foreach (var item in listValInt)
{
addVal2TranslInt(item.Value);
}
}
}
}
}
/// <summary>
/// Init parametri speciali, tipicamente KVP opzionali da json
/// </summary>
protected virtual void setupSpecialParams()
{
string sCond = "";
// per prima cosa inizializzo lista PODL inviati
POdlSentList = POdlSentFileArch;
// verifico se sia attiva gestione riduzione FluxLog...
if (!string.IsNullOrEmpty(getOptJsonKVP("fluxLogReduce")))
{
bool.TryParse(getOptJsonKVP("fluxLogReduce"), out fluxLogReduce);
double.TryParse(getOptJsonKVP("fluxLogRedDeadBand"), NumberStyles.Any, CultureInfo.InvariantCulture, out fluxLogRedDeadBand);
int.TryParse(getOptJsonKVP("fluxLogResendPeriod"), out fluxLogResendPeriod);
lgInfo($"FluxLog Redux | fluxLogReduce: {fluxLogReduce} | fluxLogRedDeadBand: {fluxLogRedDeadBand:N2} | fluxLogResendPeriod: {fluxLogResendPeriod} min");
}
// check modalità ricetta attiva
bool.TryParse(getOptJsonKVP("hasRecipe"), out hasRecipe);
// verifico se usare ricette locali/http...
bool.TryParse(getOptJsonKVP("useLocalRecipe"), out useLocalRecipe);
// recupero quantitativo massimo KG x PODL
int.TryParse(getOptJsonKVP("maxPodlQty"), out maxQtyPerFile);
// recupero le folder specifiche x IN/OUT ricette filtrando direttamente l'area di memoria...
sCond = "path";
var dirList = memMap.optKVP
.Where(x => x.Key.StartsWith(sCond))
.ToList();
pathList = dirList.ToDictionary(x => x.Key, x => x.Value);
// gestione replace in file ricette...
sCond = "replace-";
var ruleList = memMap.optKVP
.Where(x => x.Key.StartsWith(sCond))
.ToList();
RecipeReplRules = ruleList.ToDictionary(x => x.Key.Replace(sCond, ""), x => x.Value);
}
/// <summary>
/// Cleanup stringa x impiego tipo ident da char dubbi
/// </summary>
/// <param name="origData"></param>
/// <returns></returns>
protected string strFixId(string origData)
{
return origData.Replace(".", "").Replace(" ", "_");
}
/// <summary>
/// Effettua chiamata MP-IO per cheidere all'utente se vuole effettuare chiusura ODL
/// corrente della macchina
/// </summary>
/// <returns></returns>
protected bool tryAskCloseCurrODL()
{
bool fatto = false;
string fullUrl = $"{urlODLAskClose}{currIdxODL}";
try
{
// invio chiamata URL x chiusura ODL su macchina
string callResp = callUrl(fullUrl, false);
fatto = (callResp != "KO") ? true : false;
}
catch
{ }
lgInfo($"Eseguito tryAskCloseCurrODL per {currIdxODL} | url: {fullUrl} | esito: {fatto}");
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura ODL corrente della macchina
/// </summary>
/// <returns></returns>
protected bool tryCloseCurrODL()
{
bool fatto = false;
string fullUrl = $"{urlODLClose}{currIdxODL}";
try
{
// invio chiamata URL x chiusura ODL su macchina
string callResp = callUrl(fullUrl, false);
fatto = (callResp != "KO") ? true : false;
}
catch
{ }
lgInfo($"Eseguito tryCloseCurrODL per {currIdxODL} | url: {fullUrl} | esito: {fatto}");
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura ODL specifico
/// </summary>
/// <param name="IdxODL"></param>
/// <returns></returns>
protected bool tryCloseODL(int IdxODL)
{
bool fatto = false;
string fullUrl = $"{urlODLClose}{IdxODL}";
try
{
// invio chiamata URL x chiusura ODL su macchina
string callResp = callUrl(fullUrl, false);
fatto = (callResp != "KO") ? true : false;
}
catch
{ }
lgInfo($"Eseguito tryCloseODL per {IdxODL} | url: {fullUrl} | esito: {fatto}");
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura PODL --&gt; ODL specifico
/// </summary>
/// <param name="idxPODL"></param>
/// <returns></returns>
protected bool tryClosePODL(int idxPODL)
{
bool fatto = false;
string fullUrl = $"{urlPODLClose}{idxPODL}";
try
{
// invio chiamata URL x chiusura ODL su macchina
string callResp = callUrl(fullUrl, false);
fatto = (callResp != "KO") ? true : false;
}
catch
{ }
lgInfo($"Eseguito tryClosePODL per {idxPODL} | url: {fullUrl} | esito: {fatto}");
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura PODL --&gt; ODL specifico
/// </summary>
/// <param name="idxPODL"></param>
/// <param name="doConfirm"></param>
/// <param name="dtEve"></param>
/// <param name="dtCurr"></param>
/// <returns></returns>
protected bool tryClosePODL(int idxPODL, DateTime dtEve, DateTime dtCurr)
{
bool fatto = false;
string fullUrl = $"{urlPODLClose}{idxPODL}&dtEve={dtEve:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}";
try
{
// invio chiamata URL x chiusura ODL su macchina
string callResp = callUrl(fullUrl, false);
fatto = (callResp != "KO") ? true : false;
}
catch
{ }
lgInfo($"Eseguito tryClosePODL per {idxPODL} | url: {fullUrl} | esito: {fatto}");
return fatto;
}
/// <summary>
/// Processa avvio PODL (ed eventuale chiusura precedente)
/// </summary>
/// <param name="codArt">Cod Articolo del PODL da avviare</param>
/// <param name="codGruppo">Cod Gruppo dell'impianto come default</param>
/// <param name="numPz">num pz richiesti</param>
protected int TryCreatePodl(string codArt, string codGruppo, int numPz)
{
int answ = 0;
string urlEncoded = $"{urlCreatePOdl}CodArt={codArt}&CodGruppo={codGruppo}&numPz={numPz}";
string resp = callUrl(urlEncoded, false);
int.TryParse(resp, out answ);
return answ;
}
/// <summary>
/// Cerca di inviare su un altro thread i vari dati accumulati...
/// </summary>
protected 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 = Task.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 = Task.Run(() => svuotaCodaSignIN());
Task taskFLog = Task.Run(() => svuotaCodaFLog());
Task taskRawTransf = Task.Run(() => svuotaCodaRawTransf());
Task taskULog = Task.Run(() => svuotaCodaULog());
}
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();
// coda RawTransf
svuotaCodaRawTransf();
// coda UserLog
svuotaCodaULog();
// refresh
raiseRefresh(currDispData);
}
// se arrivo è tutto ok...
currSendErrors = 0;
}
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)
{
lgError("IOB - SERVER NOT READY");
}
}
}
catch (Exception exc)
{
lgError($"Errore in fase trySendValues | currSendErrors: {currSendErrors}{Environment.NewLine}{exc}");
currDispData.semOut = Semaforo.SR;
}
raiseRefresh(currDispData);
}
/// <summary>
/// Effettua chiamata MP-IO per tentare setup del PODL indicato
/// </summary>
/// <param name="idxPODL"></param>
/// <returns></returns>
protected bool trySetupPODL(int idxPODL)
{
bool fatto = false;
string fullUrl = $"{urlOdlStartFromPOdl}{idxPODL}";
try
{
// invio chiamata URL x avvio PODL su macchina
string rawSplit = callUrl(fullUrl, false);
fatto = (rawSplit != "KO") ? true : false;
}
catch
{ }
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare setup del PODL indicato con indicazioni estese
/// (confirm pezzi, dtEvento, dtCorrente)
/// </summary>
/// <param name="idxPODL"></param>
/// <param name="doConfirm"></param>
/// <param name="dtEve"></param>
/// <param name="dtCurr"></param>
/// <returns></returns>
protected bool trySetupPODL(int idxPODL, bool doConfirm, DateTime dtEve, DateTime dtCurr)
{
bool fatto = false;
//string format = "yyyyMMddHHmmssfff";
string fullUrl = $"{urlOdlStartFromPOdl}{idxPODL}&doConfirm={doConfirm}&dtEve={dtEve:yyyyMMddHHmmssfff}&dtCurr={dtCurr:yyyyMMddHHmmssfff}";
try
{
// invio chiamata URL x avvio PODL su macchina
string rawSplit = callUrl(fullUrl, false);
fatto = (rawSplit != "KO") ? true : false;
}
catch
{ }
return fatto;
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Dizionario dei valori bloccati x evitare log eccessivo
/// </summary>
private Dictionary<string, DateTime> vetoLogError = new Dictionary<string, DateTime>();
/// <summary>
/// Periodo di veto log in minuti
/// </summary>
private int vetoPeriodMin = 15;
#endregion Private Fields
#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);
IobOnline = answ;
}
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;
}
}
private bool qInEnabCurr { get; set; } = false;
/// <summary>
/// Redis key del dizionari valori currProdData persistiti
/// </summary>
private string rKeyCurrProdData
{
get => redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:CurrProdData");
}
/// <summary>
/// test ping all'indirizzo impostato nei parametri
/// </summary>
/// <returns></returns>
private IPStatus testPingServer
{
get
{
IPStatus answ = IPStatus.Unknown;
// se disabilitato salto...
if (pingDisabled)
{
answ = IPStatus.Success;
}
else
{
IPAddress address;
PingReply reply;
var rand = new Random();
int pingTOut = 500;
using (Ping pingSender = new Ping())
{
address = IPAddress.Loopback;
int maxRetry = maxPingRetry + 1;
int numRetry = 1;
string ipAddr = cIobConf.serverData.MPIP.Replace($"{cIobConf.serverData.TRANSP}://", "");
// fix se fosse ip + porta...
if (ipAddr.IndexOf(":") >= 0)
{
ipAddr = ipAddr.Substring(0, ipAddr.IndexOf(":"));
}
IPAddress.TryParse(ipAddr, out address);
// se null --> provo DNS...
if (address == null)
{
var rawAddresses = Dns.GetHostAddresses(ipAddr);
if (rawAddresses.Length > 0)
{
address = rawAddresses[0];
}
}
try
{
// se != null --> uso address...
if (address != null)
{
pingTOut = rand.Next(200, 400);
reply = pingSender.Send(address, pingTOut);
}
else
{
pingTOut = rand.Next(300, 600);
//reply = pingSender.Send(cIobConf.cncIpAddr, pingTOut);
reply = pingSender.Send(IPAddress.Loopback, pingTOut);
lgError($"ping to loopback addres per mancanza address server");
}
}
catch (Exception exc)
{
pingTOut = rand.Next(100, 200);
reply = pingSender.Send(IPAddress.Loopback, pingTOut);
lgError($"ping to loopback addres per eccezione:{Environment.NewLine}{exc}");
}
// se ho timeout riprovo...
while (reply.Status != IPStatus.Success && numRetry < maxRetry)
{
Thread.Sleep(rand.Next(50, 200));
pingTOut = pingServerMsTimeout * numRetry / 2;
reply = pingSender.Send(address, pingTOut);
numRetry++;
if (reply.Status == IPStatus.Success)
{
lgInfo("Server PING OK!");
break;
}
else
{
lgInfo($"Server Ping KO | reply: {reply.Status} --> retry | TimeOut: {pingTOut}");
}
}
}
answ = reply.Status;
}
return answ;
}
}
#endregion Private Properties
#region Private Methods
/// <summary>
/// Aggiunge in setup memoria la checkCondition BIT "decodificata"
/// </summary>
/// <param name="ompKey">Chiave da aggiungere</param>
/// <param name="ompVal">Valore da decodificare e poi aggiungere</param>
private void addCheckConditionBit(string ompKey, string ompVal)
{
var newCond = new BitConditionCheck(ompKey, ompVal);
// cerco x aggiornare o aggiungere...
if (OptCheckCondBit.ContainsKey(ompKey))
{
OptCheckCondBit[ompKey] = newCond;
}
else
{
OptCheckCondBit.Add(ompKey, newCond);
}
}
/// <summary>
/// Aggiunge in setup memoria la checkCondition INT "decodificata"
/// </summary>
/// <param name="ompKey">Chiave da aggiungere</param>
/// <param name="ompVal">Valore da decodificare e poi aggiungere</param>
private void addCheckConditionInt(string ompKey, string ompVal)
{
var newCond = new IntConditionCheck(ompKey, ompVal);
// cerco x aggiornare o aggiungere...
if (OptCheckCondInt.ContainsKey(ompKey))
{
OptCheckCondInt[ompKey] = newCond;
}
else
{
OptCheckCondInt.Add(ompKey, newCond);
}
}
/// <summary>
/// Aggiunge in setup memoria l'oggetto tipo valore BIT (NON mutuamente esclusivo) da tradurre
/// </summary>
/// <param name="csvList">Elenco valori da aggiungere formato csv (es "a,b,c")</param>
private void addVal2TranslBit(string csvList)
{
var list2add = csvList.Split(',');
foreach (var memKey in list2add)
{
// cerco x aggiungere...
if (!OptVar2TranslBit.ContainsKey(memKey))
{
// init traduzione vuota
OptVar2TranslBit.Add(memKey, "");
}
}
}
/// <summary>
/// Aggiunge in setup memoria l'oggetto tipo valore INT (mutuamente esclusivo) da tradurre
/// </summary>
/// <param name="csvList">Elenco valori da aggiungere formato csv (es "a,b,c")</param>
private void addVal2TranslInt(string csvList)
{
var list2add = csvList.Split(',');
foreach (var memKey in list2add)
{
// cerco x aggiungere...
if (!OptVar2TranslInt.ContainsKey(memKey))
{
// init traduzione vuota
OptVar2TranslInt.Add(memKey, "");
}
}
}
/// <summary>
/// Verifica e se necessario comprime directory log...
/// </summary>
private void checkShrinkDir()
{
// comprimo x prima cosa la folder dell'IOB corrente
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", cIobConf.codIOB);
try
{
baseUtils.shrinkDir(path);
// provo a comprimere anche la folder MAIN....
path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "MAIN");
baseUtils.shrinkDir(path);
}
catch (Exception exc)
{
lgError($"Eccezione in checkShrinkDir{Environment.NewLine}{exc}");
}
}
/// <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;
}
/// <summary>
/// Archivia una cartella in un file zip
/// </summary>
/// <param name="folderPath">Cartelal da archiviare</param>
/// <param name="zipName">Nome del file zip da produrre...</param>
private bool doZipArchiveFolder(string folderPath, string zipName)
{
bool fatto = false;
if (Directory.Exists(folderPath))
{
fatto = fileMover.zippaDirectory(folderPath, zipName);
// se fatto elimino vecchia directory...
if (fatto)
{
Directory.Delete(folderPath, true);
}
}
return fatto;
}
/// <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)
{
lgTrace("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
{
lgTrace("END BLINK: B{0}", i);
}
}
}
}
}
}
/// <summary>
/// Legge da conf il valore di demoltiplica lettura dynData (se presente) o ignora e pone a 1...
/// </summary>
private void fixDemFactDynData()
{
var rawDemFact = getOptPar("DEM_FACT_DYN_DATA");
if (string.IsNullOrEmpty(rawDemFact))
{
demFactDynData = 1;
}
else
{
int.TryParse(rawDemFact, out demFactDynData);
}
}
#if false
/// <summary>
/// Traduzione del valore chiave configurato dal dizionario RecipeKeyTranslate + BaseKeyTranslate
/// </summary>
/// <param name="currKey"></param>
/// <returns></returns>
protected string RecipeKeyDecod(Dictionary<string, string> keyDict, string currKey)
{
string answ = currKey;
if (keyDict != null)
{
answ = keyDict[currKey];
}
return answ;
}
#endif
/// <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>
/// Verifica se il log di un dato errore sia permesso
/// </summary>
/// <param name="logKey">ID del valore log da loggare/verificare</param>
/// <returns></returns>
private bool logValuePermit(string logKey)
{
bool doLog = false;
if (vetoLogError.ContainsKey(logKey))
{
// verifico se veto scaduto...
if (DateTime.Now > vetoLogError[logKey])
{
doLog = true;
vetoLogError[logKey] = DateTime.Now.AddMinutes(vetoPeriodMin);
}
}
else
{
doLog = true;
vetoLogError.Add(logKey, DateTime.Now.AddMinutes(vetoPeriodMin));
}
return doLog;
}
/// <summary>
/// Classe fittizia in caso di processing GLOBALE di tutto in 1 solo colpo...
/// </summary>
private void processAllMemory()
{
// verifico se sia abilitato processing...
if (queueInEnabCurr)
{
// 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);
}
else
{
lgInfo($"VETO on processAllMemory: queueIn disabled");
checkVetoQueueIn();
}
}
private void processMem2Write()
{
List<objItem> updatedPar = new List<objItem>();
// ciclo tutti gli oggetti write x vedere se modificati...
foreach (var item in currProdData)
{
bool needWrite = false;
// li cerco su last... se non ci sono o modificati --> salvo da scrivere e copio
if (lastProdData.ContainsKey(item.Key))
{
// verifico se variato...
if (!item.Value.Equals(lastProdData[item.Key]))
{
needWrite = true;
lastProdData[item.Key] = item.Value;
}
}
// aggiungo
else
{
needWrite = true;
lastProdData.Add(item.Key, item.Value);
}
// se devo scrivere --> riporto
if (needWrite)
{
if (memMap != null && memMap.mMapWrite.ContainsKey(item.Key))
{
// preparo obj da scrivere
objItem newWrite = new objItem()
{
uid = item.Key,
name = item.Key,
description = memMap.mMapWrite[item.Key].description,
//reqValue = memMap.mMapWrite[item.Key].value,
reqValue = item.Value,
value = item.Value,
lastRequest = DateTime.Now,
writable = true,
displOrdinal = memMap.mMapWrite[item.Key].displOrdinal
};
updatedPar.Add(newWrite);
}
}
}
// se ho da scrivere... scrivo TUTTI!
if (updatedPar.Count > 0)
{
// scrivo valore!
lgInfo($"Chiamata di plcWriteParams da processMem2Write: {updatedPar.Count} updatedPar");
plcWriteParams(ref updatedPar);
// invio su cloud parametri!
string rawData = JsonConvert.SerializeObject(updatedPar);
utils.callUrl($"{urlUpdateWriteParams}", rawData);
lgInfo($"Notificato a server scrittura {updatedPar.Count} parametri");
}
}
/// <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 = $"[PROG]{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));
}
}
}
}
/// <summary>
/// Effettua calcolo dei consumi + esportazione in file richiesto
/// </summary>
/// <param name="folderPath">Folder dei file da processare</param>
/// <param name="reportPath">Percorso file out di export</param>
private bool RecipeDoConsumeReport(string folderPath, string reportPath)
{
// var di base
bool fatto = false;
string expMode = "csv";
List<ConsRec> ListConsDet = new List<ConsRec>();
List<ConsOut> ListConsSum = new List<ConsOut>();
// file conf x conversioni...
string confSetupPath = pathList["path-confSetup"];
ConvSetup currConf = new ConvSetup();
bool addHeader = false;
int numDec = 4;
string codMag = "NA";
if (!string.IsNullOrEmpty(confSetupPath))
{
string rawConfFile = File.ReadAllText(confSetupPath);
if (!string.IsNullOrEmpty(rawConfFile))
{
currConf = JsonConvert.DeserializeObject<ConvSetup>(rawConfFile);
if (currConf != null)
{
addHeader = currConf.addHeader;
numDec = currConf.numDec;
codMag = currConf.codMag;
expMode = currConf.mode.ToLower();
}
}
}
// ciclo x ogni file ricevendone i consumi ed accumulandoli in lista...
var fileList = Directory.GetFiles(folderPath);
foreach (var cFile in fileList)
{
var consumiFile = RecipeGetCons(cFile);
ListConsDet.AddRange(consumiFile);
}
// se ho trovato dati
double ratioConv = 1;
if (ListConsDet.Count > 0)
{
var dirName = new DirectoryInfo(folderPath).Name;
// recupero elenco colori
var elencoColori = ListConsDet
.GroupBy(x => x.ColourCode)
.Select(grp => grp.Last())
.ToList();
// faccio le somme x colore
foreach (var colore in elencoColori)
{
ratioConv = 1;
// ricerca in conf (% di consumo)
if (currConf != null && currConf.convRatio != null)
{
if (currConf.convRatio.ContainsKey(colore.ColourCode))
{
ratioConv = currConf.convRatio[colore.ColourCode];
}
}
// calcolo peso equivalente in kg con conversione ratio
var totWeightKg = ListConsDet
.Where(x => x.ColourCode == colore.ColourCode)
.Sum(x => x.WeightGrTot) / 1000 * ratioConv;
// record finale trasformato in KG...
ConsOut colTotal = new ConsOut()
{
CodMag = codMag,
ColourCode = colore.ColourCode,
Description = colore.Description,
UM = "KG",
WeightKgProc = totWeightKg > 0.01 ? Math.Round(totWeightKg, numDec) : 0.01,
DtRif = colore.DtRif
};
ListConsSum.Add(colTotal);
}
// infine serializzo x output in base alla modalità richiesta
switch (expMode)
{
case "fixwidth":
fatto = DataExport.SaveFixedWidth(ListConsSum, $"{reportPath}.txt", currConf.fieldLength, currConf.fieldLPad, currConf.fieldNDec);
break;
case "csv":
default:
fatto = DataExport.SaveToCsv(ListConsSum, $"{reportPath}.csv", addHeader);
break;
}
}
return fatto;
}
/// <summary>
/// Processa la ricetta alla ricerca dei dati PODL x inviare chiamate ad MP-IO
/// </summary>
/// <param name="recipeFile">Path RIcetta</param>
private bool RecipeDoProcessPODL(string recipeFile)
{
bool answ = false;
// init var
int idxPOdl = 0;
string rawVal = "";
// leggo righe file!
var recipeRows = File.ReadAllLines(recipeFile);
// per ogni file ricevuto controlla se viene dal MES (cerca <Variant>PODL)
string tokenSearch = "<Variant>PODL";
// recupero riga PODL...
var rowPodl = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault();
if (rowPodl != null)
{
DateTime dtStartPOdl = DateTime.Today;
int duration = 0;
DateTime dtEndPOdl = DateTime.Today.AddMinutes(10);
string dtEve = "";
string dtCurr = "";
// inizio ricerca PODL
rawVal = rowPodl.Trim().Replace(tokenSearch, "").Replace("</Variant>", "");
int.TryParse(rawVal, out idxPOdl);
if (idxPOdl > 0)
{
// leggo inizio commessa
tokenSearch = "<Date>";
var rowStart = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault();
if (rowStart != null)
{
rawVal = rowStart.Trim().Replace(tokenSearch, "").Replace("</Date>", "");
DateTime.TryParse(rawVal, out dtStartPOdl);
}
// leggo durata commessa
tokenSearch = "<DosDuration>";
var rowDurSec = recipeRows.Where(x => x.Contains(tokenSearch)).FirstOrDefault();
if (rowDurSec != null)
{
rawVal = rowDurSec.Trim().Replace(tokenSearch, "").Replace("</DosDuration>", "");
int.TryParse(rawVal, out duration);
dtEndPOdl = dtStartPOdl.AddSeconds(duration);
}
// prova ad avviare/chiudere PODL relativo (eventualmente duplicandolo)
dtEve = $"{dtStartPOdl:yyyyMMddHHmmssfff}";
dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}";
callUrl($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false);
// ora chiamo chiusura...
dtEve = $"{dtEndPOdl:yyyyMMddHHmmssfff}";
dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}";
callUrl($"{urlPODLClose}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false);
}
answ = true;
}
return answ;
}
/// <summary>
/// Elimino record da REDIS (locale e remoto)
/// </summary>
/// <param name="keyReq"></param>
private void RecipeRemoveWeekStatus(string keyReq)
{
string fullKey = redisMan.redHash($"IOB:Status:{cIobConf.codIOB}:WeekStats");
var okHashDict = redisMan.redRemoveHashField(fullKey, keyReq);
// rileggo status hash
Dictionary<string, string> currDict = redisMan.redGetHashDict(fullKey);
// invio ANCHE in MP-IO l'update delle info...
string remUrl = urlSetHashDict;
string dictPayload = JsonConvert.SerializeObject(currDict);
callUrlWithPayload(remUrl, dictPayload, false);
}
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 delle 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");
// fix url wait random...
urlRandWait = utils.CRI("urlRandWait");
// fix fattore demoltiplica dynData
fixDemFactDynData();
// svuoto code se richiesto
if (resetQueue)
{
QueueAlarm = new DataQueue(cIobConf.codIOB, "QueueAlarm", cIobConf.EnableRedisQueue);
//QueueAlarm = new ConcurrentQueue<string>();
QueueIN = new DataQueue(cIobConf.codIOB, "QueueIN", cIobConf.EnableRedisQueue);
//QueueIN = new ConcurrentQueue<string>();
QueueFLog = new DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue);
//QueueFLog = new ConcurrentQueue<string>();
QueueMessages = new DataQueue(cIobConf.codIOB, "QueueMessages", cIobConf.EnableRedisQueue);
//QueueMessages = new ConcurrentQueue<string>();
QueueRawTransf = new DataQueue(cIobConf.codIOB, "QueueRawTransf", cIobConf.EnableRedisQueue);
//QueueRawTransf = new ConcurrentQueue<string>();
QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue);
//QueueULog = new ConcurrentQueue<string>();
}
// imposto contatori blink a zero...
i_counters = new int[32];
lastPeriodicLog = DateTime.Now;
// fix parametri generali...
enablePrgName = true;
// valore standard divieto accodamento segnali IN
string VETO_QUEUE_IN = getOptPar("VETO_QUEUE_IN");
if (!string.IsNullOrEmpty(VETO_QUEUE_IN))
{
int.TryParse(VETO_QUEUE_IN, out vetoQueueIn);
}
// fix slow data
string ENABLE_SLOW_DATA = getOptPar("ENABLE_SLOW_DATA");
if (!string.IsNullOrEmpty(ENABLE_SLOW_DATA))
{
bool.TryParse(getOptPar("ENABLE_SLOW_DATA"), out enableSlowData);
}
}
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
if (!isMulti)
{
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()
{
bool sendOnMachineOff = false;
// 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... SE non disabilitato..
if (!disableWdst)
{
accodaFLog(sVal, qEncodeFLog("WDST", wdStatus));
}
lastWatchDog = DateTime.Now;
sendOnMachineOff = true;
lgInfo("Impostato sendOnMachineOff a true");
}
// 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 || sendOnMachineOff)
{
// 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);
lastWatchDog = DateTime.Now;
}
else
{
// invio in blocco
listaValori = QueueFLog.ToList();
// invio
sendDataBlock(urlType.FLog, listaValori);
// svuoto!
QueueFLog = new DataQueue(cIobConf.codIOB, "QueueFLog", cIobConf.EnableRedisQueue);
//QueueFLog = new ConcurrentQueue<string>();
lastWatchDog = DateTime.Now;
}
}
else
{
// INVIO SINGOLO...!!!
QueueFLog.TryDequeue(out currVal);
sendToMoonPro(urlType.FLog, currVal);
lastWatchDog = DateTime.Now;
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
}
/// <summary>
/// Processo la coda RawTransf...
/// </summary>
private bool svuotaCodaRawTransf(bool force = false)
{
bool fatto = false;
// verifico SE la coda abbia dei valori...
if (QueueRawTransf.Count > 0)
{
// invio pacchetto di dati (max da conf)
for (int i = 0; i < nMaxSend; i++)
{
// SE ho qualcosa in coda...
if (QueueRawTransf.Count > 0)
{
string currVal = "";
if (MPOnline || force)
{
if (IobOnline || force)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueRawTransf.Count > maxJsonData)
{
// prendoi primi maxJsonDataValori
for (int j = 0; j < maxJsonData; j++)
{
QueueRawTransf.TryDequeue(out currVal);
listaValori.Add(currVal);
}
fatto = sendDataBlock(urlType.RawTransf, listaValori, force);
}
else
{
// invio in blocco
listaValori = QueueRawTransf.ToList();
// invio
fatto = sendDataBlock(urlType.RawTransf, listaValori, force);
if (fatto)
{
// svuoto se ha okReport!
QueueRawTransf = new DataQueue(cIobConf.codIOB, "QueueRawTransf", cIobConf.EnableRedisQueue);
//QueueRawTransf = new ConcurrentQueue<string>();
}
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
return fatto;
}
/// <summary>
/// Processo la coda UserLog...
/// </summary>
private void svuotaCodaULog()
{
// verifico SE la coda abbia dei valori...
if (QueueULog.Count > 0)
{
// invio pacchetto di dati (max da conf)
for (int i = 0; i < nMaxSend; i++)
{
// SE ho qualcosa in coda...
if (QueueULog.Count > 0)
{
string currVal = "";
if (MPOnline)
{
if (IobOnline)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueULog.Count > maxJsonData)
{
// prendoi primi maxJsonDataValori
for (int j = 0; j < maxJsonData; j++)
{
QueueULog.TryDequeue(out currVal);
listaValori.Add(currVal);
}
sendDataBlock(urlType.ULog, listaValori);
}
else
{
// invio in blocco
listaValori = QueueULog.ToList();
// invio
sendDataBlock(urlType.ULog, listaValori);
// svuoto!
QueueULog = new DataQueue(cIobConf.codIOB, "QueueULog", cIobConf.EnableRedisQueue);
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
}
/// <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
}
}