b4dc683ffc
- fix generic - fix fanuc - fix simula - fix FTP - fix OPC-UA CMP/Cimolai - fix Siemens (At2001, Comur, Cosma, Ingenia, Lasco, Pressoil, Saet, Simec, Robotservice)
8718 lines
342 KiB
C#
8718 lines
342 KiB
C#
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 --> 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% --> 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 --> 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..
|
||
upsertKey(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..
|
||
upsertKey(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..
|
||
upsertKey(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>
|
||
/// 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 + cache REDIS
|
||
/// </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) --> se supera soglia maxErroriCheck --> 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 --> 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 --> 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
|
||
}
|
||
} |