diff --git a/AGENTS.md b/AGENTS.md
index c77fd7e8..cee8e2b8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -10,16 +10,22 @@ A collection of .NET (C#/VB) projects for industrial communication with NC contr
```powershell
& "$env:MSBUILD_PATH" "ProjectName\ProjectName.csproj" -target:Build /p:Configuration=Release /p:Platform="x86" /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m
```
-- **NuGet**: Requires Steamware Nexus Proxy sources (defined in `.gitlab-ci.yml`).
-- **Versioning**: Automated during CI via `VersGen\VersGen.cs` and `.nuspec` updates.
-- **Release Artifacts**: Zipped using `7z.exe`; MD5/SHA1 hashes are generated; uploaded to Nexus.
+- **NuGet**: Requires Steamware Nexus Proxy sources. Use `dotnet nuget restore` on the specific `.sln` before building.
+- **Versioning**: Automated via `VersGen\VersGen.cs` and `.nuspec` updates during CI. Manual updates to `VersGen.cs` might be needed if mimicking CI.
+- **Release Artifacts**: Zipped using `7z.exe`; MD5/SHA1 hashes are generated.
## Key Directories & Files
- **`IOB-WIN-*`**: Protocol-specific implementations (e.g., `FANUC`, `SIEMENS`, `SHELLY`).
-- **`VersGen\`**: Handles automated assembly versioning during build.
-- **`UtilityScripts\`**: Contains `alarmFormatter.py` for converting alarm CSV/Excel to JSON. Requires `python3` and `pip install inquirer`.
+- **`VersGen\`**: Handles automated assembly versioning.
+- **`UtilityScripts\`**: Contains `alarmFormatter.py`. Requires `python3` and `pip install inquirer`.
+- **`EgwCApp\`**: Core application logic and testing projects.
## Environment & Setup
- **Siemens PLC**: Must enable "PUT/GET" permission in TIA Portal.
-- **Dependencies**: Uses `saltminion` (via Chocolatey) and specific Windows accounts (`steamware`/`IOB`).
-- **Docs**: Documentation for several modules is generated via `docfx`.
+- **Dependencies**: Uses `saltminion` and specific Windows accounts (`steamware`/`IOB`).
+- **Documentation**: Generated via `docfx`.
+
+## Important Workflow Notes
+- **CI Logic**: The `.gitlab-ci.yml` contains critical automation logic for NuGet sources, versioning, and deployment. Refer to it for exact operational steps.
+- **NuGet Fixes**: The CI uses a `.nuget-fix` helper to manage Steamware Nexus Proxy sources. If encountering NuGet errors, check source configuration.
+- **Versioning Flow**: Versioning involves updating `VersGen.cs` (replacing `0.0.0.0`) and updating `.nuspec` files.
diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj
index a0dc1387..be331be1 100644
--- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj
+++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj
@@ -217,7 +217,7 @@
-
+
diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs
index 9d886dbd..a7c5c09c 100644
--- a/IOB-UT-NEXT/Iob/BaseObj.cs
+++ b/IOB-UT-NEXT/Iob/BaseObj.cs
@@ -49,13 +49,6 @@ namespace IOB_UT_NEXT.Iob
///
public AlarmBlockType alarmType = AlarmBlockType.Bitmap;
-#if false
- ///
- /// Conf adapter corrente
- ///
- public IobConfiguration cIobConf;
-#endif
-
///
/// Conteggio ATTUALE ore macchina IN LAVORO
///
@@ -146,6 +139,11 @@ namespace IOB_UT_NEXT.Iob
///
public DateTime lastIobOnline = DateTime.Now.AddHours(-1);
+ ///
+ /// Ultima verifica status IOB x forzare display status SRV
+ ///
+ public DateTime lastIobStatusDisplUpdate = DateTime.Now;
+
///
/// dataOra ultimo log periodico...
///
@@ -171,11 +169,6 @@ namespace IOB_UT_NEXT.Iob
///
public DateTime lastSim;
- ///
- /// Ultima verifica status IOB x forzare display status SRV
- ///
- public DateTime lastIobStatusDisplUpdate = DateTime.Now;
-
///
/// dataOra ultimo segnale inviato al SERVER...
///
@@ -206,11 +199,6 @@ namespace IOB_UT_NEXT.Iob
///
public bool needRefreshPzCount = true;
- ///
- /// Coda degli esiti di ping x calcolo stato macchina
- ///
- public DataQueue QueuePing;
-
///
/// Determina se utilizzare blocchi di memoria IOT contigui (e quindi processing
/// "monoblocco" semplificato"=
@@ -220,22 +208,27 @@ namespace IOB_UT_NEXT.Iob
///
/// Coda valori ALLARMI ove gestiti...
///
- public DataQueue QueueAlarm;// = new DataQueue("000", "QueueAlarm", false);
+ public DataQueue QueueAlarm;
///
/// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi)
///
- public DataQueue QueueFLog;// = new DataQueue("000", "QueueFLog", false);
+ public DataQueue QueueFLog;
///
/// Oggetto della coda degli elementi letti (e non ancora trasmessi)
///
- public DataQueue QueueIN;// = new DataQueue("000", "QueueIN", false);
+ public DataQueue QueueIN;
///
/// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)...
///
- public DataQueue QueueMessages;// = new DataQueue("000", "QueueMessages", false);
+ public DataQueue QueueMessages;
+
+ ///
+ /// Coda degli esiti di ping x calcolo stato macchina
+ ///
+ public DataQueue QueuePing;
///
/// Oggetto della coda degli elementi di tipo RawTransf (e non ancora trasmessi)
@@ -326,38 +319,20 @@ namespace IOB_UT_NEXT.Iob
///
/// Verifica se sia in modalità DEMO avanzata (campionamento da set di valori ammessi...)
///
- public static bool DemoInSample
- {
- get
- {
- return baseUtils.CRB("DemoInSample");
- }
- }
+ public static bool DemoInSample => baseUtils.CRB("DemoInSample");
///
/// Verifica se sia in modalità DEMO x dati OUTPUT
///
- public static bool DemoOut
- {
- get
- {
- return utils.CRB("DemoOut");
- }
- }
+ public static bool DemoOut => utils.CRB("DemoOut");
///
/// Indicazione VETO PING a server sino alla data-ora indicata
///
public static DateTime dtVetoPing
{
- get
- {
- return utils.dtVetoPing;
- }
- set
- {
- utils.dtVetoPing = value;
- }
+ get => utils.dtVetoPing;
+ set => utils.dtVetoPing = value;
}
///
@@ -365,14 +340,8 @@ namespace IOB_UT_NEXT.Iob
///
public static DateTime dtVetoQueueIN
{
- get
- {
- return utils.dtVetoQueueIN;
- }
- set
- {
- utils.dtVetoQueueIN = value;
- }
+ get => utils.dtVetoQueueIN;
+ set => utils.dtVetoQueueIN = value;
}
///
@@ -380,46 +349,29 @@ namespace IOB_UT_NEXT.Iob
///
public static DateTime dtVetoSend
{
- get
- {
- return utils.dtVetoSend;
- }
- set
- {
- utils.dtVetoSend = value;
- }
+ get => utils.dtVetoSend;
+ set => utils.dtVetoSend = value;
}
///
/// Verifica se sia abilitato test lettura blocchi memoria all'avvio
///
- public static bool EnableTest
- {
- get
- {
- return baseUtils.CRB("enableTest");
- }
- }
+ public static bool EnableTest => baseUtils.CRB("enableTest");
///
/// stato Online/Offline del server MP IO (su REDIS)
///
public static bool MPOnline
{
- get
- {
- return utils.MPIO_Online;
- }
- set
- {
- utils.MPIO_Online = value;
- }
+ get => utils.MPIO_Online;
+ set => utils.MPIO_Online = value;
}
#endregion Public Properties
#region Public Methods
+#if false
///
/// Effettua chiamata URL e restituisce risultato
///
@@ -452,7 +404,7 @@ namespace IOB_UT_NEXT.Iob
}
}
return answ;
- }
+ }
///
/// Effettua chiamata URL e restituisce risultato
@@ -480,6 +432,7 @@ namespace IOB_UT_NEXT.Iob
}
return answ;
}
+#endif
///
/// processa dataLayer e se necessario salva/mostra
@@ -504,22 +457,12 @@ namespace IOB_UT_NEXT.Iob
return sMacAddress;
}
- public static void resetDebugConsole()
- {
- }
-
- ///
- /// Reset dei webclients
- ///
- public static void resetWebClients()
- {
- utils.resetWebClients();
- }
-
#endregion Public Methods
#region Protected Fields
+
+
///
/// Valore di attesa (random) dopo ogni invio x evitare congestione send...
///
@@ -642,6 +585,49 @@ namespace IOB_UT_NEXT.Iob
return doVeto;
}
+ ///
+ /// Recupera la chiave per le statistiche delle chiamate.
+ ///
+ protected string GetCallStatsKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:CallStats");
+
+ ///
+ /// Recupera la chiave per i dati di produzione correnti.
+ ///
+ protected string GetCurrProdDataKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:CurrProdData");
+
+ ///
+ /// Recupera la chiave per il flusso di memoria.
+ ///
+ protected string GetFluxMemKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:FluxMem");
+
+ ///
+ /// Recupera la chiave per l'invio dei PODL.
+ ///
+ protected string GetPOdlSentKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:POdlSent");
+
+ ///
+ /// Recupera un valore specifico dal hash dello stato dell'IOB.
+ ///
+ protected string GetStatusField(string field)
+ {
+ string baseKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}");
+ return redisMan.redGetHashField(baseKey, field);
+ }
+
+ ///
+ /// Recupera la chiave per le statistiche settimanali.
+ ///
+ protected string GetWeekStatsKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:WeekStats");
+
+ ///
+ /// Imposta un valore nel hash dello stato dell'IOB.
+ ///
+ protected void SetStatusField(string field, string value)
+ {
+ string baseKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}");
+ redisMan.redSetHashField(baseKey, field, value);
+ }
+
///
/// Setup di tutti gli oggetti Queue, ma solo alcuni hanno coda REDIS (quelli senza sono "sacrificabili"
///
diff --git a/IOB-UT-NEXT/RedisMan.cs b/IOB-UT-NEXT/RedisIobCache.cs
similarity index 98%
rename from IOB-UT-NEXT/RedisMan.cs
rename to IOB-UT-NEXT/RedisIobCache.cs
index 714100a4..d4fb3ca7 100644
--- a/IOB-UT-NEXT/RedisMan.cs
+++ b/IOB-UT-NEXT/RedisIobCache.cs
@@ -1171,6 +1171,28 @@ namespace IOB_UT_NEXT
return answ;
}
+ ///
+ /// Salva un SINGOLO valore nella hash dati key e field
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool redSetHashField(string hashKey, string hashField, string value)
+ {
+ bool answ = false;
+ // cerco se ci sia valore in redis...
+ try
+ {
+ answ = currDb.HashSet((RedisKey)hashKey, (RedisValue)hashField, (RedisValue)value);
+ }
+ catch (Exception exc)
+ {
+ Logging.Instance.Error($"redSetHashField {exc}");
+ }
+ return answ;
+ }
+
///
/// Conteggio elementi in QUEUE (LIFO)
///
diff --git a/IOB-UT-NEXT/baseUtils.cs b/IOB-UT-NEXT/baseUtils.cs
index c39aa38b..330d2642 100644
--- a/IOB-UT-NEXT/baseUtils.cs
+++ b/IOB-UT-NEXT/baseUtils.cs
@@ -600,12 +600,6 @@ namespace IOB_UT_NEXT
if (num >= 100) num /= 100;
if (num >= 10) num /= 10;
-#if false
- // formulazione alternativa con ciclo...
- while (num >= 10)
- num /= 10;
-#endif
-
return num;
}
@@ -812,15 +806,6 @@ namespace IOB_UT_NEXT
return result;
}
- public static void resetWebClients()
- {
-#if false
- // resetto i webclients...
- client = new WebClientWT();
- clientPayload = new WebClientWT();
-#endif
- }
-
///
/// Effettua reverse della stringa
///
diff --git a/IOB-WIN-FANUC/DATA/CONF/MAIN.ini b/IOB-WIN-FANUC/DATA/CONF/MAIN.ini
index f621315d..51e0748d 100644
--- a/IOB-WIN-FANUC/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-FANUC/DATA/CONF/MAIN.ini
@@ -34,3 +34,4 @@ CLI_INST=SteamWareSim
STARTLIST=SIMUL_01
MAXCNC=10
+
diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs
index 39a2c8a5..89233c02 100644
--- a/IOB-WIN-FORM/AdapterForm.cs
+++ b/IOB-WIN-FORM/AdapterForm.cs
@@ -1466,7 +1466,7 @@ namespace IOB_WIN_FORM
// salvo nuovo valore invio
iobObj.LastSendSet(sendKey, DateTime.Now);
// segnalo reboot (programma - url file)...
- await Iob.Generic.callUrl(iobObj.urlReboot, true);
+ await utils.callUrlAsync(iobObj.urlReboot);
}
}
else
diff --git a/IOB-WIN-FORM/IOB-WIN-FORM.csproj b/IOB-WIN-FORM/IOB-WIN-FORM.csproj
index f066e5f4..9df513cc 100644
--- a/IOB-WIN-FORM/IOB-WIN-FORM.csproj
+++ b/IOB-WIN-FORM/IOB-WIN-FORM.csproj
@@ -131,9 +131,10 @@
-
-
+
+
+
Form
diff --git a/IOB-WIN-FORM/Iob/Generic.Public.cs b/IOB-WIN-FORM/Iob/Generic.Public.cs
deleted file mode 100644
index 8e46735d..00000000
--- a/IOB-WIN-FORM/Iob/Generic.Public.cs
+++ /dev/null
@@ -1,4053 +0,0 @@
-using EgwProxy.Ftp;
-using IOB_UT_NEXT;
-using IOB_UT_NEXT.Config;
-using MapoSDK;
-using MathNet.Numerics.Statistics;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using NLog;
-using NLog.Targets.Wrappers;
-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_FORM.Iob
-{
- public partial class Generic : BaseObj
- {
- #region Public Fields
-
- public int numPzReqOdl = 0;
-
- #endregion Public Fields
-
- #region Public Constructors
-
- ///
- /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto
- ///
- /// Form chiamante
- /// Configurazione (v 4.x)
- public Generic(AdapterForm caller, IobConfTree IobConfNew)
- {
- // salvo il form chiamante
- parentForm = caller;
- if (IobConfNew != null)
- {
- // salvo configurazione...
- IOBConfFull = IobConfNew;
-
- // init oggetto redis...
- redisMan = new RedisIobCache(IobConfNew.MapoMes.IpAddr, IobConfNew.General.FilenameIOB, $"{IobConfNew.General.IobType}", IobConfNew.General.MinDeltaSec);
-
- // init code
- SetupQueue();
-
- // initi oggetto TCMan
- tcMan = new TCMan(IobConfNew.TCDataConf.Lambda, IobConfNew.TCDataConf.MaxDelayFactor, IobConfNew.TCDataConf.MaxIncrPz);
-
- lastConnectTry = DateTime.Now;
-
- lgInfo("Avvio preliminare AdapterGeneric");
- lastLogStartup = DateTime.Now;
-
- // setup currProdData & last prod data
- currProdData = redisMan.redGetHashDict(rKeyCurrProdData);
- // i last li avvio a VUOTI... x evitare errore mancata riscrittura SIMEC che ha memorie NON ritentive
- lastProdData = new Dictionary();
- //lastProdData = new Dictionary(currProdData);
- // 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);
- string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}";
- lgInfoStartup(msgVeto);
- lgInfo(msgVeto);
-
- // invio info IOB
- SendM2IOB();
- // invio altri dati accessori...
- SendMachineConf();
- if (resetAlarmOnStart)
- {
- SendAlarmReset();
- }
- // concluso!
- lgInfoStartup("Istanziata classe preliminare IOBGeneric");
- }
- else
- {
- lgError("Error: IobCOnf is null!");
- }
- }
-
- #endregion Public Constructors
-
- #region Public Events
-
- ///
- /// Evento Iob ha subito un refresh
- ///
- public event EventHandler eh_refreshed;
-
- #endregion Public Events
-
- #region Public Properties
-
- ///
- /// Salva verifica stato connessione OK con macchina (PLC/CNC)
- ///
- ///
- public virtual bool connectionOk
- {
- get
- {
- return _connOk || DemoIn;
- }
- set
- {
- _connOk = value;
- }
- }
-
- ///
- /// Contapezzi attuale
- ///
- public Int32 contapezziIOB
- {
- get
- {
- return tcMan.pzCountIOB;
- }
- set
- {
- tcMan.pzCountIOB = value;
- }
- }
-
- ///
- /// Ultima lettura variabile contapezzi da CNC
- ///
- public Int32 contapezziPLC
- {
- get
- {
- return tcMan.pzCountPLC;
- }
- set
- {
- tcMan.pzCountPLC = value;
- }
- }
-
- ///
- /// Contatore x invio dati FluxLog
- ///
- public int counterFLog { get; set; }
-
- ///
- /// Contatore x invio dati RawTransf
- ///
- public int counterRawTransf { get; set; }
-
- ///
- /// Contatore x invio dati SignalIN
- ///
- public int counterSigIN { get; set; }
-
- ///
- /// Contatore x invio dati UserLog
- ///
- public int counterULog { get; set; }
-
- ///
- /// nome Programma corrente
- ///
- public string currPrgName { get; set; }
-
- ///
- /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA...
- ///
- public bool DemoIn
- {
- get => IOBConfFull.General.IobType == tipoAdapter.SIMULA;
- }
-
- ///
- /// Dizionario contapezzi Macchina (valori da impianto) x macchine multi tavola/pallet
- ///
- public Dictionary DictPzCountImp { get; set; } = new Dictionary();
-
- ///
- /// Dizionario contapezzi MES (valori salvati su server) x macchine multi tavola/pallet
- ///
- public Dictionary DictPzCountMes { get; set; } = new Dictionary();
-
- ///
- /// Indica se la chiamata WDST dit racking watchdog sia disabilitata dall'invio nel FluxLog
- ///
- public bool disableWdst { get; set; } = false;
-
- ///
- /// Indica lo stato Online/Offline della IOB
- ///
- public bool IobOnline
- {
- get
- {
- return utils.IOB_Online;
- }
- set
- {
- utils.IOB_Online = value;
- }
- }
-
- ///
- /// Verifica se sia macchina multi = DoppioPallet da CONF
- ///
- public bool isMulti
- {
- get => IOBConfFull.Device.IsMulti;
- }
-
- ///
- /// Log verboso da configurazione (SOLO CHIAVE "verbose"...)
- ///
- public bool isVerboseLog { get; set; } = utils.CRB("verbose");
-
- ///
- /// Ultimo Alarm letto
- ///
- public string lastAlarm { get; set; }
-
- ///
- /// Ultimo ARRAY DynData letto
- ///
- public Dictionary lastDynData { get; set; } = new Dictionary();
-
- ///
- /// Ultimo DynData (sunto) letto
- ///
- public string lastDynDataCtrlVal { get; set; }
-
- ///
- /// Ultimo Override set letto
- ///
- public string lastOverrideFS { get; set; }
-
- ///
- /// Ultimo Override set letto
- ///
- public string lastOverrideRapid { get; set; }
-
- ///
- /// Ultimo programma letto
- ///
- public string lastPrgName { get; set; }
-
- ///
- /// Ultimo SysInfo letto
- ///
- public string lastSysInfo { get; set; }
-
- ///
- /// Ultimo URL
- ///
- public string lastUrl { get; set; }
-
- ///
- /// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per
- /// 100... 200% --> 200)
- ///
- public int maxPzDeltaPerc
- {
- get => IOBConfFull.Counters.MaxIncrPzCountPerc;
- }
-
- ///
- /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...)
- ///
- public bool periodicLog
- {
- get
- {
- bool answ = false;
- answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut"));
- if (answ)
- {
- lastPeriodicLog = DateTime.Now;
- }
-
- return answ;
- }
- }
-
- ///
- /// Valore medio del TC rilevato x verifica derive sul delta variazione contapezzi
- ///
- public double plcAvgTc
- {
- get
- {
- double answ = tcMan.avgTC > 0 ? tcMan.avgTC : 1;
- return answ;
- }
- }
-
- ///
- /// DataOra dell'ultima lettura variabile contapezzi da CNC
- ///
- public DateTime plcLastPzRead
- {
- get
- {
- return tcMan.lastObservedData;
- }
- }
-
- ///
- /// Determina se il contapezzi plc sia valido (lo è se data avvio adapter è prima di ultimo dato registrato in contapezzi)
- ///
- public bool plcPzCountValid
- {
- get => tcMan.lastObservedData > dtAvvioAdp;
- }
-
- ///
- /// Abilitazione coda segnali ingresso
- ///
- public bool queueInEnabCurr
- {
- get => qInEnabCurr;
- set
- {
- qInEnabCurr = value;
- lgInfo($"SET queueInEnabCurr: {value} | {DateTime.Now:HHmmss}");
- }
- }
-
- ///
- /// Finestra dei byte da mostrare di default x il RawDataInput
- ///
- public int RawDataInputSize { get; set; } = 8;
-
- ///
- /// Indice di partenza della memoria RawData Input da mostrare
- ///
- public int RawDataInputStart { get; set; } = 0;
-
- ///
- /// URL per segnalazione reboot...
- ///
- public string urlReboot
- {
- get => $@"{urlCommandIobFile("sendReboot")}?mac={GetMACAddress()}";
- }
-
- ///
- /// URL per salvataggio dati conf YAML completi IOB...
- ///
- public string urlSaveConfYaml
- {
- get => $@"{urlCommandIobFile("saveConfYaml")}";
- }
-
- ///
- /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN)
- ///
- 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
-
- ///
- /// Esegue conversione in un dizionario di tipo string/string serializzando e deserializzando
- ///
- ///
- ///
- public static Dictionary ConvertToStringDict(Dictionary input)
- {
- return input.ToDictionary(pair => pair.Key, pair => pair.Value?.ToString());
- }
-
- ///
- /// Accumula in coda i valori ALARM e logga...
- ///
- /// VALORE RAW (x display)
- /// VALORE già processato con qEncodeFLog(...)
- 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;
- }
- }
-
- ///
- /// Accumula in coda i valori Flux Log e logga. Restituisce true se inviato (x track su REDIS)
- ///
- /// Nome del flusso da inviare (per verifica veto invio)
- /// VALORE RAW (x display)
- /// VALORE già processato con qEncodeFLog(...)
- public bool accodaFLog(string codFlux, string val, string encodedVal)
- {
- bool enabled = false;
- // verifico se il parametro sia abilitato...
- if (IOBConfFull.Memory != null && IOBConfFull.Memory.mMapRead != null && IOBConfFull.Memory.mMapRead.ContainsKey(codFlux))
- {
- enabled = IOBConfFull.Memory.mMapRead[codFlux].sendEnabled;
- if (!enabled)
- {
- lgDebug($"accodaFLog : invio bloccato per conf parametro sendEnabled | codFlux: {codFlux}");
- }
- else
- {
- // processo SOLO SE ho dei valori non nulli x encodedVal...
- if (!string.IsNullOrEmpty(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($"[QUEUE-FLOG] {encodedVal}");
- counterFLog++;
- if (counterFLog > 9999)
- {
- counterFLog = 0;
- }
- }
- else
- {
- lgTrace($"ERRORE in [QUEUE-FLOG] : encodedVal vuoto | val: {val}");
- }
- }
- }
- else
- {
- // registro nel dizionario dei valori FluxLog filtrati
- SaveFiltFluxLog(codFlux);
- // verifico se registrare elenco valori filtrati in log...
- FiltFluxLogCheckSave(100);
- }
- return enabled;
- }
-
- ///
- /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA
- ///
- ///
- public void accodaOtherData(string newLine)
- {
- // inserisco in cima allo stack
- parentForm.WriteTextSafe(newLine);
- }
-
- ///
- /// Accumula in coda i valori RawData + log
- ///
- ///
- ///
- 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;
- }
- }
-
- ///
- /// Accumula in coda i valori Signal IN e logga...
- /// Parametri da aggiornare x display in form
- ///
- public void accodaSigIN(ref newDisplayData currDispData)
- {
- DateTime adesso = DateTime.Now;
- lgDebug($"accodaSigIN 01 | qEncodeIN: {qEncodeIN}");
- // mostro dati variati letti...
- displayInData(ref currDispData);
- // verifico veto a invio status macchina
- if (IOBConfFull.Device.DisabSigIn)
- {
- lgTrace($"Filtrato accodamento valore da conf IOB | DisabSigIn | [QUEUE-IN] {qEncodeIN}");
- }
- else
- {
- // verifico non sia in veto invio iniziale...
- if (queueInEnabCurr)
- {
- // --> accodo (valore già formattato)!
- QueueIN.Enqueue(qEncodeIN);
- // loggo!
- lgDebug($"[QUEUE-IN] {qEncodeIN}");
- }
- else
- {
- lgTrace($"[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;
- }
- }
- lgDebug($"accodaSigIN 02 | nReadFilt: {nReadFilt} | counterSigIN: {counterSigIN} | QueueIN len: {QueueIN.Count}");
- }
-
- ///
- /// Accumula in coda i valori USER LOG e logga...
- ///
- /// VALORE RAW (x display)
- /// VALORE già processato con qEncodeULog(...)
- 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;
- }
- }
-
- ///
- /// Verifica se la IOB sia ENABLED (da server o Demo)
- ///
- public async Task CheckIobEnabled()
- {
- // 1. Controllo Veto (Sincrono)
- if (dtVetoCheckIOB >= DateTime.Now)
- return IobOnline;
-
- bool currentAnsw = false;
-
- if (DemoOut)
- {
- currentAnsw = (QueueIN.Count + QueueFLog.Count >= nMaxSend);
- }
- else
- {
- currentAnsw = await ExecuteIobCheckWithRetry(maxRetries: 5);
- }
-
- // 3. Gestione Stato e Logica UI
- UpdateIobState(currentAnsw);
-
- return currentAnsw;
- }
-
- ///
- /// Verifica veto invio per una chiave specifica in Redis
- ///
- ///
- ///
- public bool CheckSendVeto(string keyReq, TimeSpan vetoReq)
- {
- DateTime adesso = DateTime.Now;
- DateTime lastSend = LastSendGet(keyReq);
- bool sendEnab = lastSend.Add(vetoReq) <= adesso;
- return sendEnab;
- }
-
- ///
- /// Verifica se il server sia ALIVE (tramite PING)
- ///
- public bool CheckServerAlive() // Rimosso async, restituisce bool
- {
- // 1. Controllo Veto (Sincrono)
- if (dtVetoPing >= DateTime.Now) return MPOnline;
- if (DemoOut) return true;
-
- // 2. Eseguo la parte asincrona forzando l'attesa
- // Usiamo Task.Run per far girare il codice asincrono su un thread del pool
- // evitando deadlock con il thread della UI
- bool isAlive = Task.Run(async () => await ExecuteApiCheckWithRetryAsync(maxRetries: 7))
- .GetAwaiter()
- .GetResult();
-
- // 3. Gestione Stato (Sincrono)
- UpdateServerState(isAlive);
-
- return isAlive;
- }
-
- ///
- /// Verifica se il server sia ALIVE (tramite PING)
- ///
- public async Task CheckServerAliveAsync()
- {
- // 1. Controllo Veto (Sincrono)
- if (dtVetoPing >= DateTime.Now) return MPOnline;
- if (DemoOut) return true;
-
- bool isAlive = await ExecuteApiCheckWithRetryAsync(maxRetries: 7);
-
- // 3. Gestione Stato (Sincrono)
- UpdateServerState(isAlive);
-
- return isAlive;
- }
-
- ///
- /// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile
- ///
- public void checkVetoQueueIn()
- {
- queueInEnabCurr = dtVetoQueueIN < DateTime.Now;
- }
-
- ///
- /// Update visualizzaizone BIT in ingresso Parametri da
- /// aggiornare x display in form
- ///
- 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:X}";
- currDispData.newSignalData = newString;
- }
- }
-
- ///
- /// Mostra cosa ha/avrebbe inviato
- ///
- ///
- public void displayOtherData(string newData)
- {
- // mostro update...
- accodaOtherData(newData);
- }
-
- ///
- /// Effettua i task di comunicazione IN/OUT con la macchina
- ///
- ///
- ///
- public void doMachineTask(gatherCycle ciclo)
- {
- // init obj display
- newDisplayData currDispData = new newDisplayData();
- // 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
- {
- if (ciclo == gatherCycle.VHF)
- {
- //processVHF();
- }
- // processing dati memoria (lettura, filtraggio, enqueque)
- else if (ciclo == gatherCycle.HF)
- {
- processWhatchDog();
- //Thread.Sleep(5);
- processAllMemory();
- }
- else if (ciclo == gatherCycle.MF)
- {
- processMode();
- //Thread.Sleep(5);
- ExecServerRequests();
- //Thread.Sleep(5);
- processCustomTaskMF();
- //Thread.Sleep(5);
- processOverride();
- //Thread.Sleep(5);
- processContapezzi();
- //Thread.Sleep(5);
- processCncAlarms();
- //Thread.Sleep(5);
- processDynData();
- //Thread.Sleep(5);
- processMem2Write();
- }
- else if (ciclo == gatherCycle.LF)
- {
- processCustomTaskLF();
- //Thread.Sleep(5);
- processOtherCounters().GetAwaiter().GetResult(); ;
- //Thread.Sleep(5);
- processProgram();
- }
- else if (ciclo == gatherCycle.VLF)
- {
- //processRecipeSyncArch();
- // recupero dati SETUP (sysinfo) e li invio/mostro se variati...
- processSysInfo();
- if (enableSlowData)
- {
- //Thread.Sleep(5);
- processSlowDataRead();
- }
- }
-
- }
- catch (Exception exc)
- {
- // segnalo eccezione e indico disconnesso...
- lgError($"Exception doMachineTask | {ciclo} | fermo adapter{Environment.NewLine}{exc}");
- parentForm.fermaAdapter(true, false, true);
- }
- // tolgo flag running
- adpCommAct = false;
- }
- else
- {
- if (periodicLog)
- {
- lgDebug("ADP not running...");
- }
- }
- }
- else
- {
- // log ADP running
- lgInfo("Non eseguo chiamata: ADP ancora in running");
- // se è bloccato da oltre maxSec lo sblocco...
- if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec"))
- {
- // tolgo flag running
- adpCommAct = false;
- adpStartRun = DateTime.Now;
- }
- }
- }
- else
- {
- // provo a riconnettere SE abilitato tryRestart...
- if (adpTryRestart && !connectionOk)
- {
- // controllo se sia scaduto periodi di veto al tryConnect...
- int waitRecMSec = utils.CRI("waitRecMSec");
- // cerco se ci sia un valore in ovverride x il singolo IOB...
- if (IOBConfFull.General.WaitRecMsec > 0)
- {
- waitRecMSec = IOBConfFull.General.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);
- }
-
- ///
- /// Effettua i task di comunicazione IN/OUT con il server MAPO
- ///
- ///
- ///
- public async Task doServerTaskAsync(gatherCycle ciclo)
- {
- // init obj display
- newDisplayData currDispData = new newDisplayData();
- // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!!
- try
- {
- await TrySendValuesAsync();
- }
- catch (Exception exc)
- {
- lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria");
- currDispData.semOut = Semaforo.SR;
- }
- // controllo connessione/connettività verso PLC...
- if (connectionOk)
- {
- // 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)
- {
- // recupera ed invia risposte al server
- await ServerPutRespAsync();
- }
- else if (ciclo == gatherCycle.MF)
- {
- // recupero elenco richieste
- await ServerGetRequestsAsync();
- }
- else if (ciclo == gatherCycle.LF)
- {
-
- // verifico se devo gestire cambio ODL in modo automatico
- await ProcessAutoOdlAsync();
- // verifico se devo gestire auto generazione dossier quotidiana
- ProcessAutoDossier();
- // effettua gestione import file se configurato...
- await ProcessFileImportAsync();
- // effettua process ritorno ricette
- await ProcessRecipeFileRetAsync();
- }
- else if (ciclo == gatherCycle.VLF)
- {
- if (utils.CRB("enableContapezzi"))
- {
- // rilettura contapezzi da server...
- lgTrace("Ciclo MsVLF: pzCntReload(true)");
- if (!isMulti)
- {
- pzCntReload(true);
- }
-
- // refresh associazione Macchina - IOB
- await SendM2IobAsync();
- // invio altri dati accessori...
- await SendMachineConfAsync();
- }
- // checkLogDir x shrink!
- checkShrinkDir();
- // eventuale log!
- if (utils.CRB("recTime"))
- {
- try
- {
- logTimeResults();
- }
- catch
- { }
- }
- processRecipeSyncArch();
- }
-
- // 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($"Exception | doServerTaskAsync | {ciclo}{Environment.NewLine}{exc}");
- }
- }
- 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
- await ProcessAutoOdlAsync();
- // verifico se devo gestire auto generazione dossier quotidiana
- ProcessAutoDossier();
- // effettua gestione import file se configurato...
- await ProcessFileImportAsync();
- // effettua process ritorno ricette
- await ProcessRecipeFileRetAsync();
- }
- 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");
- // // cerco se ci sia un valore in ovverride x il singolo IOB...
- // if (IOBConfFull.General.WaitRecMsec > 0)
- // {
- // waitRecMSec = IOBConfFull.General.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);
- }
-
- public void ExecServerRequests()
- {
- // verifica se ci siano richieste da eseguire
- if (QueueSrvReq.Count > 0)
- {
- if (MPOnline)
- {
- if (IobOnline)
- {
- // prendo elenco
- List listaValori = QueueSrvReq.ToList();
-
- foreach (var rawJob in listaValori)
- {
- // deserializzo...
- JobTaskData jobTaskReq = JsonConvert.DeserializeObject(rawJob);
- // processo!
- var reqDict = JobTaskData.TaskDict(jobTaskReq.RawData);
- if (reqDict.Count > 0)
- {
- var taskDone = ProcessTask(JobTaskData.TaskDict(jobTaskReq.RawData), jobTaskReq.CodTav);
- // accodo task eseguiti...
- string serVal = JsonConvert.SerializeObject(taskDone);
- accodaServResp(jobTaskReq.CodTav, serVal);
- }
- }
-
- // svuoto!
- QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan);
- }
- }
- }
- }
-
- ///
- /// Esecuzione dei task richiesti e pulizia coda richieste eseguite
- ///
- /// Elenco task da eseguire
- /// Codice TAV (per macchine multi pallet) - opzionale
- public virtual Dictionary executeTasks(Dictionary task2exe, string codTav)
- {
- string logMsg = $"Generic: call executeTasks | {task2exe.Count} task";
- if (!string.IsNullOrEmpty(codTav))
- {
- logMsg += $" | codTav: {codTav}";
- }
- lgInfo(logMsg);
- // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
- Dictionary taskDone = new Dictionary();
- 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);
- string iKey = item.Key;
- // se è DP aggiungo in chiave il valore della TAV richiesta... ad es setComm --> setComm#TAV_1
- if (!string.IsNullOrEmpty(codTav))
- {
- iKey += $"#{codTav}";
- }
- // 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(iKey))
- {
- dataConf currMem = memMap.mMapWrite[iKey];
- string addr = currMem.memAddr;
- taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
- // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
- memMap.mMapWrite[iKey].value = item.Value;
- }
- else
- {
- taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
- }
- }
- else
- {
- taskVal = $"NO BankConf found, SET task: {iKey} --> {item.Value}";
- }
- // salvo in currProd..
- upsertKey(iKey, item.Value);
-
- break;
-
- case taskType.endProd:
- // reset contapezzi inizio setup
- if (IOBConfFull.Counters.ResetOnProdEnd)
- {
- lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC");
- taskOk = resetContapezziPLC(codTav);
- }
- break;
-
- case taskType.forceResetPzCount:
- // recupero dati da memMap...
- if (memMap != null && memMap.mMapWrite != null)
- {
- lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC | memMap");
- if (memMap.mMapWrite.ContainsKey(iKey))
- {
- dataConf currMem = memMap.mMapWrite[iKey];
- string addr = currMem.memAddr;
- taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
- // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
- memMap.mMapWrite[iKey].value = item.Value;
- taskOk = true;
- }
- else
- {
- taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
- }
- // imposto reset gestione pzcount da parametri
- vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup);
- dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
- }
- else
- {
- // reset contapezzi senza memMap
- lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC | NO memMap");
- taskOk = resetContapezziPLC(codTav);
- taskVal = taskOk ? "forceResetPzCount | RESET PZ COUNT OK" : "forceResetPzCount | PZ RESET DISABLED | NO EXEC";
- }
- lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");
- break;
-
- case taskType.forceSetPzCount:
- // recupero dati da memMap...
- if (memMap != null && memMap.mMapWrite != null)
- {
- if (memMap.mMapWrite.ContainsKey(iKey))
- {
- dataConf currMem = memMap.mMapWrite[iKey];
- string addr = currMem.memAddr;
- taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
- // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
- memMap.mMapWrite[iKey].value = item.Value;
- }
- else
- {
- taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
- }
- }
- else
- {
- taskVal = $"NO BankConf found, SET task: {iKey} --> {item.Value}";
- }
-
- lgInfo($"Chiamata forceSetPzCount: taskVal: {taskVal}");
- 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(iKey))
- {
- dataConf currMem = memMap.mMapWrite[iKey];
- string addr = currMem.memAddr;
- taskVal = $"SET task: {iKey} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
- // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
- memMap.mMapWrite[iKey].value = newVal;
- }
- else
- {
- taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})";
- }
- }
- else
- {
- taskVal = $"NO BankConf found, SET task: {iKey} --> {newVal} ({item.Value})";
- }
-
- // salvo in currProd..
- upsertKey(iKey, 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(iKey))
- {
- dataConf currMem = memMap.mMapWrite[iKey];
- string addr = currMem.memAddr;
- taskVal = $"SET task: {iKey} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
- // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
- memMap.mMapWrite[iKey].value = newVal;
- }
- else
- {
- taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})";
- }
- }
- else
- {
- taskVal = $"NO BankConf found, SET task: {iKey} --> {newVal} ({item.Value})";
- }
-
- // salvo in currProd..
- upsertKey(iKey, newVal);
- break;
-
- case taskType.startSetup:
- // reset contapezzi inizio setup
- if (IOBConfFull.Counters.ResetOnSetupStart)
- {
- lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC");
- taskOk = resetContapezziPLC(codTav);
- vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup);
- dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
- }
- taskVal = taskOk ? "startSetup | RESET: SETUP START" : "startSetup | PZ RESET DISABLED | NO EXEC";
- lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");
- break;
-
- case taskType.stopSetup:
- // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO
- if (IOBConfFull.Counters.ResetOnSetupStop)
- {
- lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC");
- taskOk = resetContapezziPLC(codTav);
- }
- taskVal = taskOk ? "stopSetup | RESET: SETUP END" : "stopSetup | 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(iKey, item.Value);
- taskVal = okProc ? $"OK ProcessOtherInfoAsync | {iKey} | {item.Value}" : $"ERROR ProcessOtherInfoAsync | {iKey} | {item.Value}";
-#if false
- try
- {
- Task.Run(async () => okProc = await ProcessOtherInfoAsync(iKey, item.Value))
- .GetAwaiter()
- .GetResult();
- taskVal = okProc ? $"OK ProcessOtherInfoAsync | {iKey} | {item.Value}" : $"ERROR ProcessOtherInfoAsync | {iKey} | {item.Value}";
- }
- catch (Exception ex)
- {
- lgError("ProcessOtherInfoAsync | Crash nel ponte Sync/Async: " + ex.Message);
- }
-#endif
- break;
-
- default:
- taskVal = $"taskReq: {tName} | key: {iKey} | 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 BankConf) | 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;
- }
-
- ///
- /// Cerca parametri opzionali in modalità "like" del nome
- ///
- ///
- ///
- public Dictionary findOptPar(string keyStartSearch = "")
- {
- Dictionary answ = new Dictionary();
- // controllo SE keySearch !=""
- if (!string.IsNullOrWhiteSpace(keyStartSearch))
- {
- if (IOBConfFull.OptPar.Count > 0)
- {
- // ciclo su tutti e cerco occorrenze che INIZINO...
- foreach (var item in IOBConfFull.OptPar)
- {
- if (item.Key.StartsWith(keyStartSearch))
- {
- answ.Add(item.Key, item.Value);
- }
- }
- }
- }
- return answ;
- }
-
- ///
- /// forza reset ODL
- ///
- public void forceResetOdl()
- {
- lgInfo("Registrato richiesta forzatura reset ODL");
- pzCountResetted = true;
- }
-
- ///
- /// Effettua chiamata x split ODL
- ///
- ///
- public async Task 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 = "";
- try
- {
- /***************************************************
- * Descrizione procedura (OK X SIMULATORI... da provare x DP come OpcUaSiemensSW)
- *
- * - chiamata su MP/IO
- * - verifica che su DB sia abilitato AUTO ODL
- * - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW
- * - viene duplicato e chiuso ODL corrente
- * - viene fatto partire ODL nuovo ADESSO
- * - num pezzi come ODL precedente (o da media 3 ODL precedenti)
- * - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL)
- * - reset contapezzi PLC locale...
- *
- *
- *
- * DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE
- * - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI)
- * - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!)
- * - viene 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
- *
- * ***************************************************/
-
- // se normale splitto!
- if (!isMulti)
- {
- // invio chiamata URL x reset ODL su macchina
- rawSplit = await callUrl(urlForceSplit, false);
- fatto = (rawSplit != "KO") ? true : false;
- }
- // se multi gestisco il bit delle tavole...
- else
- {
- foreach (string item in IOBConfFull.Device.MultiIobList)
- {
- // invio chiamata URL x reset ODL su macchina, ATTENZIONE scriviamo
- // | al posto di "#" che in URL sarebbe filtrato...
- fullUrl = $"{urlForceSplit}&multi={item}";
- rawSplit = await 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;
- }
-
- ///
- /// Processing degli allarmi (se presenti e gestiti)
- ///
- ///
- public virtual Dictionary getAlarmData()
- {
- Dictionary outVal = new Dictionary();
- // 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;
- }
-
- ///
- /// Effettua conversione da valore bitmap ai valori configurati x allarme
- ///
- /// Configurazione banco allarmi
- /// BitState allarmi
- ///
- public string getAlarmState(BaseAlarmConf memConf, byte[] bState)
- {
- string valTransl = "";
- List descAttivi = new List();
- 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;
- }
-
- ///
- /// Effettua conversione tra gli allarmi attivi secondo configurazione banco allarmi
- ///
- /// Configurazione banco allarmi
- /// num allarmi attivi
- ///
- public virtual string getAlarmState(BaseAlarmConf memConf, int numAlarms)
- {
- string valTransl = "";
- List descAttivi = new List();
-
- // FAKE!!!: dovrebbe cercare in aree memoria x ogni valore configurato, vedere
- // implementazione specifica es OpcUa
-
- // combino string
- valTransl = string.Join(",", descAttivi);
- return valTransl;
- }
-
- ///
- /// Effettua conversione da valore bitmap ai valori configurati
- ///
- ///
- ///
- ///
- public string getBitmapState(dataConfTSVC memConf, byte[] bState)
- {
- string valTransl = "";
- List descAttivi = new List();
- 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;
- }
-
- ///
- /// Recupera eventuali allarmi CNC...
- ///
- public virtual Dictionary getCncAlarms()
- {
- Dictionary outVal = new Dictionary();
- return outVal;
- }
-
- ///
- /// Restituisce info DINAMICHE
- ///
- ///
- public virtual Dictionary getDynData()
- {
- Dictionary outVal = new Dictionary();
- return outVal;
- }
-
- ///
- /// Cerca se esiste il parametro opzionale nei KVP (JSON) e lo restituisce
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// Cerca se esiste il parametro opzionale e lo restituisce
- ///
- ///
- ///
- public string getOptPar(string key)
- {
- return IOBConfFull.OptParGet(key);
- }
-
- ///
- /// Cerca se esiste un link tra aree di memoria in write e lo restituisce
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// Restituisce info OVERRIDES
- ///
- ///
- public virtual Dictionary getOverrides()
- {
- Dictionary outVal = new Dictionary();
- return outVal;
- }
-
- ///
- /// Restituisce programma in esecuzione
- ///
- public virtual string getPrgName()
- {
- return "";
- }
-
- ///
- /// Restituisce info sistema
- ///
- ///
- public virtual Dictionary getSysInfo()
- {
- Dictionary outVal = new Dictionary();
- return outVal;
- }
-
- ///
- /// Recupera la VC x TS, svuotando lista e resettando periodo partenza
- ///
- /// Nome della VC
- /// Reimposta e resetta array VC
- ///
- 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();
- TSVC_Data[VCName].DTStart = DateTime.Now;
- }
- }
- return answ;
- }
-
- ///
- /// Recupera la VC x TS, svuotando lista e resettando periodo partenza
- ///
- /// Nome della VC
- /// Reimposta e resetta array VC
- ///
- 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();
- TSVC_Data[VCName].DTStart = DateTime.Now;
- }
- }
- return answ;
- }
-
- ///
- /// Area init asincrono (fare override async!o)
- ///
- ///
- public virtual Task InitializeAsync()
- {
- // da usare per implementare logiche di init specifiche
- return Task.CompletedTask;
- }
-
- ///
- /// Restituisce un payload in formato json della lista di valori ricevuta
- ///
- /// Tipo di URL (eventi / FLog)
- /// elenco di valori da coda string salvata
- ///
- public string jsonPayload(urlType tipoUrl, List 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();
- // inizio processando ogni valore
- foreach (var item in elencoValori)
- {
- if (item != null)
- {
- 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();
- // 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();
- // inizio processando ogni valore
- foreach (var item in elencoValori)
- {
- try
- {
- currRTData = JsonConvert.DeserializeObject(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}]";
-
- //hasVeto = "{" + $"\"rawTransfData\":[{rawResult}]" + "}";
-
- break;
-
- case urlType.ULog:
- int numVal = 0;
- int matrOp = 0;
- ulogData currUlData = new ulogData();
- ulogJsonPayload fullUlObj = new ulogJsonPayload();
- fullUlObj.fluxData = new List();
- // 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;
- }
-
- ///
- /// Restitusice valore ultimo invio di una chiave, se non esistesse fornisce valore di 1 gg prima e lo salva...
- ///
- ///
- ///
- public DateTime LastSendGet(string keyReq)
- {
- DateTime lastSend = DateTime.Now.AddDays(-1);
- string lastSendKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:LastSend");
- string rawVal = redisMan.redGetHashField(lastSendKey, keyReq);
- if (!string.IsNullOrEmpty(rawVal))
- {
- lastSend = JsonConvert.DeserializeObject(rawVal);
- }
- else
- {
- rawVal = JsonConvert.SerializeObject(lastSend);
- KeyValuePair[] hashFields = new KeyValuePair[1];
- hashFields[0] = new KeyValuePair(keyReq, rawVal);
- redisMan.redSaveHash(lastSendKey, hashFields);
- }
- return lastSend;
- }
-
- ///
- /// Imposta valore datetime x ultimo invio di una chiamata in REDIS HashSet
- ///
- ///
- ///
- public bool LastSendSet(string keyReq, DateTime dtRif)
- {
- string lastSendKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:LastSend");
- string rawVal = JsonConvert.SerializeObject(dtRif);
- KeyValuePair[] hashFields = new KeyValuePair[1];
- hashFields[0] = new KeyValuePair(keyReq, rawVal);
- bool fatto = redisMan.redSaveHash(lastSendKey, hashFields);
- return fatto;
- }
-
- ///
- /// Effettua un trim della stringa al numero max di linee da mostrare a video
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// riporta il log di tutti i dati di results temporali registrati
- ///
- 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 == IOBConfFull.General.FilenameIOB)
- {
- 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, IOBConfFull.General.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, IOBConfFull.General.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));
- }
- }
-
- ///
- /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza)
- ///
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// Verifica e processing x gestione Dossier quotidiani automatica
- ///
- public void ProcessAutoDossier()
- {
- bool fatto = false;
- string callResp = "";
- if (IOBConfFull.FluxLog.AutoSnapshotDossier)
- {
- DateTime adesso = DateTime.Now;
- if (adesso > dtVetoAutoDossier)
- {
- dtVetoAutoDossier = adesso.AddMinutes(30);
- lgTrace("Richiesta ProcessAutoDossier");
-
- lgTrace("AutoSnapshotDossier abilitato");
- // chiamo stored x creare Snapshot Dossier giornalieri fino alla data...
- callResp = utils.callUrl(urlFixDailyDossier);
- fatto = callResp == "OK";
- lgDebug($"Esecuzione ProcessAutoDossier completata --> esito: {callResp}");
- }
- else
- {
- lgTrace("AutoSnapshotDossier DISABILITATO");
- }
- }
- // loggo se enabled
- if (fatto)
- {
- lgTrace($"Effettuato ProcessAutoDossier, esito positivo | {DateTime.Now:HH.mm.ss}");
- }
- }
-
- ///
- /// Wrapper AutoOdl in modalità Sync
- ///
- public void ProcessAutoOdl()
- {
- try
- {
- Task.Run(async () => await ProcessAutoOdlAsync())
- .GetAwaiter()
- .GetResult();
- }
- catch (Exception ex)
- {
- lgError("ProcessAutoOdl | Crash nel ponte Sync/Async: " + ex.Message);
- }
- }
-
- ///
- /// Verifica e processing x gestione ODL automatica
- ///
- public async Task ProcessAutoOdlAsync()
- {
- bool fatto = false;
- if (IOBConfFull.Odl.AutoChangeOdl)
- {
- lgTrace("ProcessAutoOdlAsync | AutoChangeOdl abilitato");
- // imposto il veto lettura contapezzi a 1 minuto x iniziare...
- dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
- string fullUrl = "";
- DateTime adesso = DateTime.Now;
- DateTime inizioOdl = adesso;
- string rawDataInizio = "";
- if (VetoProcessAutoOdl > adesso)
- {
- lgTrace($"Veto per check autoOdl attivo fino a {VetoProcessAutoOdl} | salto verifica");
- }
- else
- {
- bool callChangeODL = false;
- DateTime dtStart = await currOdlStart();
- switch (IOBConfFull.Odl.ChangeOdlMode)
- {
- case "PZCOUNT_RESET":
- /* verifico se sia "armato" il reset cambio ODL, DEVO essere :
- * - NON in produzione
- * - contapezzi ACT < contapezzi LAST
- * */
- lgTrace("ProcessAutoOdlAsync: 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}");
- }
- break;
-
- case "DAILY":
- // verifico inizio ODL, se è data di oggi NON eseguo...
- if (dtStart.Date < adesso.Date)
- {
- // chiamo stored x creare ODL giornalieri alla data...
- string autoOdlRes = utils.callUrl(urlFixDailyOdl);
- fatto = autoOdlRes == "OK";
- // imposto x prox controllo veto a 10 min
- VetoProcessAutoOdl = adesso.AddMinutes(10);
- }
- else
- {
- // imposto x prox controllo veto a 30 min
- VetoProcessAutoOdl = adesso.AddMinutes(30);
- }
- break;
-
- case "DAILY_CONF_PZ":
- // verifico inizio ODL, se è data di oggi NON eseguo...
- if (dtStart.Date < adesso.Date)
- {
- // imposto x prox controllo veto a 10 min
- VetoProcessAutoOdl = adesso.AddMinutes(10);
- string autoOdlRes = "";
- if (!isMulti)
- {
- // chiamo stored x creare ODL giornalieri alla data + conferma pezzi...
- autoOdlRes = utils.callUrl(urlFixDailyOdlConfPzCount);
- }
- else
- {
- // prendo il + vecchio...
- foreach (var item in IOBConfFull.Device.MultiIobList)
- {
- fullUrl = $@"{urlCommand("fixDailyOdlConfPzCount")}{item}";
- autoOdlRes = await callUrl(fullUrl, false);
- }
- }
- fatto = autoOdlRes == "OK";
- }
- else
- {
- // imposto x prox controllo veto a 30 min
- VetoProcessAutoOdl = adesso.AddMinutes(30);
- }
- break;
-
- case "SIMUL":
- case "TIME":
- // imposto x prox controllo veto a 1 min
- VetoProcessAutoOdl = adesso.AddMinutes(1);
- // controllo parametri validi
- if (IOBConfFull.Odl.OdlDurationHours > 0 && IOBConfFull.Odl.IdleStateMin >= 0)
- {
- // leggo da server inizio ODL... se non multi 1 solo...
- inizioOdl = DateTime.Now;
- rawDataInizio = "";
- if (!isMulti)
- {
- rawDataInizio = await callUrl(urlInizioOdlIob, false);
- DateTime.TryParse(rawDataInizio, out inizioOdl);
- }
- else
- {
- DateTime tmpData = DateTime.Now;
- // prendo il + vecchio...
- foreach (var item in IOBConfFull.Device.MultiIobList)
- {
- fullUrl = $"{urlInizioOdlIob}|{item}";
- rawDataInizio = await callUrl(fullUrl, false);
- DateTime.TryParse(rawDataInizio, out tmpData);
- inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl;
- }
- }
- // verifico se sia scaduto...
- if (inizioOdl.AddHours(IOBConfFull.Odl.OdlDurationHours) < adesso)
- {
- string rawIdle = "";
- int idlePeriod = 0;
- if (!isMulti)
- {
- // controllo SE sono fermo (spento o in manuale) per il
- // periodo minimo richiesto...
- rawIdle = await callUrl(urlIdleTime, false);
- int.TryParse(rawIdle, out idlePeriod);
- }
- else
- {
- int tmpIdle = 0;
- // prendo il + grande...
- foreach (var item in IOBConfFull.Device.MultiIobList)
- {
- fullUrl = $"{urlIdleTime}|{item}";
- rawIdle = await callUrl(fullUrl, false);
- int.TryParse(rawIdle, out tmpIdle);
- idlePeriod = tmpIdle > idlePeriod ? tmpIdle : idlePeriod;
- }
- }
- if (idlePeriod >= IOBConfFull.Odl.IdleStateMin)
- {
- callChangeODL = true;
- }
- }
- }
- // se NON fosse scaduto MA è simulato esegue controllo sul num pezzi da
- // fare, se sfora (RANDOM) > +(50...110)% --> cambia!
- if (!callChangeODL && IOBConfFull.Odl.ChangeOdlMode == "SIMUL")
- {
- var rawCount = await callUrl(urlGetNumPzCurrODL, false);
- if (int.TryParse(rawCount, out var numPzReqOdl))
- {
- int limitQty = (numPzReqOdl * rndGen.Next(150, 210)) / 100;
- callChangeODL = contapezziPLC > limitQty;
- }
- }
- break;
-
- default:
- break;
- }
-
- // vero processing...
- if (callChangeODL)
- {
- lgTrace("Chiamata: ProcessAutoOdlAsync --> forceSplitODL");
- fatto = await forceSplitOdl();
- // metto contapezzi a zero..
- contapezziPLC = 0;
- // aspetto 2 sec per proseguire dopo force split...
- await Task.Delay(2000);
- pzCountResetted = false;
- lgInfo("Esecuzione ProcessAutoOdlAsync completata --> pzCountResetted = false");
- // imposto x prox controllo veto a 15 min
- VetoProcessAutoOdl = adesso.AddMinutes(15);
- }
- }
- }
- else
- {
- lgTrace("ProcessAutoOdlAsync | AutoChangeOdl DISABILITATO");
- }
- // loggo se ok + processing post opzionali
- if (fatto)
- {
- lgInfo($"Effettuato ProcessAutoOdlAsync | mode: {IOBConfFull.Odl.ChangeOdlMode}");
- processAutoOdlExtraStep();
- }
- }
-
- ///
- /// Effettua processing degli allarmi CNC SE disponibili
- ///
- public void processCncAlarms()
- {
- if (utils.CRB("enableAlarms"))
- {
- Dictionary currAlarms = new Dictionary();
- 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)
- {
- // verifico NON sia un ND...
- if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
- {
- // log anomalia...
- lgTrace($"Errore in predisposizione FL Allarmi CNC: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
- }
- else
- {
- sVal = string.Format("[CNC_ALARM]{0}|{1}", item.Key, item.Value);
- // chiamo accodamento...
- bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
- if (sent)
- {
- // traccio valore DynData x analisi
- trackDynData(item.Key, item.Value);
- }
- }
- }
- }
- // accodo ALTRI allarmi NON CNC...
- foreach (var item in currAlarms.Where(X => X.Key != "CNC_ALARM"))
- {
- // verifico NON sia un ND...
- if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
- {
- // log anomalia...
- lgTrace($"Errore in predisposizione FL Allarmi NON CNC: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
- }
- else
- {
- sVal = $"{item.Key} | {item.Value}";
- bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
- if (sent)
- { // traccio valore DynData x analisi
- trackDynData(item.Key, item.Value);
- }
- }
- }
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in processCncAlarms{Environment.NewLine}{exc}");
- }
- }
- }
- }
-
- ///
- /// Effettua processing contapezzi (ed eventualmente alza il bit di contapezzo...)
- ///
- public virtual void processContapezzi()
- { }
-
- ///
- /// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
- /// ogni 5 sec se base timer 10ms, vedere app.config)
- ///
- public virtual void processCustomTaskLF()
- { }
-
- ///
- /// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
- /// ogni 3 sec se base timer 10ms, vedere app.config)
- ///
- public virtual void processCustomTaskMF()
- { }
-
- ///
- /// Task periodici SE disconnesso
- ///
- public virtual void processDisconnectedTask()
- {
- }
-
- ///
- /// Effettua processing del recupero dei valori dinamici:
- /// es: speed (RPM, feedrate) degli assi, valori pressioni, temeprature
- ///
- public void processDynData()
- {
- // FixMe Todo: generalizzare parametri nell'obj?
- bool enableByApp = utils.CRB("enableDynData");
- Dictionary currDynData = new Dictionary();
-
- if (enableByApp || IOBConfFull.FluxLog.EnableDynData)
- {
- // 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();
- bool hasDynDataVal = currDynData.Count > 0;
- if (!hasDynDataVal)
- {
- lgWarn($"processDynData.getDynData: nessun valori DynData ricevuto");
- }
- else
- {
- // verifico DynData siano validi: se tutti uguali NON li considero validi...
- bool isValidSet = checkValidDynData(currDynData);
- // se non valido loggo e NON proseguo..
- if (!isValidSet)
- {
- lgWarn($"processDynData.getDynData: Valori ricevuti NON validi | # dynData{currDynData.Count}");
- }
- else
- {
- var currAlarmData = getAlarmData();
- lgTrace($"currDynData: {currDynData.Count} | currAlarmData: {currAlarmData.Count}");
- // 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") && hasDynDataVal)
- {
- currDynData.Add("DYNDATA", $"{currDynData.Count}xVal");
- }
- // verifico se DynData sia abilitato IN GENERALE
- if (!IOBConfFull.FluxLog.DisDynData)
- {
- try
- {
- string sVal = "";
- // se richiesto send diretto...
- if (IOBConfFull.FluxLog.ForceDynData)
- {
- // per ogni valore del dizionario mostro ed accodo!
- foreach (var item in currDynData)
- {
- // controllo se vietato dynData
- if (IOBConfFull.FluxLog.SendDynDataRec || item.Key != "DYNDATA")
- {
- // verifico NON sia un ND...
- if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
- {
- // log anomalia...
- lgTrace($"Errore in processDynData.01: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
- }
- else
- {
- sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
- // chiamo accodamento...
- bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
- if (sent)
- {
- // traccio valore DynData x analisi
- trackDynData(item.Key, item.Value);
- }
- }
- }
- }
- }
- // altrimenti verifico SE sia cambiato il valore dei DynData...
- else if (hasDynDataVal && (lastDynDataCtrlVal == null || lastDynDataCtrlVal != currDynData["DYNDATA"]))
- {
- // salvo!
- lastDynDataCtrlVal = currDynData["DYNDATA"];
- // per ogni valore del dizionario mostro ed accodo!
- foreach (var item in currDynData)
- {
- // controllo se vietato dynData
- if (IOBConfFull.FluxLog.SendDynDataRec || item.Key != "DYNDATA")
- {
- // verifico NON sia un ND...
- if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
- {
- // log anomalia...
- lgTrace($"Errore in processDynData.02: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
- }
- else
- {
- sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
- // chiamo accodamento...
- bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
- if (sent)
- {
- // traccio valore DynData x analisi
- trackDynData(item.Key, item.Value);
- }
- }
- }
- }
- }
- // salvo array...
- lastDynData = currDynData;
- }
- catch (Exception exc)
- {
- lgError(exc, "Eccezione in processDynData");
- }
- }
- // ora popolo prod data dai dynData...
- foreach (var item in currDynData)
- {
- // se configurato x scrivere valori in WRITE PROD
- if (IOBConfFull.FluxLog.CopyDyn2MemWrite)
- {
- // se presente nelle memorie write/read --> metto in curr data...
- if (memMap != null && memMap.mMapWrite.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
- {
- upsertKey(item.Key, item.Value);
- }
- }
- // verifico area read x i lastProd...
- if (memMap != null && memMap.mMapRead.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
- {
- upsertKeyLP(item.Key, item.Value);
- }
- }
- }
- }
- }
- else
- {
- lgError("Errore connessione mancante x getDynData");
- }
- }
- }
- }
-
- ///
- /// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC...
- ///
- public void processMemoryDiscon()
- {
- // init obj display
- newDisplayData currDispData = new newDisplayData();
- // controllo contatore invio "keepalive"... invio solo a scadenza
- int disconnMaxSec = utils.CRI("disconMaxSec");
- if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > disconnMaxSec)
- {
- // 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;
- lgInfo($"Send 00 | disconMaxSec: {disconnMaxSec}");
- }
- raiseRefresh(currDispData);
- }
-
- ///
- /// Effettua processing mode/status (EDIT/MDI/...)
- ///
- public virtual void processMode()
- { }
-
- ///
- /// Effettua processing ALTRI contatori/parametri (ed invia ad IO)
- ///
- public virtual async Task processOtherCounters()
- {
- await Task.Delay(1);
- }
-
- ///
- /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid)
- ///
- public virtual void processOverride()
- {
- bool enableByApp = utils.CRB("enableOverrides");
- Dictionary currOverride = new Dictionary();
- if (enableByApp || IOBConfFull.FluxLog.EnableOverrides)
- {
- 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)
- {
- // verifico NON sia un ND...
- if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
- {
- // log anomalia...
- lgTrace($"Errore in processOverride: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
- }
- else
- {
- sVal = string.Format("[OVERRIDES]{0}|{1}", item.Key, item.Value);
- // chiamo accodamento...
- bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
- if (sent)
- {
- // traccio valore DynData x analisi
- trackDynData(item.Key, item.Value);
- }
- }
- }
- }
- }
- }
- }
- }
-
- ///
- /// Processa esecuzione task ricevuti
- ///
- ///
- ///
- /// Restituisce elenco task svolti --> per accodamento risposta
- public Dictionary ProcessTask(Dictionary task2exe, string codTav)
- {
- Dictionary taskDone = new Dictionary();
- Dictionary task2Add = new Dictionary();
- // eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)...
- if (!IOBConfFull.Device.DisabExeTask)
- {
- if (task2exe != null && task2exe.Count > 0)
- {
- string logMsg = $"Task2Exe S01: {task2exe.Count} task ricevuti";
- if (!string.IsNullOrEmpty(codTav))
- {
- logMsg += $" | codTav: {codTav}";
- }
- lgInfo(logMsg);
- 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 SE manca...
- if (!task2Add.ContainsKey(linkVal))
- {
- 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, codTav);
- lgInfo($"Task2Exe S02: eseguiti {taskDone.Count} task");
- // loggo tutti i task done...
- foreach (var item in taskDone)
- {
- sendToTaskWatch(item.Key, item.Value, codTav);
- }
- }
- }
- return taskDone;
- }
-
-#if false
- ///
- /// Processa esecuzione task ricevuti
- ///
- ///
- ///
- ///
- public async Task> ProcessTask(Dictionary task2exe, string codTav)
- {
- Dictionary taskDone = new Dictionary();
- Dictionary task2Add = new Dictionary();
- // eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)...
- if (!IOBConfFull.Device.DisabExeTask)
- {
- if (task2exe != null)
- {
- string logMsg = $"Task2Exe S01: {task2exe.Count} task ricevuti";
- if (!string.IsNullOrEmpty(codTav))
- {
- logMsg += $" | codTav: {codTav}";
- }
- lgInfo(logMsg);
- 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 SE manca...
- if (!task2Add.ContainsKey(linkVal))
- {
- 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, codTav);
- lgInfo($"Task2Exe S02: eseguiti {taskDone.Count} task");
- // loggo tutti i task done...
- foreach (var item in taskDone)
- {
- sendToTaskWatch(item.Key, item.Value, codTav);
- }
- // ora chiamo la cancellazione dei task eseguiti...
- foreach (var item in taskDone)
- {
- await remTask2exe(item.Key, item.Value, codTav);
- }
- }
- }
- return taskDone;
- }
-#endif
-
- ///
- /// Classe fittizia in caso di processing task in MsVHF
- ///
- public virtual void processVHF()
- {
- }
-
- ///
- /// Classe fittizia in caso di processing watchdog data
- ///
- public virtual void processWhatchDog()
- {
- }
-
- ///
- /// Effettua rilettura del contapezzi dal server MP/IO
- ///
- /// Forza rilettura da DB tempi ciclo rilevati
- public void pzCntReload(bool forceCountRec, string forceMach = "")
- {
- // se NON disabilitato contapezzi
- if (!IOBConfFull.Device.EnabPzCount)
- {
- lgDebug("pzCntReload disabilitato da EnabPzCount");
- }
- else
- {
- // legge da IO server ULTIMO valore CONTPEZZI al riavvio...
- string currServerCount = "";
- string lastIdxODL = "";
- string calcUrl = "";
- if (!disableOdl)
- {
- var srvAlive = CheckServerAlive();
- if (srvAlive)
- {
- if (isMulti)
- {
- // disabilitato per macchina MULTI, da riportare logica da OpcUa ...
- }
- else
- {
- // leggo PRIMA ODL ....
- calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetCurrODL : urlGetCurrODL.Replace(IOBConfFull.General.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(IOBConfFull.General.CodIOB, forceMach);
- currServerCount = utils.callUrl(calcUrl);
- lgInfo($"Lettura contapezzi da TCiclo registrati dall'url {calcUrl} --> num pz: {currServerCount}");
- }
- else
- {
- // uso il contapezzi dichiarato dall'IOB stesso
- calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCount : urlGetPzCount.Replace(IOBConfFull.General.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;
- lgWarn("Errore server NON pronto in pzCntReload");
- }
- }
- }
- }
-
- ///
- /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
- /// dtEve#flusso#valore#cont Flusso datiValore da salvare
- ///
- public string qEncodeFLog(string flusso, string valore)
- {
- string answ = "";
- // solo se valore !="", su DynData...
- if (flusso != "DYNDATA" || !string.IsNullOrEmpty(valore))
- {
- try
- {
- answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterULog}";
- }
- catch
- { }
- }
- return answ;
- }
-
- ///
- /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
- /// dtEve#flux#valReq#cont DataOra evento
- /// registratoFlusso datiValore da salvare
- ///
- public string qEncodeFLog(DateTime eventDT, string flusso, string valore)
- {
- string answ = "";
- try
- {
- answ = $"{eventDT:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterFLog}";
- }
- catch
- { }
- return answ;
- }
-
- ///
- /// Fornisce il valore di UserLog e valore in formato valido x messa in coda nel formato:
- /// dtEve#flusso#valReq#cont#matrOpr#label#valNum Flusso dati
- /// (RC/RS/DI)Valore da inviare
- /// (valStringMatricola operatoreValore etichetta: causale scarto / tagCodeValore numerico: esitoOk (0/1) / nuo scarti
- ///
- 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;
- }
-
- ///
- /// Effettua lettura dati
- /// Parametri da aggiornare x display in form
- ///
- 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 (!IOBConfFull.Device.DisabStateCh)
- {
- 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);
- }
-
- ///
- /// Effettua lettura semafori principale Parametri da
- /// aggiornare x display in form
- ///
- public virtual void readSemafori(ref newDisplayData currDispData)
- {
- lastReadPLC = DateTime.Now;
- }
-
- ///
- /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati (32bit)
- /// 2021.08.27: filtrati secondo i valori setup start/size RawDataInput
- ///
- 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 BankConf -----------{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
- { }
- }
-
- ///
- /// Metodo generico di reset contapezzi...
- ///
- ///
- public virtual bool resetContapezziPLC(string codTav)
- {
- lgInfo("Generic.resetContapezziPLC");
- return false;
- }
-
- ///
- /// Effettua salvataggio in LUT del valore ricevuto (valori string)
- /// - non serve calcolo medie o altro
- /// - accoda in out val e basta
- ///
- /// Array eventi da popolare
- /// valore da salvare
- /// ID/chiave di riferimento
- ///
- public virtual void saveAlarmString(ref Dictionary outVal, string valore, string chiave)
- {
- DateTime adesso = DateTime.Now;
- //check obj preliminare
- if (outVal == null)
- {
- outVal = new Dictionary();
- }
- // 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;
- }
- }
-
- ///
- /// metodo dummy x salvataggio aree memoria conf x CN
- ///
- /// tipo di DUMP
- public virtual void saveMemDump(dumpType tipo)
- {
- }
-
- ///
- /// Effettua salvataggio in LUT del valore ricevuto (valori numerici)
- ///
- ///
- ///
- ///
- ///
- public virtual void saveValue(ref Dictionary outVal, string chiave, double valore)
- {
- //check obj preliminare
- if (outVal == null)
- {
- outVal = new Dictionary();
- }
- bool scaduto = stackVal_TSVC(chiave, valore, DateTime.Now);
- // recupero VC
- valore = getVal_TSVC(chiave, scaduto);
-
- // 2025.08.05: verifico se il valore SIA configurato come onlyIncr...
- if (IOBConfFull.Memory.mMapRead.ContainsKey(chiave))
- {
- // in quel caso recupero ultimo valore
- var dataRec = IOBConfFull.Memory.mMapRead[chiave];
- // ....e se inferiore uso quello...
- if (dataRec.onlyIncr && valore < LastTSVC[chiave])
- {
- valore = LastTSVC[chiave];
- }
- }
-
- // 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;
- if (scaduto)
- {
- lgTrace($"saveValue: deadBand superata | {chiave} | old: {fluxLogReduceLast[chiave]:F3} | {valore:F3} | DBand: {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)
- {
- /*--------------------------------------------------
- * FIX gestione decimali e formati IT/EN
- * - limite a 3 digit x valore float
- * - culture invariant (
- * - richiede CHECK i vari double/float parser...
- *
- * per decodificare usare ad esempio:
- *
- * bool success = double.TryParse(rawVal, NumberStyles.Any, CultureInfo.InvariantCulture, out cntDouble);
- * */
- outVal.Add(chiave, valore.ToString("F3", CultureInfo.InvariantCulture));
- //outVal.Add(chiave, $"{valore:N3}");
- LastTSVC[chiave] = valore;
- fluxLogReduceLast[chiave] = valore;
- lgDebug($"saveValue: valore accodato | {chiave}: {valore:F3}");
- }
- else
- {
- lgTrace($"saveValue: filtro attivo | {chiave}: {valore:F3}");
- }
- }
-
- ///
- /// Effettua salvataggio in LUT del valore ricevuto (valori string)
- /// - non serve calcolo medie o altro
- /// - accoda in out val e basta
- ///
- /// Array eventi da popolare
- /// ID/chiave di riferimento
- /// valore da salvare
- ///
- public virtual void saveValueString(ref Dictionary outVal, string chiave, string valore)
- {
- DateTime adesso = DateTime.Now;
- //check obj preliminare
- if (outVal == null)
- {
- outVal = new Dictionary();
- }
- 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;
- }
- }
-
- ///
- /// Invio la variazione dello stato allarmi (se avvenuta)
- ///
- /// COD memoria allarmi
- /// Indice memoria allarmi
- /// ultimo stato rilevato
- /// stato corrente
- /// Lista allarmi validi per l'area
- ///
- public bool sendAlarmVariations(string memAddr, int index, uint lastStatus, uint currStatus, List AlarmList)
- {
- bool fatto = false;
- if (lastStatus != currStatus)
- {
- List ActiveAlarmList = new List();
- // 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 Num.{i}");
- //}
- }
- }
- }
-
- string rawData = JsonConvert.SerializeObject(ActiveAlarmList);
- string resp = utils.CallUrlPost(lastUrl, rawData);
- //string resp = utils.callUrlAsync(lastUrl, rawData);
- if (resp != null)
- {
- 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;
- }
-
- ///
- /// Invia una LISTA di valori
- ///
- ///
- ///
- public async Task sendDataBlock(urlType tipoUrl, List 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 (await CheckServerAliveAsync())
- {
- // chiamo URL!
- string answ = await callUrlWithPayloadAsync(lastUrl, payload, doAsync);
-
- // valutare invio REST alternativo...
-#if false
- // invio alternativo nuovo
- if (string.IsNullOrEmpty(answ))
- {
- answ = utils.ExecCallPostPlain(lastUrl, payload);
- }
-#endif
-
- // 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;
- }
-
- ///
- /// Effettua invio a MoonPro del valore richiesto
- ///
- ///
- ///
- /// Valore da trasmettere: es
- /// INPUT: lo status rilevato in HEX
- /// FLog: il valore da trasmettere per il flusso indicato
- ///
- public async Task sendToMoonPro(urlType tipoUrl, string queueVal)
- {
- // controllo NON nullo..
- if (queueVal != null)
- {
- // 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 (await CheckServerAliveAsync())
- {
- // chiamo URL!
- string answ = await 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);
- }
- else
- {
- lgTrace($"Richiesto invio valore nullo, salto");
- }
- }
-
- ///
- /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi...
- ///
- /// Pezzi richiesti
- ///
- public virtual bool setcontapezziPLC(int newPzCount, string codTav)
- {
- return false;
- }
-
- ///
- /// Effettua impostazione del conteggio pezzi richiesti
- ///
- ///
- public virtual bool setPzComm(int pzReq)
- {
- return false;
- }
-
- ///
- /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e
- /// restituisce bool val se SCADUTO periodo controllo
- ///
- /// Nome della VC
- /// Valore (nuovo) delal VC
- ///
- 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)
- {
- // 2026.01.20: solo se ho almeno 3 valori altrimenti rischio zeri...
- answ = TSVC_Data[VCName].dataArray.Count > 2;
- //answ = true;
- }
- lgTrace($"stackVal_TSVC | {VCName} | {VCVal} | scaduta: {answ}");
- }
- return answ;
- }
-
- ///
- /// Avvia l'adapter sulla porta richiesta
- ///
- /// indica se sia richiesto di SVUOTARE le code delle info
- 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
- dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn);
- queueInEnabCurr = false;
- string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}";
- lgInfoStartup(msgVeto);
- lgDebug($"startAdapter completed | veto queueIn impostato per {vetoQueueIn}s fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}");
- }
-
- ///
- /// Ferma l'adapter...
- ///
- ///
- /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato
- /// in automatico)
- ///
- /// indica se sia richiesto di SVUOTARE le code delle info
- public virtual async Task stopAdapter(bool tryRestart, bool forceDequeue)
- {
- // controllo che non ci sia redis queue altrimenti NON forza svuotamento...
- if (forceDequeue && !IOBConfFull.General.EnabRedisQue)
- {
- // 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);
- await 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 listaValori = new List();
- // 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);
- }
- await sendDataBlock(urlType.FLog, listaValori);
- }
- else
- {
- // invio in blocco
- listaValori = QueueFLog.ToList();
- // invio
- await sendDataBlock(urlType.FLog, listaValori);
- // svuoto! NO REDIS
- QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", false, redisMan);
- //QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", IOBConfFull.General.EnabRedisQue, redisMan);
- }
- }
- // HO FINITO invio di FLog...
- }
- else
- {
- string currVal = "";
- while (QueueFLog.Count > 0)
- {
- // INVIO COMUNQUE...!!!
- QueueFLog.TryDequeue(out currVal);
- await sendToMoonPro(urlType.FLog, currVal);
- }
- }
-
- // svuoto coda ULog
- while (QueueULog.Count > 0)
- {
- List listaValori = new List();
- // 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);
- }
- await sendDataBlock(urlType.ULog, listaValori);
- }
- else
- {
- // invio in blocco
- listaValori = QueueULog.ToList();
- // invio
- await sendDataBlock(urlType.ULog, listaValori);
- // svuoto!
- QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueULog", false, redisMan);
- }
- }
- }
- parentForm.displayTaskAndLog("[STOP] Stopping adapter...", true);
- adpTryRestart = false;
-
- parentForm.displayTaskAndLog("[STOP] Stopping adapter - last periodic data read...", true);
-
- // salvo statistiche
- string callKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:CallStats");
- await CallMetricsCollector.SaveToRedisAsync(redisMan.currDb, callKey, false);
-
- // 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;
- }
-
- ///
- /// Processo la coda SignalIN...
- ///
- public async Task svuotaCodaSignInAsync()
- {
- // 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 listaValori = new List();
- // 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);
- }
- await sendDataBlock(urlType.SignIN, listaValori);
- }
- else
- {
- // invio in blocco
- listaValori = QueueIN.ToList();
- // invio
- await sendDataBlock(urlType.SignIN, listaValori);
- // svuoto!
- QueueIN = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueIN", IOBConfFull.General.EnabRedisQue, redisMan);
- }
- }
- else
- {
- // INVIO SINGOLO...!!!
- QueueIN.TryDequeue(out currVal);
- await 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;
- }
- }
- }
- }
-
- ///
- /// Metodo base connessione...
- ///
- public virtual void tryConnect()
- {
- dtAvvioAdp = DateTime.Now;
- queueInEnabCurr = true;
- }
-
- ///
- /// Metodo base disconnessione...
- ///
- public virtual void tryDisconnect()
- {
- queueInEnabCurr = false;
- }
-
- ///
- /// Inserimento/aggiornamento chiavi/valore in currProdData + cache REDIS
- ///
- ///
- ///
- /// True se modificato/inserito, false se INVARIATO
- public bool upsertKey(string chiave, string valore)
- {
- bool done = false;
- if (currProdData.ContainsKey(chiave))
- {
- // se variato inserisco...
- if (currProdData[chiave] != valore)
- {
- currProdData[chiave] = valore;
- done = true;
- }
- }
- else
- {
- currProdData.Add(chiave, valore);
- done = true;
- }
- if (done)
- {
- // salvo in redis...
- redisMan.redSaveHashDict(rKeyCurrProdData, currProdData);
- }
- return done;
- }
-
- ///
- /// Inserimento/aggiornamento chiavi/valore in lastProdData
- ///
- ///
- ///
- /// True se modificato/inserito, false se INVARIATO
- public bool upsertKeyLP(string chiave, string valore)
- {
- bool done = false;
- if (lastProdData.ContainsKey(chiave))
- {
- // se variato inserisco...
- if (lastProdData[chiave] != valore)
- {
- lastProdData[chiave] = valore;
- done = true;
- }
- }
- else
- {
- lastProdData.Add(chiave, valore);
- done = true;
- }
- return done;
- }
-
- ///
- /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi
- ///
- ///
- ///
- public virtual string urlDataBlock(urlType tipoUrl)
- {
- // verifico la parte di link "tipoComando"
- string tipoComando = "";
- switch (tipoUrl)
- {
- case urlType.FLog:
- tipoComando = "flogJson";
- break;
-
- case urlType.SignIN:
- tipoComando = "evListJson";
- break;
-
- case urlType.RawTransf:
- tipoComando = "rawTransfJson";
- break;
-
- case urlType.ULog:
- tipoComando = "ulogJson";
- break;
-
- default:
- break;
- }
- // URL base x input
- string answ = $@"{urlCommandIob(tipoComando)}";
- // se è disabilitato keepalive aggiungo opzione
- if (IOBConfFull.MapoMes.DisabKeepAlive)
- {
- // se c'è già "?" aggiungo con "&" altrimenti
- string sPar = answ.Contains("?") ? "&&" : "?";
- answ += $@"{sPar}disabKA=true";
- }
- return answ;
- }
-
- ///
- /// Fornisce URL di tipo FluxLog
- ///
- /// valore salvato in coda nel formato dtEve#flux#valore#counter
- ///
- public string urlFLog(string queueVal)
- {
- // URL base x input
- string answ = $@"{urlCommandIob("flog")}";
- // decodifica valore!
- string[] valori = qDecodeIN(queueVal);
- // aggiungo flux e valore...
- answ += $@"?flux={valori[1]}&&valore={valori[2]}";
- // aggiondo dataOra evento e corrente + contatore...
- answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[3]}";
- // se è disabilitato keepalive aggiungo opzione
- if (IOBConfFull.MapoMes.DisabKeepAlive)
- {
- ;
- answ += $"&&disabKA=true";
- }
- return answ;
- }
-
- ///
- /// URL per recupero dati ODL alla data...
- ///
- public string urlGetOdlAtDate(DateTime dtRif)
- {
- string answ = $@"{urlCommandIob("getOdlAtDate")}?dateRif={dtRif:yyyyMMdd}";
- return answ;
- }
-
- ///
- /// Fornisce URL INPUT per i parametri richiesti
- ///
- /// valore salvato in coda formato dtEve#valore#counter
- ///
- public string urlInput(string queueVal)
- {
- // URL base x input
- string answ = $@"{urlCommandIob("input")}";
- // decodifica valore!
- string[] valori = qDecodeIN(queueVal);
- // aggiungo macchina e valore...
- answ += $@"?valore={valori[1]}";
- // aggiondo dataOra evento e corrente + contatore...
- answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[2]}";
- return answ;
- }
-
- ///
- /// Fornisce URL di tipo UserLog
- ///
- /// valore salvato in coda nel formato dtEve#flux#valore#counter
- ///
- public string urlULog(string queueVal)
- {
- // URL base x input
- string answ = $@"{urlCommandIob("ulog")}";
- // decodifica valore!
- string[] valori = qDecodeIN(queueVal);
- // aggiungo macchina e valore...
- answ += $@"?flux={valori[1]}&&valore={valori[2]}";
- // aggiondo dataOra evento e corrente + contatore...
- answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[3]}";
- return answ;
- }
-
- #endregion Public Methods
-
- #region Private Methods
-
- ///
- /// Test ping + api al server in modalità Async
- ///
- ///
- ///
- private async Task ExecuteApiCheckWithRetryAsync(int maxRetries)
- {
- var rand = new Random();
- for (int i = 0; i <= maxRetries; i++)
- {
- try
- {
- // Se non è il primo tentativo, resetta i client e aspetta
- if (i > 0)
- {
- if (i == 4) resetWebClients(); // Reset specifico a metà tentativi
- await Task.Delay(rand.Next(150, 500));
- }
-
- string resp = await callUrl(urlAlive, false);
- if (resp == "OK") return true;
- }
- catch (Exception ex)
- {
- if (i == 0) lgError($"Errore API Check: {ex.Message}");
- }
- }
- return false;
- }
-
-#if false
- ///
- /// Processing di una risposta raw di task2exe
- ///
- /// Risposta come string RAW
- /// Cod Tav (opzionale)
- private async Task ProcessResp(string resp, string codTav)
- {
- Dictionary task2exe = new Dictionary();
- Dictionary taskDone = new Dictionary();
- if (!string.IsNullOrEmpty(resp) && resp.Length > 2)
- {
- try
- {
- task2exe = JsonConvert.DeserializeObject>(resp);
- // se ho da fare chiamo esecuzione..
- if (task2exe.Count > 0)
- {
- taskDone = await ProcessTask(task2exe, codTav);
- }
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in ServerGetRequestsAsync.ProcessResp:{Environment.NewLine}{exc}");
- }
- }
- }
-#endif
-
- ///
- /// Update stato server
- ///
- ///
- private void UpdateServerState(bool currentAlive)
- {
- if (MPOnline != currentAlive)
- {
- MPOnline = currentAlive;
- parentForm.commSrvActive = currentAlive ? 1 : 0;
-
- if (currentAlive)
- {
- lgInfo("SERVER ONLINE");
- dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10);
- }
- else
- {
- lgError("SERVER OFFLINE");
- // Veto standard per server offline
- dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20);
- utils.dtVetoSend = dtVetoPing;
- }
- }
- else
- {
- // Se lo stato è invariato (es. era online e resta online), allunghiamo il prossimo controllo
- dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 30);
- }
- }
-
- #endregion Private Methods
- }
-}
\ No newline at end of file
diff --git a/IOB-WIN-FORM/Iob/Generic.Protected.cs b/IOB-WIN-FORM/Iob/Generic.cs
similarity index 58%
rename from IOB-WIN-FORM/Iob/Generic.Protected.cs
rename to IOB-WIN-FORM/Iob/Generic.cs
index 27835e2d..5b06e56a 100644
--- a/IOB-WIN-FORM/Iob/Generic.Protected.cs
+++ b/IOB-WIN-FORM/Iob/Generic.cs
@@ -1,7 +1,11 @@
using EgwProxy.Ftp;
using IOB_UT_NEXT;
+using IOB_UT_NEXT.Config;
+using IOB_WIN_FORM.Iob.Services;
using MapoSDK;
+using MathNet.Numerics.Statistics;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using NLog;
using NLog.Targets;
using System;
@@ -16,12 +20,12 @@ using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.AccessControl;
+using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
-using System.Xml.Serialization;
using YamlDotNet.Core.Tokens;
using static IOB_UT_NEXT.BaseAlarmConf;
using static IOB_UT_NEXT.CustomObj;
@@ -32,6 +36,3883 @@ namespace IOB_WIN_FORM.Iob
{
public partial class Generic : BaseObj
{
+ #region Public Fields
+
+ public int numPzReqOdl = 0;
+
+ #endregion Public Fields
+
+ #region Public Constructors
+
+ ///
+ /// inizializzo l'oggetto sulla form SULLA BASE DEL FILE DI CONFIGURAZIONE letto
+ ///
+ /// Form chiamante
+ /// Configurazione (v 4.x)
+ public Generic(AdapterForm caller, IobConfTree IobConfNew)
+ {
+ // salvo il form chiamante
+ parentForm = caller;
+ if (IobConfNew != null)
+ {
+ // salvo configurazione...
+ IOBConfFull = IobConfNew;
+
+ // init oggetto redis...
+ redisMan = new RedisIobCache(IobConfNew.MapoMes.IpAddr, IobConfNew.General.FilenameIOB, $"{IobConfNew.General.IobType}", IobConfNew.General.MinDeltaSec);
+
+ // init code
+ SetupQueue();
+
+ // initi oggetto TCMan
+ tcMan = new TCMan(IobConfNew.TCDataConf.Lambda, IobConfNew.TCDataConf.MaxDelayFactor, IobConfNew.TCDataConf.MaxIncrPz);
+
+ lastConnectTry = DateTime.Now;
+
+ lgInfo("Avvio preliminare AdapterGeneric");
+ lastLogStartup = DateTime.Now;
+
+ // setup currProdData & last prod data
+ currProdData = redisMan.redGetHashDict(rKeyCurrProdData);
+ // i last li avvio a VUOTI... x evitare errore mancata riscrittura SIMEC che ha memorie NON ritentive
+ lastProdData = new Dictionary();
+ //lastProdData = new Dictionary(currProdData);
+ // 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);
+ string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}";
+ lgInfoStartup(msgVeto);
+ lgInfo(msgVeto);
+
+ // invio info IOB
+ SendM2IOB();
+ // invio altri dati accessori...
+ SendMachineConf();
+ if (resetAlarmOnStart)
+ {
+ SendAlarmReset();
+ }
+ // concluso!
+ lgInfoStartup("Istanziata classe preliminare IOBGeneric");
+ }
+ else
+ {
+ lgError("Error: IobCOnf is null!");
+ }
+ }
+
+ #endregion Public Constructors
+
+ #region Public Events
+
+ ///
+ /// Evento Iob ha subito un refresh
+ ///
+ public event EventHandler eh_refreshed;
+
+ #endregion Public Events
+
+ #region Public Properties
+
+ ///
+ /// Salva verifica stato connessione OK con macchina (PLC/CNC)
+ ///
+ ///
+ public virtual bool connectionOk
+ {
+ get
+ {
+ return _connOk || DemoIn;
+ }
+ set
+ {
+ _connOk = value;
+ }
+ }
+
+ ///
+ /// Contapezzi attuale
+ ///
+ public Int32 contapezziIOB
+ {
+ get
+ {
+ return tcMan.pzCountIOB;
+ }
+ set
+ {
+ tcMan.pzCountIOB = value;
+ }
+ }
+
+ ///
+ /// Ultima lettura variabile contapezzi da CNC
+ ///
+ public Int32 contapezziPLC
+ {
+ get
+ {
+ return tcMan.pzCountPLC;
+ }
+ set
+ {
+ tcMan.pzCountPLC = value;
+ }
+ }
+
+ ///
+ /// Contatore x invio dati FluxLog
+ ///
+ public int counterFLog { get; set; }
+
+ ///
+ /// Contatore x invio dati RawTransf
+ ///
+ public int counterRawTransf { get; set; }
+
+ ///
+ /// Contatore x invio dati SignalIN
+ ///
+ public int counterSigIN { get; set; }
+
+ ///
+ /// Contatore x invio dati UserLog
+ ///
+ public int counterULog { get; set; }
+
+ ///
+ /// nome Programma corrente
+ ///
+ public string currPrgName { get; set; }
+
+ ///
+ /// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA...
+ ///
+ public bool DemoIn
+ {
+ get => IOBConfFull.General.IobType == tipoAdapter.SIMULA;
+ }
+
+ ///
+ /// Dizionario contapezzi Macchina (valori da impianto) x macchine multi tavola/pallet
+ ///
+ public Dictionary DictPzCountImp { get; set; } = new Dictionary();
+
+ ///
+ /// Dizionario contapezzi MES (valori salvati su server) x macchine multi tavola/pallet
+ ///
+ public Dictionary DictPzCountMes { get; set; } = new Dictionary();
+
+ ///
+ /// Indica se la chiamata WDST dit racking watchdog sia disabilitata dall'invio nel FluxLog
+ ///
+ public bool disableWdst { get; set; } = false;
+
+ ///
+ /// Indica lo stato Online/Offline della IOB
+ ///
+ public bool IobOnline
+ {
+ get
+ {
+ return utils.IOB_Online;
+ }
+ set
+ {
+ utils.IOB_Online = value;
+ }
+ }
+
+ ///
+ /// Verifica se sia macchina multi = DoppioPallet da CONF
+ ///
+ public bool isMulti
+ {
+ get => IOBConfFull.Device.IsMulti;
+ }
+
+ ///
+ /// Log verboso da configurazione (SOLO CHIAVE "verbose"...)
+ ///
+ public bool isVerboseLog { get; set; } = utils.CRB("verbose");
+
+ ///
+ /// Ultimo Alarm letto
+ ///
+ public string lastAlarm { get; set; }
+
+ ///
+ /// Ultimo ARRAY DynData letto
+ ///
+ public Dictionary lastDynData { get; set; } = new Dictionary();
+
+ ///
+ /// Ultimo DynData (sunto) letto
+ ///
+ public string lastDynDataCtrlVal { get; set; }
+
+ ///
+ /// Ultimo Override set letto
+ ///
+ public string lastOverrideFS { get; set; }
+
+ ///
+ /// Ultimo Override set letto
+ ///
+ public string lastOverrideRapid { get; set; }
+
+ ///
+ /// Ultimo programma letto
+ ///
+ public string lastPrgName { get; set; }
+
+ ///
+ /// Ultimo SysInfo letto
+ ///
+ public string lastSysInfo { get; set; }
+
+ ///
+ /// Ultimo URL
+ ///
+ public string lastUrl { get; set; }
+
+ ///
+ /// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per
+ /// 100... 200% --> 200)
+ ///
+ public int maxPzDeltaPerc
+ {
+ get => IOBConfFull.Counters.MaxIncrPzCountPerc;
+ }
+
+ ///
+ /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...)
+ ///
+ public bool periodicLog
+ {
+ get
+ {
+ bool answ = false;
+ answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut"));
+ if (answ)
+ {
+ lastPeriodicLog = DateTime.Now;
+ }
+
+ return answ;
+ }
+ }
+
+ ///
+ /// Valore medio del TC rilevato x verifica derive sul delta variazione contapezzi
+ ///
+ public double plcAvgTc
+ {
+ get
+ {
+ double answ = tcMan.avgTC > 0 ? tcMan.avgTC : 1;
+ return answ;
+ }
+ }
+
+ ///
+ /// DataOra dell'ultima lettura variabile contapezzi da CNC
+ ///
+ public DateTime plcLastPzRead
+ {
+ get
+ {
+ return tcMan.lastObservedData;
+ }
+ }
+
+ ///
+ /// Determina se il contapezzi plc sia valido (lo è se data avvio adapter è prima di ultimo dato registrato in contapezzi)
+ ///
+ public bool plcPzCountValid
+ {
+ get => tcMan.lastObservedData > dtAvvioAdp;
+ }
+
+ ///
+ /// Abilitazione coda segnali ingresso
+ ///
+ public bool queueInEnabCurr
+ {
+ get => qInEnabCurr;
+ set
+ {
+ qInEnabCurr = value;
+ lgInfo($"SET queueInEnabCurr: {value} | {DateTime.Now:HHmmss}");
+ }
+ }
+
+ ///
+ /// Finestra dei byte da mostrare di default x il RawDataInput
+ ///
+ public int RawDataInputSize { get; set; } = 8;
+
+ ///
+ /// Indice di partenza della memoria RawData Input da mostrare
+ ///
+ public int RawDataInputStart { get; set; } = 0;
+
+ ///
+ /// URL per segnalazione reboot...
+ ///
+ public string urlReboot
+ {
+ get => $@"{urlCommandIobFile("sendReboot")}?mac={GetMACAddress()}";
+ }
+
+ ///
+ /// URL per salvataggio dati conf YAML completi IOB...
+ ///
+ public string urlSaveConfYaml
+ {
+ get => $@"{urlCommandIobFile("saveConfYaml")}";
+ }
+
+ ///
+ /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN)
+ ///
+ 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
+
+ ///
+ /// Esegue conversione in un dizionario di tipo string/string serializzando e deserializzando
+ ///
+ ///
+ ///
+ public static Dictionary ConvertToStringDict(Dictionary input)
+ {
+ return DataSerializer.ToDictionary(input);
+ }
+
+ ///
+ /// Accumula in coda i valori ALARM e logga...
+ ///
+ /// VALORE RAW (x display)
+ /// VALORE già processato con qEncodeFLog(...)
+ 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;
+ }
+ }
+
+ ///
+ /// Accumula in coda i valori Flux Log e logga. Restituisce true se inviato (x track su REDIS)
+ ///
+ /// Nome del flusso da inviare (per verifica veto invio)
+ /// VALORE RAW (x display)
+ /// VALORE già processato con qEncodeFLog(...)
+ public bool accodaFLog(string codFlux, string val, string encodedVal)
+ {
+ bool enabled = false;
+ // verifico se il parametro sia abilitato...
+ if (IOBConfFull.Memory != null && IOBConfFull.Memory.mMapRead != null && IOBConfFull.Memory.mMapRead.ContainsKey(codFlux))
+ {
+ enabled = IOBConfFull.Memory.mMapRead[codFlux].sendEnabled;
+ if (!enabled)
+ {
+ lgDebug($"accodaFLog : invio bloccato per conf parametro sendEnabled | codFlux: {codFlux}");
+ }
+ else
+ {
+ // processo SOLO SE ho dei valori non nulli x encodedVal...
+ if (!string.IsNullOrEmpty(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($"[QUEUE-FLOG] {encodedVal}");
+ counterFLog++;
+ if (counterFLog > 9999)
+ {
+ counterFLog = 0;
+ }
+ }
+ else
+ {
+ lgTrace($"ERRORE in [QUEUE-FLOG] : encodedVal vuoto | val: {val}");
+ }
+ }
+ }
+ else
+ {
+ // registro nel dizionario dei valori FluxLog filtrati
+ SaveFiltFluxLog(codFlux);
+ // verifico se registrare elenco valori filtrati in log...
+ FiltFluxLogCheckSave(100);
+ }
+ return enabled;
+ }
+
+ ///
+ /// Accoda (visualizzando in cima allo stack) la nuova stringa di output per area OTHER DATA
+ ///
+ ///
+ public void accodaOtherData(string newLine)
+ {
+ // inserisco in cima allo stack
+ parentForm.WriteTextSafe(newLine);
+ }
+
+ ///
+ /// Accumula in coda i valori RawData + log
+ ///
+ ///
+ ///
+ 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 = DataSerializer.Serialize(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;
+ }
+ }
+
+ ///
+ /// Accumula in coda i valori Signal IN e logga...
+ /// Parametri da aggiornare x display in form
+ ///
+ public void accodaSigIN(ref newDisplayData currDispData)
+ {
+ DateTime adesso = DateTime.Now;
+ lgDebug($"accodaSigIN 01 | qEncodeIN: {qEncodeIN}");
+ // mostro dati variati letti...
+ displayInData(ref currDispData);
+ // verifico veto a invio status macchina
+ if (IOBConfFull.Device.DisabSigIn)
+ {
+ lgTrace($"Filtrato accodamento valore da conf IOB | DisabSigIn | [QUEUE-IN] {qEncodeIN}");
+ }
+ else
+ {
+ // verifico non sia in veto invio iniziale...
+ if (queueInEnabCurr)
+ {
+ // --> accodo (valore già formattato)!
+ QueueIN.Enqueue(qEncodeIN);
+ // loggo!
+ lgDebug($"[QUEUE-IN] {qEncodeIN}");
+ }
+ else
+ {
+ lgTrace($"[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;
+ }
+ }
+ lgDebug($"accodaSigIN 02 | nReadFilt: {nReadFilt} | counterSigIN: {counterSigIN} | QueueIN len: {QueueIN.Count}");
+ }
+
+ ///
+ /// Accumula in coda i valori USER LOG e logga...
+ ///
+ /// VALORE RAW (x display)
+ /// VALORE già processato con qEncodeULog(...)
+ 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;
+ }
+ }
+
+ ///
+ /// Verifica se la IOB sia ENABLED (da server o Demo)
+ ///
+ public async Task CheckIobEnabled()
+ {
+ // 1. Controllo Veto (Sincrono)
+ if (dtVetoCheckIOB >= DateTime.Now)
+ return IobOnline;
+
+ bool currentAnsw = false;
+
+ if (DemoOut)
+ {
+ currentAnsw = (QueueIN.Count + QueueFLog.Count >= nMaxSend);
+ }
+ else
+ {
+ currentAnsw = await ExecuteIobCheckWithRetry(maxRetries: 5);
+ }
+
+ // 3. Gestione Stato e Logica UI
+ UpdateIobState(currentAnsw);
+
+ return currentAnsw;
+ }
+
+ ///
+ /// Verifica veto invio per una chiave specifica in Redis
+ ///
+ ///
+ ///
+ public bool CheckSendVeto(string keyReq, TimeSpan vetoReq)
+ {
+ DateTime adesso = DateTime.Now;
+ DateTime lastSend = LastSendGet(keyReq);
+ bool sendEnab = lastSend.Add(vetoReq) <= adesso;
+ return sendEnab;
+ }
+
+ ///
+ /// Verifica se il server sia ALIVE (tramite PING)
+ ///
+ public bool CheckServerAlive() // Rimosso async, restituisce bool
+ {
+ // 1. Controllo Veto (Sincrono)
+ if (dtVetoPing >= DateTime.Now) return MPOnline;
+ if (DemoOut) return true;
+
+ // 2. Eseguo la parte asincrona forzando l'attesa
+ // Usiamo Task.Run per far girare il codice asincrono su un thread del pool
+ // evitando deadlock con il thread della UI
+ bool isAlive = Task.Run(async () => await ExecuteApiCheckWithRetryAsync(maxRetries: 7))
+ .GetAwaiter()
+ .GetResult();
+
+ // 3. Gestione Stato (Sincrono)
+ UpdateServerState(isAlive);
+
+ return isAlive;
+ }
+
+ ///
+ /// Verifica se il server sia ALIVE (tramite PING)
+ ///
+ public async Task CheckServerAliveAsync()
+ {
+ // 1. Controllo Veto (Sincrono)
+ if (dtVetoPing >= DateTime.Now) return MPOnline;
+ if (DemoOut) return true;
+
+ bool isAlive = await ExecuteApiCheckWithRetryAsync(maxRetries: 7);
+
+ // 3. Gestione Stato (Sincrono)
+ UpdateServerState(isAlive);
+
+ return isAlive;
+ }
+
+ ///
+ /// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile
+ ///
+ public void checkVetoQueueIn()
+ {
+ queueInEnabCurr = dtVetoQueueIN < DateTime.Now;
+ }
+
+ ///
+ /// Update visualizzaizone BIT in ingresso Parametri da
+ /// aggiornare x display in form
+ ///
+ 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:X}";
+ currDispData.newSignalData = newString;
+ }
+ }
+
+ ///
+ /// Mostra cosa ha/avrebbe inviato
+ ///
+ ///
+ public void displayOtherData(string newData)
+ {
+ // mostro update...
+ accodaOtherData(newData);
+ }
+
+ ///
+ /// Effettua i task di comunicazione IN/OUT con la macchina
+ ///
+ ///
+ ///
+ public void doMachineTask(gatherCycle ciclo)
+ {
+ // init obj display
+ newDisplayData currDispData = new newDisplayData();
+ // 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
+ {
+ if (ciclo == gatherCycle.VHF)
+ {
+ //processVHF();
+ }
+ // processing dati memoria (lettura, filtraggio, enqueque)
+ else if (ciclo == gatherCycle.HF)
+ {
+ processWhatchDog();
+ //Thread.Sleep(5);
+ processAllMemory();
+ }
+ else if (ciclo == gatherCycle.MF)
+ {
+ processMode();
+ //Thread.Sleep(5);
+ ExecServerRequests();
+ //Thread.Sleep(5);
+ processCustomTaskMF();
+ //Thread.Sleep(5);
+ processOverride();
+ //Thread.Sleep(5);
+ processContapezzi();
+ //Thread.Sleep(5);
+ processCncAlarms();
+ //Thread.Sleep(5);
+ processDynData();
+ //Thread.Sleep(5);
+ processMem2Write();
+ }
+ else if (ciclo == gatherCycle.LF)
+ {
+ processCustomTaskLF();
+ //Thread.Sleep(5);
+ processOtherCounters().GetAwaiter().GetResult(); ;
+ //Thread.Sleep(5);
+ processProgram();
+ }
+ else if (ciclo == gatherCycle.VLF)
+ {
+ //processRecipeSyncArch();
+ // recupero dati SETUP (sysinfo) e li invio/mostro se variati...
+ processSysInfo();
+ if (enableSlowData)
+ {
+ //Thread.Sleep(5);
+ processSlowDataRead();
+ }
+ }
+
+ }
+ catch (Exception exc)
+ {
+ // segnalo eccezione e indico disconnesso...
+ lgError($"Exception doMachineTask | {ciclo} | fermo adapter{Environment.NewLine}{exc}");
+ parentForm.fermaAdapter(true, false, true);
+ }
+ // tolgo flag running
+ adpCommAct = false;
+ }
+ else
+ {
+ if (periodicLog)
+ {
+ lgDebug("ADP not running...");
+ }
+ }
+ }
+ else
+ {
+ // log ADP running
+ lgInfo("Non eseguo chiamata: ADP ancora in running");
+ // se è bloccato da oltre maxSec lo sblocco...
+ if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec"))
+ {
+ // tolgo flag running
+ adpCommAct = false;
+ adpStartRun = DateTime.Now;
+ }
+ }
+ }
+ else
+ {
+ // provo a riconnettere SE abilitato tryRestart...
+ if (adpTryRestart && !connectionOk)
+ {
+ // controllo se sia scaduto periodi di veto al tryConnect...
+ int waitRecMSec = utils.CRI("waitRecMSec");
+ // cerco se ci sia un valore in ovverride x il singolo IOB...
+ if (IOBConfFull.General.WaitRecMsec > 0)
+ {
+ waitRecMSec = IOBConfFull.General.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);
+ }
+
+ ///
+ /// Effettua i task di comunicazione IN/OUT con il server MAPO
+ ///
+ ///
+ ///
+ public async Task doServerTaskAsync(gatherCycle ciclo)
+ {
+ // init obj display
+ newDisplayData currDispData = new newDisplayData();
+ // IN OGNI CASO a prima di tutto EFFETTUO GESTIONE INVII dati da code!!!
+ try
+ {
+ await TrySendValuesAsync();
+ }
+ catch (Exception exc)
+ {
+ lgError(exc, "Errore in gestione svuotamento/invio preliminare code memoria");
+ currDispData.semOut = Semaforo.SR;
+ }
+ // controllo connessione/connettività verso PLC...
+ if (connectionOk)
+ {
+ // 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)
+ {
+ // recupera ed invia risposte al server
+ await ServerPutRespAsync();
+ }
+ else if (ciclo == gatherCycle.MF)
+ {
+ // recupero elenco richieste
+ await ServerGetRequestsAsync();
+ }
+ else if (ciclo == gatherCycle.LF)
+ {
+
+ // verifico se devo gestire cambio ODL in modo automatico
+ await ProcessAutoOdlAsync();
+ // verifico se devo gestire auto generazione dossier quotidiana
+ ProcessAutoDossier();
+ // effettua gestione import file se configurato...
+ await ProcessFileImportAsync();
+ // effettua process ritorno ricette
+ await ProcessRecipeFileRetAsync();
+ }
+ else if (ciclo == gatherCycle.VLF)
+ {
+ if (utils.CRB("enableContapezzi"))
+ {
+ // rilettura contapezzi da server...
+ lgTrace("Ciclo MsVLF: pzCntReload(true)");
+ if (!isMulti)
+ {
+ pzCntReload(true);
+ }
+
+ // refresh associazione Macchina - IOB
+ await SendM2IobAsync();
+ // invio altri dati accessori...
+ await SendMachineConfAsync();
+ }
+ // checkLogDir x shrink!
+ checkShrinkDir();
+ // eventuale log!
+ if (utils.CRB("recTime"))
+ {
+ try
+ {
+ logTimeResults();
+ }
+ catch
+ { }
+ }
+ processRecipeSyncArch();
+ }
+
+ // 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($"Exception | doServerTaskAsync | {ciclo}{Environment.NewLine}{exc}");
+ }
+ }
+ 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
+ await ProcessAutoOdlAsync();
+ // verifico se devo gestire auto generazione dossier quotidiana
+ ProcessAutoDossier();
+ // effettua gestione import file se configurato...
+ await ProcessFileImportAsync();
+ // effettua process ritorno ricette
+ await ProcessRecipeFileRetAsync();
+ }
+ 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");
+ // // cerco se ci sia un valore in ovverride x il singolo IOB...
+ // if (IOBConfFull.General.WaitRecMsec > 0)
+ // {
+ // waitRecMSec = IOBConfFull.General.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);
+ }
+
+ public void ExecServerRequests()
+ {
+ // verifica se ci siano richieste da eseguire
+ if (QueueSrvReq.Count > 0)
+ {
+ if (MPOnline)
+ {
+ if (IobOnline)
+ {
+ // prendo elenco
+ List listaValori = QueueSrvReq.ToList();
+
+ foreach (var rawJob in listaValori)
+ {
+ // deserializzo...
+ JobTaskData jobTaskReq = JsonConvert.DeserializeObject(rawJob);
+ // processo!
+ var reqDict = JobTaskData.TaskDict(jobTaskReq.RawData);
+ if (reqDict.Count > 0)
+ {
+ var taskDone = ProcessTask(JobTaskData.TaskDict(jobTaskReq.RawData), jobTaskReq.CodTav);
+ // accodo task eseguiti...
+ string serVal = JsonConvert.SerializeObject(taskDone);
+ accodaServResp(jobTaskReq.CodTav, serVal);
+ }
+ }
+
+ // svuoto!
+ QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Esecuzione dei task richiesti e pulizia coda richieste eseguite
+ ///
+ /// Elenco task da eseguire
+ /// Codice TAV (per macchine multi pallet) - opzionale
+ public virtual Dictionary executeTasks(Dictionary task2exe, string codTav)
+ {
+ string logMsg = $"Generic: call executeTasks | {task2exe.Count} task";
+ if (!string.IsNullOrEmpty(codTav))
+ {
+ logMsg += $" | codTav: {codTav}";
+ }
+ lgInfo(logMsg);
+ // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
+ Dictionary taskDone = new Dictionary();
+ 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);
+ string iKey = item.Key;
+ // se è DP aggiungo in chiave il valore della TAV richiesta... ad es setComm --> setComm#TAV_1
+ if (!string.IsNullOrEmpty(codTav))
+ {
+ iKey += $"#{codTav}";
+ }
+ // 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(iKey))
+ {
+ dataConf currMem = memMap.mMapWrite[iKey];
+ string addr = currMem.memAddr;
+ taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
+ // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
+ memMap.mMapWrite[iKey].value = item.Value;
+ }
+ else
+ {
+ taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
+ }
+ }
+ else
+ {
+ taskVal = $"NO BankConf found, SET task: {iKey} --> {item.Value}";
+ }
+ // salvo in currProd..
+ upsertKey(iKey, item.Value);
+
+ break;
+
+ case taskType.endProd:
+ // reset contapezzi inizio setup
+ if (IOBConfFull.Counters.ResetOnProdEnd)
+ {
+ lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC");
+ taskOk = resetContapezziPLC(codTav);
+ }
+ break;
+
+ case taskType.forceResetPzCount:
+ // recupero dati da memMap...
+ if (memMap != null && memMap.mMapWrite != null)
+ {
+ lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC | memMap");
+ if (memMap.mMapWrite.ContainsKey(iKey))
+ {
+ dataConf currMem = memMap.mMapWrite[iKey];
+ string addr = currMem.memAddr;
+ taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
+ // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
+ memMap.mMapWrite[iKey].value = item.Value;
+ taskOk = true;
+ }
+ else
+ {
+ taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
+ }
+ // imposto reset gestione pzcount da parametri
+ vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup);
+ dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
+ }
+ else
+ {
+ // reset contapezzi senza memMap
+ lgInfo($"Generic.executeTasks {tName} | resetContapezziPLC | NO memMap");
+ taskOk = resetContapezziPLC(codTav);
+ taskVal = taskOk ? "forceResetPzCount | RESET PZ COUNT OK" : "forceResetPzCount | PZ RESET DISABLED | NO EXEC";
+ }
+ lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");
+ break;
+
+ case taskType.forceSetPzCount:
+ // recupero dati da memMap...
+ if (memMap != null && memMap.mMapWrite != null)
+ {
+ if (memMap.mMapWrite.ContainsKey(iKey))
+ {
+ dataConf currMem = memMap.mMapWrite[iKey];
+ string addr = currMem.memAddr;
+ taskVal = $"SET task: {iKey} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
+ // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
+ memMap.mMapWrite[iKey].value = item.Value;
+ }
+ else
+ {
+ taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}";
+ }
+ }
+ else
+ {
+ taskVal = $"NO BankConf found, SET task: {iKey} --> {item.Value}";
+ }
+
+ lgInfo($"Chiamata forceSetPzCount: taskVal: {taskVal}");
+ 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(iKey))
+ {
+ dataConf currMem = memMap.mMapWrite[iKey];
+ string addr = currMem.memAddr;
+ taskVal = $"SET task: {iKey} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
+ // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
+ memMap.mMapWrite[iKey].value = newVal;
+ }
+ else
+ {
+ taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})";
+ }
+ }
+ else
+ {
+ taskVal = $"NO BankConf found, SET task: {iKey} --> {newVal} ({item.Value})";
+ }
+
+ // salvo in currProd..
+ upsertKey(iKey, 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(iKey))
+ {
+ dataConf currMem = memMap.mMapWrite[iKey];
+ string addr = currMem.memAddr;
+ taskVal = $"SET task: {iKey} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
+ // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
+ memMap.mMapWrite[iKey].value = newVal;
+ }
+ else
+ {
+ taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})";
+ }
+ }
+ else
+ {
+ taskVal = $"NO BankConf found, SET task: {iKey} --> {newVal} ({item.Value})";
+ }
+
+ // salvo in currProd..
+ upsertKey(iKey, newVal);
+ break;
+
+ case taskType.startSetup:
+ // reset contapezzi inizio setup
+ if (IOBConfFull.Counters.ResetOnSetupStart)
+ {
+ lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC");
+ taskOk = resetContapezziPLC(codTav);
+ vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup);
+ dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
+ }
+ taskVal = taskOk ? "startSetup | RESET: SETUP START" : "startSetup | PZ RESET DISABLED | NO EXEC";
+ lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");
+ break;
+
+ case taskType.stopSetup:
+ // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO
+ if (IOBConfFull.Counters.ResetOnSetupStop)
+ {
+ lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC");
+ taskOk = resetContapezziPLC(codTav);
+ }
+ taskVal = taskOk ? "stopSetup | RESET: SETUP END" : "stopSetup | 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(iKey, item.Value);
+ taskVal = okProc ? $"OK ProcessOtherInfoAsync | {iKey} | {item.Value}" : $"ERROR ProcessOtherInfoAsync | {iKey} | {item.Value}";
+ break;
+
+ default:
+ taskVal = $"taskReq: {tName} | key: {iKey} | 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 BankConf) | 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;
+ }
+
+ ///
+ /// Cerca parametri opzionali in modalità "like" del nome
+ ///
+ ///
+ ///
+ public Dictionary findOptPar(string keyStartSearch = "")
+ {
+ Dictionary answ = new Dictionary();
+ // controllo SE keySearch !=""
+ if (!string.IsNullOrWhiteSpace(keyStartSearch))
+ {
+ if (IOBConfFull.OptPar.Count > 0)
+ {
+ // ciclo su tutti e cerco occorrenze che INIZINO...
+ foreach (var item in IOBConfFull.OptPar)
+ {
+ if (item.Key.StartsWith(keyStartSearch))
+ {
+ answ.Add(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ return answ;
+ }
+
+ ///
+ /// forza reset ODL
+ ///
+ public void forceResetOdl()
+ {
+ lgInfo("Registrato richiesta forzatura reset ODL");
+ pzCountResetted = true;
+ }
+
+ ///
+ /// Effettua chiamata x split ODL
+ ///
+ ///
+ public async Task 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 = "";
+ try
+ {
+ /***************************************************
+ * Descrizione procedura (OK X SIMULATORI... da provare x DP come OpcUaSiemensSW)
+ *
+ * - chiamata su MP/IO
+ * - verifica che su DB sia abilitato AUTO ODL
+ * - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW
+ * - viene duplicato e chiuso ODL corrente
+ * - viene fatto partire ODL nuovo ADESSO
+ * - num pezzi come ODL precedente (o da media 3 ODL precedenti)
+ * - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL)
+ * - reset contapezzi PLC locale...
+ *
+ *
+ *
+ * DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE
+ * - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI)
+ * - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!)
+ * - viene 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
+ *
+ * ***************************************************/
+
+ // se normale splitto!
+ if (!isMulti)
+ {
+ // invio chiamata URL x reset ODL su macchina
+ rawSplit = await utils.callUrlAsync(urlForceSplit);
+ fatto = (rawSplit != "KO") ? true : false;
+ }
+ // se multi gestisco il bit delle tavole...
+ else
+ {
+ foreach (string item in IOBConfFull.Device.MultiIobList)
+ {
+ // invio chiamata URL x reset ODL su macchina, ATTENZIONE scriviamo
+ // | al posto di "#" che in URL sarebbe filtrato...
+ fullUrl = $"{urlForceSplit}&multi={item}";
+ rawSplit = await utils.callUrlAsync(fullUrl);
+ 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;
+ }
+
+ ///
+ /// Processing degli allarmi (se presenti e gestiti)
+ ///
+ ///
+ public virtual Dictionary getAlarmData()
+ {
+ Dictionary outVal = new Dictionary();
+ // 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;
+ }
+
+ ///
+ /// Effettua conversione da valore bitmap ai valori configurati x allarme
+ ///
+ /// Configurazione banco allarmi
+ /// BitState allarmi
+ ///
+ public string getAlarmState(BaseAlarmConf memConf, byte[] bState)
+ {
+ string valTransl = "";
+ List descAttivi = new List();
+ 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;
+ }
+
+ ///
+ /// Effettua conversione tra gli allarmi attivi secondo configurazione banco allarmi
+ ///
+ /// Configurazione banco allarmi
+ /// num allarmi attivi
+ ///
+ public virtual string getAlarmState(BaseAlarmConf memConf, int numAlarms)
+ {
+ string valTransl = "";
+ List descAttivi = new List();
+
+ // FAKE!!!: dovrebbe cercare in aree memoria x ogni valore configurato, vedere
+ // implementazione specifica es OpcUa
+
+ // combino string
+ valTransl = string.Join(",", descAttivi);
+ return valTransl;
+ }
+
+ ///
+ /// Effettua conversione da valore bitmap ai valori configurati
+ ///
+ ///
+ ///
+ ///
+ public string getBitmapState(dataConfTSVC memConf, byte[] bState)
+ {
+ string valTransl = "";
+ List descAttivi = new List();
+ 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;
+ }
+
+ ///
+ /// Recupera eventuali allarmi CNC...
+ ///
+ public virtual Dictionary getCncAlarms()
+ {
+ Dictionary outVal = new Dictionary();
+ return outVal;
+ }
+
+ ///
+ /// Restituisce info DINAMICHE
+ ///
+ ///
+ public virtual Dictionary getDynData()
+ {
+ Dictionary outVal = new Dictionary();
+ return outVal;
+ }
+
+ ///
+ /// Cerca se esiste il parametro opzionale nei KVP (JSON) e lo restituisce
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Cerca se esiste il parametro opzionale e lo restituisce
+ ///
+ ///
+ ///
+ public string getOptPar(string key)
+ {
+ return IOBConfFull.OptParGet(key);
+ }
+
+ ///
+ /// Cerca se esiste un link tra aree di memoria in write e lo restituisce
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Restituisce info OVERRIDES
+ ///
+ ///
+ public virtual Dictionary getOverrides()
+ {
+ Dictionary outVal = new Dictionary();
+ return outVal;
+ }
+
+ ///
+ /// Restituisce programma in esecuzione
+ ///
+ public virtual string getPrgName()
+ {
+ return "";
+ }
+
+ ///
+ /// Restituisce info sistema
+ ///
+ ///
+ public virtual Dictionary getSysInfo()
+ {
+ Dictionary outVal = new Dictionary();
+ return outVal;
+ }
+
+ ///
+ /// Recupera la VC x TS, svuotando lista e resettando periodo partenza
+ ///
+ /// Nome della VC
+ /// Reimposta e resetta array VC
+ ///
+ 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();
+ TSVC_Data[VCName].DTStart = DateTime.Now;
+ }
+ }
+ return answ;
+ }
+
+ ///
+ /// Recupera la VC x TS, svuotando lista e resettando periodo partenza
+ ///
+ /// Nome della VC
+ /// Reimposta e resetta array VC
+ ///
+ 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();
+ TSVC_Data[VCName].DTStart = DateTime.Now;
+ }
+ }
+ return answ;
+ }
+
+ ///
+ /// Area init asincrono (fare override async!o)
+ ///
+ ///
+ public virtual Task InitializeAsync()
+ {
+ // da usare per implementare logiche di init specifiche
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Restituisce un payload in formato json della lista di valori ricevuta
+ ///
+ /// Tipo di URL (eventi / FLog)
+ /// elenco di valori da coda string salvata
+ ///
+ public string jsonPayload(urlType tipoUrl, List 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();
+ // inizio processando ogni valore
+ foreach (var item in elencoValori)
+ {
+ if (item != null)
+ {
+ 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();
+ // 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();
+
+ // 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}]";
+
+ //hasVeto = "{" + $"\"rawTransfData\":[{rawResult}]" + "}";
+
+ break;
+
+ case urlType.ULog:
+ int numVal = 0;
+ int matrOp = 0;
+ ulogData currUlData = new ulogData();
+ ulogJsonPayload fullUlObj = new ulogJsonPayload();
+ fullUlObj.fluxData = new List();
+ // 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;
+ }
+
+ ///
+ /// Restitusice valore ultimo invio di una chiave, se non esistesse fornisce valore di 1 gg prima e lo salva...
+ ///
+ ///
+ ///
+ public DateTime LastSendGet(string keyReq)
+ {
+ DateTime lastSend = DateTime.Now.AddDays(-1);
+ string lastSendKey = GetStatusField("LastSend");
+ string rawVal = redisMan.redGetHashField(lastSendKey, keyReq);
+ if (!string.IsNullOrEmpty(rawVal))
+ {
+ lastSend = DataSerializer.Deserialize(rawVal);
+ }
+ else
+ {
+ rawVal = DataSerializer.Serialize(lastSend);
+ KeyValuePair[] hashFields = new KeyValuePair[1];
+ hashFields[0] = new KeyValuePair(keyReq, rawVal);
+ redisMan.redSaveHash(lastSendKey, hashFields);
+ }
+ return lastSend;
+ }
+
+ ///
+ /// Imposta valore datetime x ultimo invio di una chiamata in REDIS HashSet
+ ///
+ ///
+ ///
+ public bool LastSendSet(string keyReq, DateTime dtRif)
+ {
+ string lastSendKey = GetStatusField("LastSend");
+ string rawVal = DataSerializer.Serialize(dtRif);
+ KeyValuePair[] hashFields = new KeyValuePair[1];
+ hashFields[0] = new KeyValuePair(keyReq, rawVal);
+ bool fatto = redisMan.redSaveHash(lastSendKey, hashFields);
+ return fatto;
+ }
+
+
+ ///
+ /// Effettua un trim della stringa al numero max di linee da mostrare a video
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// riporta il log di tutti i dati di results temporali registrati
+ ///
+ 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 == IOBConfFull.General.FilenameIOB)
+ {
+ 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, IOBConfFull.General.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, IOBConfFull.General.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));
+ }
+ }
+
+ ///
+ /// Processa un monitoredItem, e ritorna boolean SE richiede invio (cambio o scadenza)
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Verifica e processing x gestione Dossier quotidiani automatica
+ ///
+ public void ProcessAutoDossier()
+ {
+ bool fatto = false;
+ string callResp = "";
+ if (IOBConfFull.FluxLog.AutoSnapshotDossier)
+ {
+ DateTime adesso = DateTime.Now;
+ if (adesso > dtVetoAutoDossier)
+ {
+ dtVetoAutoDossier = adesso.AddMinutes(30);
+ lgTrace("Richiesta ProcessAutoDossier");
+
+ lgTrace("AutoSnapshotDossier abilitato");
+ // chiamo stored x creare Snapshot Dossier giornalieri fino alla data...
+ callResp = utils.callUrl(urlFixDailyDossier);
+ fatto = callResp == "OK";
+ lgDebug($"Esecuzione ProcessAutoDossier completata --> esito: {callResp}");
+ }
+ else
+ {
+ lgTrace("AutoSnapshotDossier DISABILITATO");
+ }
+ }
+ // loggo se enabled
+ if (fatto)
+ {
+ lgTrace($"Effettuato ProcessAutoDossier, esito positivo | {DateTime.Now:HH.mm.ss}");
+ }
+ }
+
+ ///
+ /// Wrapper AutoOdl in modalità Sync
+ ///
+ public void ProcessAutoOdl()
+ {
+ try
+ {
+ Task.Run(async () => await ProcessAutoOdlAsync())
+ .GetAwaiter()
+ .GetResult();
+ }
+ catch (Exception ex)
+ {
+ lgError("ProcessAutoOdl | Crash nel ponte Sync/Async: " + ex.Message);
+ }
+ }
+
+ ///
+ /// Verifica e processing x gestione ODL automatica
+ ///
+ public async Task ProcessAutoOdlAsync()
+ {
+ bool fatto = false;
+ if (IOBConfFull.Odl.AutoChangeOdl)
+ {
+ lgTrace("ProcessAutoOdlAsync | AutoChangeOdl abilitato");
+ // imposto il veto lettura contapezzi a 1 minuto x iniziare...
+ dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
+ string fullUrl = "";
+ DateTime adesso = DateTime.Now;
+ DateTime inizioOdl = adesso;
+ string rawDataInizio = "";
+ if (VetoProcessAutoOdl > adesso)
+ {
+ lgTrace($"Veto per check autoOdl attivo fino a {VetoProcessAutoOdl} | salto verifica");
+ }
+ else
+ {
+ bool callChangeODL = false;
+ DateTime dtStart = await currOdlStart();
+ switch (IOBConfFull.Odl.ChangeOdlMode)
+ {
+ case "PZCOUNT_RESET":
+ /* verifico se sia "armato" il reset cambio ODL, DEVO essere :
+ * - NON in produzione
+ * - contapezzi ACT < contapezzi LAST
+ * */
+ lgTrace("ProcessAutoOdlAsync: 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}");
+ }
+ break;
+
+ case "DAILY":
+ // verifico inizio ODL, se è data di oggi NON eseguo...
+ if (dtStart.Date < adesso.Date)
+ {
+ // chiamo stored x creare ODL giornalieri alla data...
+ string autoOdlRes = utils.callUrl(urlFixDailyOdl);
+ fatto = autoOdlRes == "OK";
+ // imposto x prox controllo veto a 10 min
+ VetoProcessAutoOdl = adesso.AddMinutes(10);
+ }
+ else
+ {
+ // imposto x prox controllo veto a 30 min
+ VetoProcessAutoOdl = adesso.AddMinutes(30);
+ }
+ break;
+
+ case "DAILY_CONF_PZ":
+ // verifico inizio ODL, se è data di oggi NON eseguo...
+ if (dtStart.Date < adesso.Date)
+ {
+ // imposto x prox controllo veto a 10 min
+ VetoProcessAutoOdl = adesso.AddMinutes(10);
+ string autoOdlRes = "";
+ if (!isMulti)
+ {
+ // chiamo stored x creare ODL giornalieri alla data + conferma pezzi...
+ autoOdlRes = utils.callUrl(urlFixDailyOdlConfPzCount);
+ }
+ else
+ {
+ // prendo il + vecchio...
+ foreach (var item in IOBConfFull.Device.MultiIobList)
+ {
+ fullUrl = $@"{urlCommand("fixDailyOdlConfPzCount")}{item}";
+ autoOdlRes = await utils.callUrlAsync(fullUrl);
+ }
+ }
+ fatto = autoOdlRes == "OK";
+ }
+ else
+ {
+ // imposto x prox controllo veto a 30 min
+ VetoProcessAutoOdl = adesso.AddMinutes(30);
+ }
+ break;
+
+ case "SIMUL":
+ case "TIME":
+ // imposto x prox controllo veto a 1 min
+ VetoProcessAutoOdl = adesso.AddMinutes(1);
+ // controllo parametri validi
+ if (IOBConfFull.Odl.OdlDurationHours > 0 && IOBConfFull.Odl.IdleStateMin >= 0)
+ {
+ // leggo da server inizio ODL... se non multi 1 solo...
+ inizioOdl = DateTime.Now;
+ rawDataInizio = "";
+ if (!isMulti)
+ {
+ rawDataInizio = await utils.callUrlAsync(urlInizioOdlIob);
+ DateTime.TryParse(rawDataInizio, out inizioOdl);
+ }
+ else
+ {
+ DateTime tmpData = DateTime.Now;
+ // prendo il + vecchio...
+ foreach (var item in IOBConfFull.Device.MultiIobList)
+ {
+ fullUrl = $"{urlInizioOdlIob}|{item}";
+ rawDataInizio = await utils.callUrlAsync(fullUrl);
+ DateTime.TryParse(rawDataInizio, out tmpData);
+ inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl;
+ }
+ }
+ // verifico se sia scaduto...
+ if (inizioOdl.AddHours(IOBConfFull.Odl.OdlDurationHours) < adesso)
+ {
+ string rawIdle = "";
+ int idlePeriod = 0;
+ if (!isMulti)
+ {
+ // controllo SE sono fermo (spento o in manuale) per il
+ // periodo minimo richiesto...
+ rawIdle = await utils.callUrlAsync(urlIdleTime);
+ int.TryParse(rawIdle, out idlePeriod);
+ }
+ else
+ {
+ int tmpIdle = 0;
+ // prendo il + grande...
+ foreach (var item in IOBConfFull.Device.MultiIobList)
+ {
+ fullUrl = $"{urlIdleTime}|{item}";
+ rawIdle = await utils.callUrlAsync(fullUrl);
+ int.TryParse(rawIdle, out tmpIdle);
+ idlePeriod = tmpIdle > idlePeriod ? tmpIdle : idlePeriod;
+ }
+ }
+ if (idlePeriod >= IOBConfFull.Odl.IdleStateMin)
+ {
+ callChangeODL = true;
+ }
+ }
+ }
+ // se NON fosse scaduto MA è simulato esegue controllo sul num pezzi da
+ // fare, se sfora (RANDOM) > +(50...110)% --> cambia!
+ if (!callChangeODL && IOBConfFull.Odl.ChangeOdlMode == "SIMUL")
+ {
+ var rawCount = await utils.callUrlAsync(urlGetNumPzCurrODL);
+ if (int.TryParse(rawCount, out var numPzReqOdl))
+ {
+ int limitQty = (numPzReqOdl * rndGen.Next(150, 210)) / 100;
+ callChangeODL = contapezziPLC > limitQty;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // vero processing...
+ if (callChangeODL)
+ {
+ lgTrace("Chiamata: ProcessAutoOdlAsync --> forceSplitODL");
+ fatto = await forceSplitOdl();
+ // metto contapezzi a zero..
+ contapezziPLC = 0;
+ // aspetto 2 sec per proseguire dopo force split...
+ await Task.Delay(2000);
+ pzCountResetted = false;
+ lgInfo("Esecuzione ProcessAutoOdlAsync completata --> pzCountResetted = false");
+ // imposto x prox controllo veto a 15 min
+ VetoProcessAutoOdl = adesso.AddMinutes(15);
+ }
+ }
+ }
+ else
+ {
+ lgTrace("ProcessAutoOdlAsync | AutoChangeOdl DISABILITATO");
+ }
+ // loggo se ok + processing post opzionali
+ if (fatto)
+ {
+ lgInfo($"Effettuato ProcessAutoOdlAsync | mode: {IOBConfFull.Odl.ChangeOdlMode}");
+ processAutoOdlExtraStep();
+ }
+ }
+
+ ///
+ /// Effettua processing degli allarmi CNC SE disponibili
+ ///
+ public void processCncAlarms()
+ {
+ if (utils.CRB("enableAlarms"))
+ {
+ Dictionary currAlarms = new Dictionary();
+ 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)
+ {
+ // verifico NON sia un ND...
+ if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
+ {
+ // log anomalia...
+ lgTrace($"Errore in predisposizione FL Allarmi CNC: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
+ }
+ else
+ {
+ sVal = string.Format("[CNC_ALARM]{0}|{1}", item.Key, item.Value);
+ // chiamo accodamento...
+ bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
+ if (sent)
+ {
+ // traccio valore DynData x analisi
+ trackDynData(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ // accodo ALTRI allarmi NON CNC...
+ foreach (var item in currAlarms.Where(X => X.Key != "CNC_ALARM"))
+ {
+ // verifico NON sia un ND...
+ if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
+ {
+ // log anomalia...
+ lgTrace($"Errore in predisposizione FL Allarmi NON CNC: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
+ }
+ else
+ {
+ sVal = $"{item.Key} | {item.Value}";
+ bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
+ if (sent)
+ { // traccio valore DynData x analisi
+ trackDynData(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in processCncAlarms{Environment.NewLine}{exc}");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Effettua processing contapezzi (ed eventualmente alza il bit di contapezzo...)
+ ///
+ public virtual void processContapezzi()
+ { }
+
+ ///
+ /// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
+ /// ogni 5 sec se base timer 10ms, vedere app.config)
+ ///
+ public virtual void processCustomTaskLF()
+ { }
+
+ ///
+ /// Effettua processing CUSTOM x l'IOB corrente (ed invia ad IO) (task svolto tipicamente
+ /// ogni 3 sec se base timer 10ms, vedere app.config)
+ ///
+ public virtual void processCustomTaskMF()
+ { }
+
+ ///
+ /// Task periodici SE disconnesso
+ ///
+ public virtual void processDisconnectedTask()
+ {
+ }
+
+ ///
+ /// Effettua processing del recupero dei valori dinamici:
+ /// es: speed (RPM, feedrate) degli assi, valori pressioni, temeprature
+ ///
+ public void processDynData()
+ {
+ // FixMe Todo: generalizzare parametri nell'obj?
+ bool enableByApp = utils.CRB("enableDynData");
+ Dictionary currDynData = new Dictionary();
+
+ if (enableByApp || IOBConfFull.FluxLog.EnableDynData)
+ {
+ // 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();
+ bool hasDynDataVal = currDynData.Count > 0;
+ if (!hasDynDataVal)
+ {
+ lgWarn($"processDynData.getDynData: nessun valori DynData ricevuto");
+ }
+ else
+ {
+ // verifico DynData siano validi: se tutti uguali NON li considero validi...
+ bool isValidSet = checkValidDynData(currDynData);
+ // se non valido loggo e NON proseguo..
+ if (!isValidSet)
+ {
+ lgWarn($"processDynData.getDynData: Valori ricevuti NON validi | # dynData{currDynData.Count}");
+ }
+ else
+ {
+ var currAlarmData = getAlarmData();
+ lgTrace($"currDynData: {currDynData.Count} | currAlarmData: {currAlarmData.Count}");
+ // 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") && hasDynDataVal)
+ {
+ currDynData.Add("DYNDATA", $"{currDynData.Count}xVal");
+ }
+ // verifico se DynData sia abilitato IN GENERALE
+ if (!IOBConfFull.FluxLog.DisDynData)
+ {
+ try
+ {
+ string sVal = "";
+ // se richiesto send diretto...
+ if (IOBConfFull.FluxLog.ForceDynData)
+ {
+ // per ogni valore del dizionario mostro ed accodo!
+ foreach (var item in currDynData)
+ {
+ // controllo se vietato dynData
+ if (IOBConfFull.FluxLog.SendDynDataRec || item.Key != "DYNDATA")
+ {
+ // verifico NON sia un ND...
+ if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
+ {
+ // log anomalia...
+ lgTrace($"Errore in processDynData.01: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
+ }
+ else
+ {
+ sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
+ // chiamo accodamento...
+ bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
+ if (sent)
+ {
+ // traccio valore DynData x analisi
+ trackDynData(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ }
+ // altrimenti verifico SE sia cambiato il valore dei DynData...
+ else if (hasDynDataVal && (lastDynDataCtrlVal == null || lastDynDataCtrlVal != currDynData["DYNDATA"]))
+ {
+ // salvo!
+ lastDynDataCtrlVal = currDynData["DYNDATA"];
+ // per ogni valore del dizionario mostro ed accodo!
+ foreach (var item in currDynData)
+ {
+ // controllo se vietato dynData
+ if (IOBConfFull.FluxLog.SendDynDataRec || item.Key != "DYNDATA")
+ {
+ // verifico NON sia un ND...
+ if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
+ {
+ // log anomalia...
+ lgTrace($"Errore in processDynData.02: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
+ }
+ else
+ {
+ sVal = string.Format("[DYNDATA]{0}|{1}", item.Key, item.Value);
+ // chiamo accodamento...
+ bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
+ if (sent)
+ {
+ // traccio valore DynData x analisi
+ trackDynData(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ }
+ // salvo array...
+ lastDynData = currDynData;
+ }
+ catch (Exception exc)
+ {
+ lgError(exc, "Eccezione in processDynData");
+ }
+ }
+ // ora popolo prod data dai dynData...
+ foreach (var item in currDynData)
+ {
+ // se configurato x scrivere valori in WRITE PROD
+ if (IOBConfFull.FluxLog.CopyDyn2MemWrite)
+ {
+ // se presente nelle memorie write/read --> metto in curr data...
+ if (memMap != null && memMap.mMapWrite.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
+ {
+ upsertKey(item.Key, item.Value);
+ }
+ }
+ // verifico area read x i lastProd...
+ if (memMap != null && memMap.mMapRead.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
+ {
+ upsertKeyLP(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ lgError("Errore connessione mancante x getDynData");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Processa gestione memoria x invio dati QUANDO IOB è disconnesso da CNC...
+ ///
+ public void processMemoryDiscon()
+ {
+ // init obj display
+ newDisplayData currDispData = new newDisplayData();
+ // controllo contatore invio "keepalive"... invio solo a scadenza
+ int disconnMaxSec = utils.CRI("disconMaxSec");
+ if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > disconnMaxSec)
+ {
+ // 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;
+ lgInfo($"Send 00 | disconMaxSec: {disconnMaxSec}");
+ }
+ raiseRefresh(currDispData);
+ }
+
+ ///
+ /// Effettua processing mode/status (EDIT/MDI/...)
+ ///
+ public virtual void processMode()
+ { }
+
+ ///
+ /// Effettua processing ALTRI contatori/parametri (ed invia ad IO)
+ ///
+ public virtual async Task processOtherCounters()
+ {
+ await Task.Delay(1);
+ }
+
+ ///
+ /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid)
+ ///
+ public virtual void processOverride()
+ {
+ bool enableByApp = utils.CRB("enableOverrides");
+ Dictionary currOverride = new Dictionary();
+ if (enableByApp || IOBConfFull.FluxLog.EnableOverrides)
+ {
+ 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)
+ {
+ // verifico NON sia un ND...
+ if (item.Key == "ND" || string.IsNullOrEmpty(item.Key))
+ {
+ // log anomalia...
+ lgTrace($"Errore in processOverride: rawJob.key risulta ND! | rawJob.key: {item.Key} | rawJob.Value: {item.Value}");
+ }
+ else
+ {
+ sVal = string.Format("[OVERRIDES]{0}|{1}", item.Key, item.Value);
+ // chiamo accodamento...
+ bool sent = accodaFLog(item.Key, sVal, qEncodeFLog(item.Key, item.Value));
+ if (sent)
+ {
+ // traccio valore DynData x analisi
+ trackDynData(item.Key, item.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Processa esecuzione task ricevuti
+ ///
+ ///
+ ///
+ /// Restituisce elenco task svolti --> per accodamento risposta
+ public Dictionary ProcessTask(Dictionary task2exe, string codTav)
+ {
+ Dictionary taskDone = new Dictionary();
+ Dictionary task2Add = new Dictionary();
+ // eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)...
+ if (!IOBConfFull.Device.DisabExeTask)
+ {
+ if (task2exe != null && task2exe.Count > 0)
+ {
+ string logMsg = $"Task2Exe S01: {task2exe.Count} task ricevuti";
+ if (!string.IsNullOrEmpty(codTav))
+ {
+ logMsg += $" | codTav: {codTav}";
+ }
+ lgInfo(logMsg);
+ 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 SE manca...
+ if (!task2Add.ContainsKey(linkVal))
+ {
+ 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, codTav);
+ lgInfo($"Task2Exe S02: eseguiti {taskDone.Count} task");
+ // loggo tutti i task done...
+ foreach (var item in taskDone)
+ {
+ sendToTaskWatch(item.Key, item.Value, codTav);
+ }
+ }
+ }
+ return taskDone;
+ }
+
+ ///
+ /// Classe fittizia in caso di processing task in MsVHF
+ ///
+ public virtual void processVHF()
+ {
+ }
+
+ ///
+ /// Classe fittizia in caso di processing watchdog data
+ ///
+ public virtual void processWhatchDog()
+ {
+ }
+
+ ///
+ /// Effettua rilettura del contapezzi dal server MP/IO
+ ///
+ /// Forza rilettura da DB tempi ciclo rilevati
+ public void pzCntReload(bool forceCountRec, string forceMach = "")
+ {
+ // se NON disabilitato contapezzi
+ if (!IOBConfFull.Device.EnabPzCount)
+ {
+ lgDebug("pzCntReload disabilitato da EnabPzCount");
+ }
+ else
+ {
+ // legge da IO server ULTIMO valore CONTPEZZI al riavvio...
+ string currServerCount = "";
+ string lastIdxODL = "";
+ string calcUrl = "";
+ if (!disableOdl)
+ {
+ var srvAlive = CheckServerAlive();
+ if (srvAlive)
+ {
+ if (isMulti)
+ {
+ // disabilitato per macchina MULTI, da riportare logica da OpcUa ...
+ }
+ else
+ {
+ // leggo PRIMA ODL ....
+ calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetCurrODL : urlGetCurrODL.Replace(IOBConfFull.General.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(IOBConfFull.General.CodIOB, forceMach);
+ currServerCount = utils.callUrl(calcUrl);
+ lgInfo($"Lettura contapezzi da TCiclo registrati dall'url {calcUrl} --> num pz: {currServerCount}");
+ }
+ else
+ {
+ // uso il contapezzi dichiarato dall'IOB stesso
+ calcUrl = string.IsNullOrEmpty(forceMach) ? urlGetPzCount : urlGetPzCount.Replace(IOBConfFull.General.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;
+ lgWarn("Errore server NON pronto in pzCntReload");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
+ /// dtEve#flusso#valore#cont Flusso datiValore da salvare
+ ///
+ public string qEncodeFLog(string flusso, string valore)
+ {
+ string answ = "";
+ // solo se valore !="", su DynData...
+ if (flusso != "DYNDATA" || !string.IsNullOrEmpty(valore))
+ {
+ try
+ {
+ answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterULog}";
+ }
+ catch
+ { }
+ }
+ return answ;
+ }
+
+ ///
+ /// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
+ /// dtEve#flux#valReq#cont DataOra evento
+ /// registratoFlusso datiValore da salvare
+ ///
+ public string qEncodeFLog(DateTime eventDT, string flusso, string valore)
+ {
+ string answ = "";
+ try
+ {
+ answ = $"{eventDT:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterFLog}";
+ }
+ catch
+ { }
+ return answ;
+ }
+
+ ///
+ /// Fornisce il valore di UserLog e valore in formato valido x messa in coda nel formato:
+ /// dtEve#flusso#valReq#cont#matrOpr#label#valNum Flusso dati
+ /// (RC/RS/DI)Valore da inviare
+ /// (valStringMatricola operatoreValore etichetta: causale scarto / tagCodeValore numerico: esitoOk (0/1) / nuo scarti
+ ///
+ 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;
+ }
+
+ ///
+ /// Effettua lettura dati
+ /// Parametri da aggiornare x display in form
+ ///
+ 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 (!IOBConfFull.Device.DisabStateCh)
+ {
+ 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);
+ }
+
+ ///
+ /// Effettua lettura semafori principale Parametri da
+ /// aggiornare x display in form
+ ///
+ public virtual void readSemafori(ref newDisplayData currDispData)
+ {
+ lastReadPLC = DateTime.Now;
+ }
+
+ ///
+ /// Aggiunge ai dati da inviare alla parentform i valori di RawInput rilevati (32bit)
+ /// 2021.08.27: filtrati secondo i valori setup start/size RawDataInput
+ ///
+ 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 BankConf -----------{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
+ { }
+ }
+
+ ///
+ /// Metodo generico di reset contapezzi...
+ ///
+ ///
+ public virtual bool resetContapezziPLC(string codTav)
+ {
+ lgInfo("Generic.resetContapezziPLC");
+ return false;
+ }
+
+ ///
+ /// Effettua salvataggio in LUT del valore ricevuto (valori string)
+ /// - non serve calcolo medie o altro
+ /// - accoda in out val e basta
+ ///
+ /// Array eventi da popolare
+ /// valore da salvare
+ /// ID/chiave di riferimento
+ ///
+ public virtual void saveAlarmString(ref Dictionary outVal, string valore, string chiave)
+ {
+ DateTime adesso = DateTime.Now;
+ //check obj preliminare
+ if (outVal == null)
+ {
+ outVal = new Dictionary();
+ }
+ // 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;
+ }
+ }
+
+ ///
+ /// metodo dummy x salvataggio aree memoria conf x CN
+ ///
+ /// tipo di DUMP
+ public virtual void saveMemDump(dumpType tipo)
+ {
+ }
+
+ ///
+ /// Effettua salvataggio in LUT del valore ricevuto (valori numerici)
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual void saveValue(ref Dictionary outVal, string chiave, double valore)
+ {
+ //check obj preliminare
+ if (outVal == null)
+ {
+ outVal = new Dictionary();
+ }
+ bool scaduto = stackVal_TSVC(chiave, valore, DateTime.Now);
+ // recupero VC
+ valore = getVal_TSVC(chiave, scaduto);
+
+ // 2025.08.05: verifico se il valore SIA configurato come onlyIncr...
+ if (IOBConfFull.Memory.mMapRead.ContainsKey(chiave))
+ {
+ // in quel caso recupero ultimo valore
+ var dataRec = IOBConfFull.Memory.mMapRead[chiave];
+ // ....e se inferiore uso quello...
+ if (dataRec.onlyIncr && valore < LastTSVC[chiave])
+ {
+ valore = LastTSVC[chiave];
+ }
+ }
+
+ // 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;
+ if (scaduto)
+ {
+ lgTrace($"saveValue: deadBand superata | {chiave} | old: {fluxLogReduceLast[chiave]:F3} | {valore:F3} | DBand: {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)
+ {
+ /*--------------------------------------------------
+ * FIX gestione decimali e formati IT/EN
+ * - limite a 3 digit x valore float
+ * - culture invariant (
+ * - richiede CHECK i vari double/float parser...
+ *
+ * per decodificare usare ad esempio:
+ *
+ * bool success = double.TryParse(rawVal, NumberStyles.Any, CultureInfo.InvariantCulture, out cntDouble);
+ * */
+ outVal.Add(chiave, valore.ToString("F3", CultureInfo.InvariantCulture));
+ //outVal.Add(chiave, $"{valore:N3}");
+ LastTSVC[chiave] = valore;
+ fluxLogReduceLast[chiave] = valore;
+ lgDebug($"saveValue: valore accodato | {chiave}: {valore:F3}");
+ }
+ else
+ {
+ lgTrace($"saveValue: filtro attivo | {chiave}: {valore:F3}");
+ }
+ }
+
+ ///
+ /// Effettua salvataggio in LUT del valore ricevuto (valori string)
+ /// - non serve calcolo medie o altro
+ /// - accoda in out val e basta
+ ///
+ /// Array eventi da popolare
+ /// ID/chiave di riferimento
+ /// valore da salvare
+ ///
+ public virtual void saveValueString(ref Dictionary outVal, string chiave, string valore)
+ {
+ DateTime adesso = DateTime.Now;
+ //check obj preliminare
+ if (outVal == null)
+ {
+ outVal = new Dictionary();
+ }
+ 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;
+ }
+ }
+
+ ///
+ /// Invio la variazione dello stato allarmi (se avvenuta)
+ ///
+ /// COD memoria allarmi
+ /// Indice memoria allarmi
+ /// ultimo stato rilevato
+ /// stato corrente
+ /// Lista allarmi validi per l'area
+ ///
+ public bool sendAlarmVariations(string memAddr, int index, uint lastStatus, uint currStatus, List AlarmList)
+ {
+ bool fatto = false;
+ if (lastStatus != currStatus)
+ {
+ List ActiveAlarmList = new List();
+ // 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 Num.{i}");
+ //}
+ }
+ }
+ }
+
+ string rawData = JsonConvert.SerializeObject(ActiveAlarmList);
+ string resp = utils.CallUrlPost(lastUrl, rawData);
+ //string resp = utils.callUrlAsync(lastUrl, rawData);
+ if (resp != null)
+ {
+ 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;
+ }
+
+ ///
+ /// Invia una LISTA di valori
+ ///
+ ///
+ ///
+ public async Task sendDataBlock(urlType tipoUrl, List 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 (await CheckServerAliveAsync())
+ {
+ // chiamo URL!
+ string answ = await utils.callUrlAsync(lastUrl, payload);
+
+ // 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;
+ }
+
+ ///
+ /// Effettua invio a MoonPro del valore richiesto
+ ///
+ ///
+ ///
+ /// Valore da trasmettere: es
+ /// INPUT: lo status rilevato in HEX
+ /// FLog: il valore da trasmettere per il flusso indicato
+ ///
+ public async Task sendToMoonPro(urlType tipoUrl, string queueVal)
+ {
+ // controllo NON nullo..
+ if (queueVal != null)
+ {
+ // 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 (await CheckServerAliveAsync())
+ {
+ // chiamo URL!
+ string answ = await utils.callUrlAsync(lastUrl);
+ // 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);
+ }
+ else
+ {
+ lgTrace($"Richiesto invio valore nullo, salto");
+ }
+ }
+
+ ///
+ /// Metodo generico di IMPOSTAZIONE FORZATA del contapezzi...
+ ///
+ /// Pezzi richiesti
+ ///
+ public virtual bool setcontapezziPLC(int newPzCount, string codTav)
+ {
+ return false;
+ }
+
+ ///
+ /// Effettua impostazione del conteggio pezzi richiesti
+ ///
+ ///
+ public virtual bool setPzComm(int pzReq)
+ {
+ return false;
+ }
+
+ ///
+ /// Processing di Variabili Campionarie x TimeSeries, accoda valori x la VC (se esiste) e
+ /// restituisce bool val se SCADUTO periodo controllo
+ ///
+ /// Nome della VC
+ /// Valore (nuovo) delal VC
+ ///
+ 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)
+ {
+ // 2026.01.20: solo se ho almeno 3 valori altrimenti rischio zeri...
+ answ = TSVC_Data[VCName].dataArray.Count > 2;
+ //answ = true;
+ }
+ lgTrace($"stackVal_TSVC | {VCName} | {VCVal} | scaduta: {answ}");
+ }
+ return answ;
+ }
+
+ ///
+ /// Avvia l'adapter sulla porta richiesta
+ ///
+ /// indica se sia richiesto di SVUOTARE le code delle info
+ 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
+ dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn);
+ queueInEnabCurr = false;
+ string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}";
+ lgInfoStartup(msgVeto);
+ lgDebug($"startAdapter completed | veto queueIn impostato per {vetoQueueIn}s fino alle {dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}");
+ }
+
+ ///
+ /// Ferma l'adapter...
+ ///
+ ///
+ /// indica se si debba tentare di riavviare l'adapter (con caduta connessione viene fermato
+ /// in automatico)
+ ///
+ /// indica se sia richiesto di SVUOTARE le code delle info
+ public virtual async Task stopAdapter(bool tryRestart, bool forceDequeue)
+ {
+ // controllo che non ci sia redis queue altrimenti NON forza svuotamento...
+ if (forceDequeue && !IOBConfFull.General.EnabRedisQue)
+ {
+ // 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);
+ await 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 listaValori = new List();
+ // 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);
+ }
+ await sendDataBlock(urlType.FLog, listaValori);
+ }
+ else
+ {
+ // invio in blocco
+ listaValori = QueueFLog.ToList();
+ // invio
+ await sendDataBlock(urlType.FLog, listaValori);
+ // svuoto! NO REDIS
+ QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", false, redisMan);
+ //QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", IOBConfFull.General.EnabRedisQue, redisMan);
+ }
+ }
+ // HO FINITO invio di FLog...
+ }
+ else
+ {
+ string currVal = "";
+ while (QueueFLog.Count > 0)
+ {
+ // INVIO COMUNQUE...!!!
+ QueueFLog.TryDequeue(out currVal);
+ await sendToMoonPro(urlType.FLog, currVal);
+ }
+ }
+
+ // svuoto coda ULog
+ while (QueueULog.Count > 0)
+ {
+ List listaValori = new List();
+ // 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);
+ }
+ await sendDataBlock(urlType.ULog, listaValori);
+ }
+ else
+ {
+ // invio in blocco
+ listaValori = QueueULog.ToList();
+ // invio
+ await sendDataBlock(urlType.ULog, listaValori);
+ // svuoto!
+ QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueULog", false, redisMan);
+ }
+ }
+ }
+ parentForm.displayTaskAndLog("[STOP] Stopping adapter...", true);
+ adpTryRestart = false;
+
+ parentForm.displayTaskAndLog("[STOP] Stopping adapter - last periodic data read...", true);
+
+ // salvo statistiche
+ string callKey = GetCallStatsKey();
+ await CallMetricsCollector.SaveToRedisAsync(redisMan.currDb, callKey, false);
+
+ // 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;
+ }
+
+ ///
+ /// Processo la coda SignalIN...
+ ///
+ public async Task svuotaCodaSignInAsync()
+ {
+ // 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 listaValori = new List();
+ // 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);
+ }
+ await sendDataBlock(urlType.SignIN, listaValori);
+ }
+ else
+ {
+ // invio in blocco
+ listaValori = QueueIN.ToList();
+ // invio
+ await sendDataBlock(urlType.SignIN, listaValori);
+ // svuoto!
+ QueueIN = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueIN", IOBConfFull.General.EnabRedisQue, redisMan);
+ }
+ }
+ else
+ {
+ // INVIO SINGOLO...!!!
+ QueueIN.TryDequeue(out currVal);
+ await 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;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Metodo base connessione...
+ ///
+ public virtual void tryConnect()
+ {
+ dtAvvioAdp = DateTime.Now;
+ queueInEnabCurr = true;
+ }
+
+ ///
+ /// Metodo base disconnessione...
+ ///
+ public virtual void tryDisconnect()
+ {
+ queueInEnabCurr = false;
+ }
+
+ ///
+ /// Inserimento/aggiornamento chiavi/valore in currProdData + cache REDIS
+ ///
+ ///
+ ///
+ /// True se modificato/inserito, false se INVARIATO
+ public bool upsertKey(string chiave, string valore)
+ {
+ bool done = false;
+ if (currProdData.ContainsKey(chiave))
+ {
+ // se variato inserisco...
+ if (currProdData[chiave] != valore)
+ {
+ currProdData[chiave] = valore;
+ done = true;
+ }
+ }
+ else
+ {
+ currProdData.Add(chiave, valore);
+ done = true;
+ }
+ if (done)
+ {
+ // salvo in redis...
+ redisMan.redSaveHashDict(rKeyCurrProdData, currProdData);
+ }
+ return done;
+ }
+
+ ///
+ /// Inserimento/aggiornamento chiavi/valore in lastProdData
+ ///
+ ///
+ ///
+ /// True se modificato/inserito, false se INVARIATO
+ public bool upsertKeyLP(string chiave, string valore)
+ {
+ bool done = false;
+ if (lastProdData.ContainsKey(chiave))
+ {
+ // se variato inserisco...
+ if (lastProdData[chiave] != valore)
+ {
+ lastProdData[chiave] = valore;
+ done = true;
+ }
+ }
+ else
+ {
+ lastProdData.Add(chiave, valore);
+ done = true;
+ }
+ return done;
+ }
+
+ ///
+ /// Formatta URL x invio in DataBlock Json dei dati FLog / eventi
+ ///
+ ///
+ ///
+ public virtual string urlDataBlock(urlType tipoUrl)
+ {
+ // verifico la parte di link "tipoComando"
+ string tipoComando = "";
+ switch (tipoUrl)
+ {
+ case urlType.FLog:
+ tipoComando = "flogJson";
+ break;
+
+ case urlType.SignIN:
+ tipoComando = "evListJson";
+ break;
+
+ case urlType.RawTransf:
+ tipoComando = "rawTransfJson";
+ break;
+
+ case urlType.ULog:
+ tipoComando = "ulogJson";
+ break;
+
+ default:
+ break;
+ }
+ // URL base x input
+ string answ = $@"{urlCommandIob(tipoComando)}";
+ // se è disabilitato keepalive aggiungo opzione
+ if (IOBConfFull.MapoMes.DisabKeepAlive)
+ {
+ // se c'è già "?" aggiungo con "&" altrimenti
+ string sPar = answ.Contains("?") ? "&&" : "?";
+ answ += $@"{sPar}disabKA=true";
+ }
+ return answ;
+ }
+
+ ///
+ /// Fornisce URL di tipo FluxLog
+ ///
+ /// valore salvato in coda nel formato dtEve#flux#valore#counter
+ ///
+ public string urlFLog(string queueVal)
+ {
+ // URL base x input
+ string answ = $@"{urlCommandIob("flog")}";
+ // decodifica valore!
+ string[] valori = qDecodeIN(queueVal);
+ // aggiungo flux e valore...
+ answ += $@"?flux={valori[1]}&&valore={valori[2]}";
+ // aggiondo dataOra evento e corrente + contatore...
+ answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[3]}";
+ // se è disabilitato keepalive aggiungo opzione
+ if (IOBConfFull.MapoMes.DisabKeepAlive)
+ {
+ ;
+ answ += $"&&disabKA=true";
+ }
+ return answ;
+ }
+
+ ///
+ /// URL per recupero dati ODL alla data...
+ ///
+ public string urlGetOdlAtDate(DateTime dtRif)
+ {
+ string answ = $@"{urlCommandIob("getOdlAtDate")}?dateRif={dtRif:yyyyMMdd}";
+ return answ;
+ }
+
+ ///
+ /// Fornisce URL INPUT per i parametri richiesti
+ ///
+ /// valore salvato in coda formato dtEve#valore#counter
+ ///
+ public string urlInput(string queueVal)
+ {
+ // URL base x input
+ string answ = $@"{urlCommandIob("input")}";
+ // decodifica valore!
+ string[] valori = qDecodeIN(queueVal);
+ // aggiungo macchina e valore...
+ answ += $@"?valore={valori[1]}";
+ // aggiondo dataOra evento e corrente + contatore...
+ answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[2]}";
+ return answ;
+ }
+
+ ///
+ /// Fornisce URL di tipo UserLog
+ ///
+ /// valore salvato in coda nel formato dtEve#flux#valore#counter
+ ///
+ public string urlULog(string queueVal)
+ {
+ // URL base x input
+ string answ = $@"{urlCommandIob("ulog")}";
+ // decodifica valore!
+ string[] valori = qDecodeIN(queueVal);
+ // aggiungo macchina e valore...
+ answ += $@"?flux={valori[1]}&&valore={valori[2]}";
+ // aggiondo dataOra evento e corrente + contatore...
+ answ += $@"&&dtEve={valori[0]}&&dtCurr={DateTime.Now:yyyyMMddHHmmssfff}&&cnt={valori[3]}";
+ return answ;
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ ///
+ /// Test ping + api al server in modalità Async
+ ///
+ ///
+ ///
+ private async Task ExecuteApiCheckWithRetryAsync(int maxRetries)
+ {
+ var rand = new Random();
+ for (int i = 0; i <= maxRetries; i++)
+ {
+ try
+ {
+ // Se non è il primo tentativo, resetta i client e aspetta
+ if (i > 0)
+ {
+ await Task.Delay(rand.Next(150, 500));
+ }
+
+ string resp = await utils.callUrlAsync(urlAlive);
+ if (resp == "OK") return true;
+ }
+ catch (Exception ex)
+ {
+ if (i == 0) lgError($"Errore API Check: {ex.Message}");
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Update stato server
+ ///
+ ///
+ private void UpdateServerState(bool currentAlive)
+ {
+ if (MPOnline != currentAlive)
+ {
+ MPOnline = currentAlive;
+ parentForm.commSrvActive = currentAlive ? 1 : 0;
+
+ if (currentAlive)
+ {
+ lgInfo("SERVER ONLINE");
+ dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10);
+ }
+ else
+ {
+ lgError("SERVER OFFLINE");
+ // Veto standard per server offline
+ dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20);
+ utils.dtVetoSend = dtVetoPing;
+ }
+ }
+ else
+ {
+ // Se lo stato è invariato (es. era online e resta online), allunghiamo il prossimo controllo
+ dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 30);
+ }
+ }
+
+ #endregion Private Methods
#region Protected Fields
///
@@ -638,7 +4519,7 @@ namespace IOB_WIN_FORM.Iob
get
{
Dictionary answ = new Dictionary();
- string redKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:POdlSent");
+ string redKey = GetPOdlSentKey();
string rawData = redisMan.getRSV(redKey);
if (!string.IsNullOrEmpty(rawData))
{
@@ -657,7 +4538,7 @@ namespace IOB_WIN_FORM.Iob
set
{
string rawVal = JsonConvert.SerializeObject(value);
- string redKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:POdlSent");
+ string redKey = GetPOdlSentKey();
redisMan.setRSV(redKey, rawVal);
lgDebug($"Salvataggio status POdlSentFileArch | {value.Count} record");
}
@@ -730,7 +4611,7 @@ namespace IOB_WIN_FORM.Iob
///
protected string rKeyFluxMem
{
- get => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:FluxMem");
+ get => GetFluxMemKey();
}
protected Random rndGen { get; set; } = new Random();
@@ -2050,33 +5931,7 @@ namespace IOB_WIN_FORM.Iob
///
protected virtual bool iobWriteLocalUSTD()
{
- bool answ = false;
-#if false
- // conf ftp
- var ftpConf = IOBConfFull.Special.FtpConf;
- // salvo articoli
- string locDir = ftpConf.DirLocal;
- bool addHeader = ftpConf.CsvAddHeader;
- string basePath = Application.StartupPath;
- string tempDir = Path.Combine(basePath, locDir);
- lgInfo($"iobWriteLocalCSV | locDir: {locDir} | addHeader: {addHeader} | 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}");
- }
- }
-#endif
- return answ;
+ return false;
}
///
@@ -2327,7 +6182,7 @@ namespace IOB_WIN_FORM.Iob
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
- var rawListPODL = await callUrl(urlGetNextPODL, false);
+ var rawListPODL = await utils.callUrlAsync(urlGetNextPODL);
if (!string.IsNullOrEmpty(rawListPODL))
{
reqPOdlList = JsonConvert.DeserializeObject>(rawListPODL) ?? new List();
@@ -2782,38 +6637,28 @@ namespace IOB_WIN_FORM.Iob
// 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))
+ var currRecipe = XmlDataSerializer.Deserialize(rawData);
+ if (currRecipe != null)
{
- try
+ // 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))
{
- 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}");
+ 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);
}
return weeksProc;
}
@@ -2875,14 +6720,13 @@ namespace IOB_WIN_FORM.Iob
redHashWeek.Add(cWeek, rawWeek);
}
// salvo in redis...
- string fullKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:WeekStats");
+ string fullKey = GetWeekStatsKey();
var okHashDict = redisMan.redSaveHashDict(fullKey, redHashWeek);
// invio ANCHE in MP-IO l'update delle info...
string remUrl = urlSetHashDict;
string dictPayload = JsonConvert.SerializeObject(redHashWeek);
- //await callUrlWithPayloadAsync(remUrl, dictPayload, false);
- await callUrlWithPayloadAsync(remUrl, dictPayload, true);
+ await utils.callUrlAsync(remUrl, dictPayload);
}
}
@@ -2905,7 +6749,7 @@ namespace IOB_WIN_FORM.Iob
// 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:{IOBConfFull.General.FilenameIOB}:WeekStats");
+ string fullKey = GetWeekStatsKey();
Dictionary currStats = redisMan.redGetHashDict(fullKey);
if (currStats != null && currStats.Count > 0)
{
@@ -3017,45 +6861,57 @@ namespace IOB_WIN_FORM.Iob
// 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))
+ var currRecipe = XmlDataSerializer.Deserialize(rawData);
+ if (currRecipe != null)
{
- try
+ if (currRecipe.ColRecipe != null && currRecipe.ColRecipe.Count > 0)
{
- var currRecipe = (ARecipe)serializer.Deserialize(reader);
- if (currRecipe != null)
+ List RigheConsumi = new List();
+ foreach (var rigaComp in currRecipe.ColRecipe)
{
- if (currRecipe.ColRecipe != null && currRecipe.ColRecipe.Count > 0)
- {
- List RigheConsumi = new List();
- 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();
- }
+ ColData datiComp = rigaComp.ColData;
+ RigheConsumi.Add(datiComp);
}
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in RecipeGetCons{Environment.NewLine}{exc}");
+ // 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();
}
}
return listConsumi;
}
+ // // 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;
+ // }
+
///
/// Prepara la ricetta indicata partendo dai dati della ricetta template + dati ODL di riferimento
///
@@ -3130,7 +6986,7 @@ namespace IOB_WIN_FORM.Iob
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
- var rawListPODL = await callUrl(urlGetNextPODL, false);
+ var rawListPODL = await utils.callUrlAsync(urlGetNextPODL);
if (!string.IsNullOrEmpty(rawListPODL))
{
try
@@ -3323,7 +7179,7 @@ namespace IOB_WIN_FORM.Iob
{
bool answ = false;
DateTime dtCurr = DateTime.Now;
- string resp = await callUrl($"{urlODLClose}{idxOdl}&dtEve={dtRif}&dtCurr={dtCurr}", false);
+ string resp = await utils.callUrlAsync($"{urlODLClose}{idxOdl}&dtEve={dtRif}&dtCurr={dtCurr}");
answ = resp == "OK";
return answ;
}
@@ -3344,7 +7200,7 @@ namespace IOB_WIN_FORM.Iob
Task.Run(async () =>
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
answ = callResp == "OK";
})
.GetAwaiter()
@@ -3495,7 +7351,7 @@ namespace IOB_WIN_FORM.Iob
// invio e salvo...
string remUrl = urlSaveMachIobConf;
string dictPayload = JsonConvert.SerializeObject(currDict);
- await callUrlWithPayloadAsync(remUrl, dictPayload, true);
+ await utils.callUrlAsync(remUrl, dictPayload);
lgTrace("Invio MachineConf effettuato");
}
}
@@ -3522,9 +7378,6 @@ namespace IOB_WIN_FORM.Iob
string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}";
lgInfo("chiamata URL " + url2call);
await utils.callUrlAsync(url2call);
-#if false
- utils.callUrlNow(url2call);
-#endif
}
}
}
@@ -3557,7 +7410,7 @@ namespace IOB_WIN_FORM.Iob
Task.Run(async () =>
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
answ = callResp == "OK";
})
.GetAwaiter()
@@ -3587,7 +7440,7 @@ namespace IOB_WIN_FORM.Iob
Task.Run(async () =>
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
answ = callResp == "OK";
})
.GetAwaiter()
@@ -4140,7 +7993,7 @@ namespace IOB_WIN_FORM.Iob
try
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
fatto = (callResp != "KO") ? true : false;
}
catch
@@ -4184,7 +8037,7 @@ namespace IOB_WIN_FORM.Iob
try
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
fatto = (callResp != "KO") ? true : false;
}
catch
@@ -4230,7 +8083,7 @@ namespace IOB_WIN_FORM.Iob
try
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
fatto = (callResp != "KO") ? true : false;
}
catch
@@ -4254,7 +8107,7 @@ namespace IOB_WIN_FORM.Iob
Task.Run(async () =>
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
fatto = (callResp != "KO") ? true : false;
})
.GetAwaiter()
@@ -4286,7 +8139,7 @@ namespace IOB_WIN_FORM.Iob
Task.Run(async () =>
{
// invio chiamata URL x chiusura ODL su macchina
- string callResp = await callUrl(fullUrl, false);
+ string callResp = await utils.callUrlAsync(fullUrl);
fatto = (callResp != "KO") ? true : false;
})
.GetAwaiter()
@@ -4315,7 +8168,7 @@ namespace IOB_WIN_FORM.Iob
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
- string resp = await callUrl(urlEncoded, false);
+ string resp = await utils.callUrlAsync(urlEncoded);
int.TryParse(resp, out answ);
})
.GetAwaiter()
@@ -4581,7 +8434,7 @@ namespace IOB_WIN_FORM.Iob
try
{
// invio chiamata URL x avvio PODL su macchina
- string rawSplit = await callUrl(fullUrl, false);
+ string rawSplit = await utils.callUrlAsync(fullUrl);
fatto = (rawSplit != "KO") ? true : false;
}
catch
@@ -4606,7 +8459,7 @@ namespace IOB_WIN_FORM.Iob
try
{
// invio chiamata URL x avvio PODL su macchina
- string rawSplit = await callUrl(fullUrl, false);
+ string rawSplit = await utils.callUrlAsync(fullUrl);
fatto = (rawSplit != "KO") ? true : false;
}
catch
@@ -4907,7 +8760,7 @@ namespace IOB_WIN_FORM.Iob
private async Task currOdlStart()
{
DateTime inizioOdl = DateTime.Now;
- string rawDataInizio = await callUrl(urlInizioOdlIob, false);
+ string rawDataInizio = await utils.callUrlAsync(urlInizioOdlIob);
DateTime.TryParse(rawDataInizio, out inizioOdl);
return inizioOdl;
}
@@ -5043,14 +8896,11 @@ namespace IOB_WIN_FORM.Iob
{
if (i > 0)
{
- // Al terzo tentativo fallito resetto i client
- if (i == 3) resetWebClients();
-
int delay = i == 3 ? rand.Next(250, 1000) : rand.Next(250, 500);
await Task.Delay(delay);
}
- string callResp = await callUrl(urlIobEnabled, i < 3); // true per i primi tentativi
+ string callResp = await utils.callUrlAsync(urlIobEnabled);
if (callResp == "OK") return true;
}
catch (Exception exc)
@@ -5623,12 +9473,12 @@ namespace IOB_WIN_FORM.Iob
// prova ad avviare/chiudere PODL relativo (eventualmente duplicandolo)
dtEve = $"{dtStartPOdl:yyyyMMddHHmmssfff}";
dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}";
- await callUrl($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false);
+ await utils.callUrlAsync($"{urlOdlStartFromPOdl}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}");
// ora chiamo chiusura...
dtEve = $"{dtEndPOdl:yyyyMMddHHmmssfff}";
dtCurr = $"{DateTime.Now:yyyyMMddHHmmssfff}";
- await callUrl($"{urlPODLClose}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}", false);
+ await utils.callUrlAsync($"{urlPODLClose}{idxPOdl}&dtEve={dtEve}&dtCurr={dtCurr}");
})
.GetAwaiter()
.GetResult();
@@ -5649,14 +9499,15 @@ namespace IOB_WIN_FORM.Iob
///
private async Task RecipeRemoveWeekStatus(string keyReq)
{
- string fullKey = redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:WeekStats");
+ string fullKey = GetWeekStatsKey();
var okHashDict = redisMan.redRemoveHashField(fullKey, keyReq);
// rileggo status hash
Dictionary currDict = redisMan.redGetHashDict(fullKey);
// invio ANCHE in MP-IO l'update delle info...
string remUrl = urlSetHashDict;
string dictPayload = JsonConvert.SerializeObject(currDict);
- await callUrlWithPayloadAsync(remUrl, dictPayload, true);
+ await utils.callUrlAsync(remUrl, dictPayload);
+ //await callUrlWithPayloadAsync(remUrl, dictPayload, true);
//await callUrlWithPayloadAsync(remUrl, dictPayload, false);
}
diff --git a/IOB-WIN-FORM/Iob/Services/DataSerializer.cs b/IOB-WIN-FORM/Iob/Services/DataSerializer.cs
new file mode 100644
index 00000000..99ed9405
--- /dev/null
+++ b/IOB-WIN-FORM/Iob/Services/DataSerializer.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Newtonsoft.Json;
+
+namespace IOB_WIN_FORM.Iob.Services
+{
+ ///
+ /// Gestisce tutte le operazioni di serializzazione e deserializzazione dei dati.
+ ///
+ public static class DataSerializer
+ {
+ ///
+ /// Serializza un oggetto in una stringa JSON utilizzando la cultura invariante.
+ ///
+ public static string Serialize(T obj)
+ {
+ if (obj == null) return null;
+ // Utilizzo di CultureInfo.InvariantCulture per garantire la coerenza dei formati (es. decimali)
+ return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
+ {
+ Culture = CultureInfo.InvariantCulture
+ });
+ }
+
+ ///
+ /// Deserializza una stringa JSON in un oggetto del tipo specificato.
+ ///
+ public static T Deserialize(string json)
+ {
+ if (string.IsNullOrWhiteSpace(json)) return default;
+ return JsonConvert.DeserializeObject(json, new JsonSerializerSettings
+ {
+ Culture = CultureInfo.InvariantCulture
+ });
+ }
+
+ ///
+ /// Helper per convertire un dizionario di oggetti in un dizionario di stringhe.
+ ///
+ public static Dictionary ToDictionary(Dictionary input)
+ {
+ if (input == null) return new Dictionary();
+
+ var dict = new Dictionary();
+ foreach (var pair in input)
+ {
+ dict[pair.Key] = pair.Value?.ToString();
+ }
+ return dict;
+ }
+ }
+}
diff --git a/IOB-WIN-FORM/Iob/Services/XmlDataSerializer.cs b/IOB-WIN-FORM/Iob/Services/XmlDataSerializer.cs
new file mode 100644
index 00000000..2770b758
--- /dev/null
+++ b/IOB-WIN-FORM/Iob/Services/XmlDataSerializer.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.Xml.Serialization;
+
+namespace IOB_WIN_FORM.Iob.Services
+{
+ ///
+ /// Gestisce le operazioni di serializzazione e deserializzazione in formato XML.
+ ///
+ public static class XmlDataSerializer
+ {
+ ///
+ /// Serializza un oggetto in una stringa XML.
+ ///
+ /// Tipo dell'oggetto.
+ /// L'oggetto da serializzare.
+ /// Stringa XML o null se l'oggetto è null.
+ public static string Serialize(T obj)
+ {
+ if (obj == null) return null;
+
+ try
+ {
+ using (var stringWriter = new StringWriter())
+ {
+ var serializer = new XmlSerializer(typeof(T));
+ serializer.Serialize(stringWriter, obj);
+ return stringWriter.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log l'errore se necessario. In un contesto di refactoring,
+ // potremmo voler passare l'eccezione verso l'alto.
+ throw new InvalidOperationException($"Errore durante la serializzazione XML per il tipo {typeof(T).Name}", ex);
+ }
+ }
+
+ ///
+ /// Deserializza una stringa XML in un oggetto del tipo specificato.
+ ///
+ /// Tipo dell'oggetto.
+ /// La stringa XML.
+ /// L'oggetto deserializzato o default se la stringa è nulla/vuota.
+ public static T Deserialize(string xmlString)
+ {
+ if (string.IsNullOrWhiteSpace(xmlString)) return default;
+
+ try
+ {
+ using (var stringReader = new StringReader(xmlString))
+ {
+ var serializer = new XmlSerializer(typeof(T));
+ return (T)serializer.Deserialize(stringReader);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Errore durante la deserializzazione XML per il tipo {typeof(T).Name}", ex);
+ }
+ }
+ }
+}
diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs
index c7105df3..58c82e1b 100644
--- a/IOB-WIN-FORM/Iob/Simula.cs
+++ b/IOB-WIN-FORM/Iob/Simula.cs
@@ -1603,10 +1603,10 @@ namespace IOB_WIN_FORM.Iob
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
- var rawListArt = await callUrl(urlGetCurrArt, false);
- var rawListDOSS = await callUrl(urlGetCurrDOSS, false);
- var rawListPODL = await callUrl(urlGetNextPODL, false);
- var rawLVFasi = await callUrl(urlGetListValFasiPodl, false);
+ var rawListArt = await utils.callUrlAsync(urlGetCurrArt);
+ var rawListDOSS = await utils.callUrlAsync(urlGetCurrDOSS);
+ var rawListPODL = await utils.callUrlAsync(urlGetNextPODL);
+ var rawLVFasi = await utils.callUrlAsync(urlGetListValFasiPodl);
if (!string.IsNullOrEmpty(rawListArt))
{
@@ -1754,9 +1754,9 @@ namespace IOB_WIN_FORM.Iob
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
- var rawListArt = await callUrl(urlGetCurrArt, false);
- var rawListDOSS = await callUrl(urlGetCurrDOSS, false);
- var rawListPODL = await callUrl(urlGetNextPODL, false);
+ var rawListArt = await utils.callUrlAsync(urlGetCurrArt);
+ var rawListDOSS = await utils.callUrlAsync(urlGetCurrDOSS);
+ var rawListPODL = await utils.callUrlAsync(urlGetNextPODL);
if (!string.IsNullOrEmpty(rawListArt))
{
try
diff --git a/IOB-WIN-FTP/DATA/CONF/MAIN.ini b/IOB-WIN-FTP/DATA/CONF/MAIN.ini
index ebc0bef6..2cb4f81c 100644
--- a/IOB-WIN-FTP/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-FTP/DATA/CONF/MAIN.ini
@@ -20,3 +20,4 @@ CLI_INST=SteamWareSim
STARTLIST=FTP_SONATEST
MAXCNC=10
+
diff --git a/IOB-WIN-MTC/DATA/CONF/MAIN.ini b/IOB-WIN-MTC/DATA/CONF/MAIN.ini
index 4b123815..b8e74cd4 100644
--- a/IOB-WIN-MTC/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-MTC/DATA/CONF/MAIN.ini
@@ -27,3 +27,4 @@ STARTLIST=3024
;STARTLIST=LVF652
MAXCNC=10
+
diff --git a/IOB-WIN-OPC-UA/DATA/CONF/MAIN.ini b/IOB-WIN-OPC-UA/DATA/CONF/MAIN.ini
index afc5df70..acfaebef 100644
--- a/IOB-WIN-OPC-UA/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-OPC-UA/DATA/CONF/MAIN.ini
@@ -34,3 +34,4 @@ STARTLIST=3026
;STARTLIST=SIMUL_01
MAXCNC=10
+
diff --git a/IOB-WIN-PING/DATA/CONF/MAIN.ini b/IOB-WIN-PING/DATA/CONF/MAIN.ini
index 01bdf429..14fc4c91 100644
--- a/IOB-WIN-PING/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-PING/DATA/CONF/MAIN.ini
@@ -20,3 +20,4 @@ STARTLIST=SIMUL_01
;STARTLIST=3023-PING
MAXCNC=10
+
diff --git a/IOB-WIN-SHELLY/DATA/CONF/MAIN.ini b/IOB-WIN-SHELLY/DATA/CONF/MAIN.ini
index a26f68cf..b844fa43 100644
--- a/IOB-WIN-SHELLY/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-SHELLY/DATA/CONF/MAIN.ini
@@ -27,4 +27,5 @@ STARTLIST=SIMUL_03_SHELLY
STARTLIST=SIMUL_03_SHELLY
STARTLIST=SIMUL_03_SHELLY
-MAXCNC=10
\ No newline at end of file
+MAXCNC=10
+
diff --git a/IOB-WIN-SIEMENS/DATA/CONF/MAIN.ini b/IOB-WIN-SIEMENS/DATA/CONF/MAIN.ini
index 41774edf..babafa34 100644
--- a/IOB-WIN-SIEMENS/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-SIEMENS/DATA/CONF/MAIN.ini
@@ -42,3 +42,4 @@ CLI_INST=SteamWareSim
STARTLIST=3010
MAXCNC=10
+
diff --git a/IOB-WIN-WPS/DATA/CONF/MAIN.ini b/IOB-WIN-WPS/DATA/CONF/MAIN.ini
index a81dc4b2..5b0016e5 100644
--- a/IOB-WIN-WPS/DATA/CONF/MAIN.ini
+++ b/IOB-WIN-WPS/DATA/CONF/MAIN.ini
@@ -24,3 +24,4 @@ STARTLIST=3018
;STARTLIST=SIMUL_06
MAXCNC=10
+
diff --git a/refactoring_wip.md b/refactoring_wip.md
new file mode 100644
index 00000000..a8bbed85
--- /dev/null
+++ b/refactoring_wip.md
@@ -0,0 +1,36 @@
+# WIP: Refactoring Phase 1 - Infrastructure Extraction
+
+## Status: IN_PROGRESS
+
+## Objective
+Extract infrastructure and helper components from `Generic.cs` to improve modularity and reduce the monolithic footprint.
+
+## Tasks
+
+### 1. Communication Service Extraction
+- [ ] Extract REST/HTTP logic (currently using `utils.callUrl`, `callUrlWithPayloadAsync`, etc.).
+- [ ] Implement a singleton/service-based `CommunicationService` using `HttpClient`.
+- [ ] **Italian Commenting Requirement**: All new/modified code comments must be in Italian.
+
+### 2. Redis Service Extraction (COMPLETED)
+- [x] Encapsulate all `redisMan` calls into a `RedisService`.
+- [x] Define a clean interface for key/value and hash operations.
+- [x] **Refactored**: Moved semantic key construction from `RedisService` to `BaseObj` to eliminate redundant service layer.
+
+### 3. Data Serializer Extraction
+- [ ] Move all `JsonConvert` and custom string formatting (e.g., `qEncodeFLog`, `qEncodeIN`) to a `DataSerializer` service.
+- [ ] Centralize `CultureInfo.InvariantCulture` usage.
+
+### 4. BaseObj Simplification (NEW)
+- [ ] Analyze `BaseObj` responsibilities (State, Config, Messaging, Diagnostics).
+- [ ] Identify candidates for extraction into specialized services (e.g., `QueueManager`, `ConfigService`, `DiagnosticService`).
+- [ ] Implement extraction of identified components.
+
+## Progress Log
+- [x] Created WIP document.
+- [x] Analyzed `Generic.cs` for Phase 1 candidates.
+- [x] Completed Redis Semantic Refactoring:
+ - Moved key construction logic from `RedisService` to `BaseObj`.
+ - Refactored `Generic.cs` to use `BaseObj` semantic methods.
+ - Eliminated redundant `RedisService.cs`.
+- [ ] Started Analysis of `BaseObj` to identify extraction candidates.