From 94933d72d135e0bd64a0141906011a0199a961c7 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 18:25:45 +0200 Subject: [PATCH 01/39] fix versione veccioa IOB-WIN (NON next) --- IOB-WIN.sln | 2 +- IOB-WIN/IobGeneric.cs | 38 +++++++++++++++++++------------------- IOB-WIN/IobOSAI.cs | 4 ++-- IOB-WIN/IobSimula.cs | 6 +++--- IOB-WIN/MainForm.cs | 10 +++++----- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/IOB-WIN.sln b/IOB-WIN.sln index 1616467b..08db1cc0 100644 --- a/IOB-WIN.sln +++ b/IOB-WIN.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 d17.12 +VisualStudioVersion = 17.12.35527.113 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOB-WIN", "IOB-WIN\IOB-WIN.csproj", "{ADCB8028-79C4-4896-A9A7-E3C5140FC00B}" EndProject diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 3a248edd..8b1c1a87 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -2173,11 +2173,11 @@ namespace IOB_WIN { lgInfo("chiamata URL " + url2call); } - answ = HttpService.CallUrlNow(url2call); + answ = utils.callUrlNow(url2call); // se vuoto faccio seconda prova... if (string.IsNullOrEmpty(answ)) { - answ = HttpService.CallUrlNow(url2call); + answ = utils.callUrlNow(url2call); } return answ; } @@ -2200,7 +2200,7 @@ namespace IOB_WIN { lgInfo("chiamata URL " + url2call); } - answ = HttpService.CallUrlNow(url2call); + answ = utils.callUrlNow(url2call); } return answ; } @@ -2462,7 +2462,7 @@ namespace IOB_WIN { // invio su cloud conf memoria... string rawData = JsonConvert.SerializeObject(memMap); - HttpService.CallUrlNow($"{urlSaveMemMap}", rawData); + utils.callUrlNow($"{urlSaveMemMap}", rawData); // salvo ANCHE come parametri i valori... objItem currItem = new objItem(); List allParam = new List(); @@ -2490,7 +2490,7 @@ namespace IOB_WIN } // invio su cloud parametri! rawData = JsonConvert.SerializeObject(allParam); - HttpService.CallUrl($"{urlSaveAllParams}", rawData); + utils.callUrl($"{urlSaveAllParams}", rawData); } } catch (Exception exc) @@ -2565,7 +2565,7 @@ namespace IOB_WIN plcWriteParams(updatedPar); // invio su cloud parametri! string rawData = JsonConvert.SerializeObject(updatedPar); - HttpService.CallUrl($"{urlUpdateWriteParams}", rawData); + utils.callUrl($"{urlUpdateWriteParams}", rawData); } } catch (Exception exc) @@ -2608,7 +2608,7 @@ namespace IOB_WIN { string url2call = $"{urlRemTask2Exe}{taskName}"; lgInfo($"Task2Exe | {esitoTask} | chiamata URL {url2call}"); - answ = HttpService.CallUrlNow(url2call); + answ = utils.callUrlNow(url2call); } return answ; } @@ -2621,7 +2621,7 @@ namespace IOB_WIN if (checkServerAlive) { lgInfo("chiamata URL " + urlSetM2IOB); - HttpService.CallUrlNow(urlSetM2IOB); + utils.callUrlNow(urlSetM2IOB); } } @@ -2636,7 +2636,7 @@ namespace IOB_WIN { string url2call = $"{urlSetOptVal}pName={paramName}&pValue={paramValue}"; lgInfo("chiamata URL " + url2call); - HttpService.CallUrlNow(url2call); + utils.callUrlNow(url2call); } } @@ -2737,14 +2737,14 @@ namespace IOB_WIN // Chiamata ASINCRONA if (doAsync) { - //Task resp = HttpService.CallUrlAsync(URL); + //Task resp = utils.callUrlAsync(URL); //answ = resp.Result; - answ = HttpService.CallUrlAsync(URL); + answ = utils.callUrlAsync(URL); } // chiamata SOLO NORMALE SINCRONA... else { - answ = HttpService.CallUrl(URL); + answ = utils.callUrl(URL); } return answ; } @@ -2762,12 +2762,12 @@ namespace IOB_WIN // Chiamata ASINCRONA if (doAsync) { - answ = HttpService.CallUrlAsync(URL, payload); + answ = utils.callUrlAsync(URL, payload); } // chiamata SOLO NORMALE SINCRONA... else { - answ = HttpService.CallUrl(URL, payload); + answ = utils.callUrl(URL, payload); } return answ; } @@ -3998,19 +3998,19 @@ namespace IOB_WIN if (checkServerAlive) { // leggo PRIMA ODL .... - lastIdxODL = HttpService.CallUrl(urlGetCurrODL); + lastIdxODL = utils.callUrl(urlGetCurrODL); lgInfo("Lettura ODL dall'url {0} --> {1}", urlGetCurrODL, lastIdxODL); // se ho valori in coda da trasmettere uso dati REDIS if (forceCountRec) { // uso dati da TCiclo registrati... - currServerCount = HttpService.CallUrl(urlGetPzCountRec); + currServerCount = utils.callUrl(urlGetPzCountRec); lgInfo("Lettura contapezzi da TCiclo dall'url {0} --> num pz: {1}", urlGetPzCountRec, currServerCount); } else { // uso il contapezzi dichiarato dall'IOB stesso - currServerCount = HttpService.CallUrl(urlGetPzCount); + currServerCount = utils.callUrl(urlGetPzCount); lgInfo("Lettura contapezzi dall'url {0}", urlGetPzCount); } // controllo: SE NON HO ODL... @@ -4640,7 +4640,7 @@ namespace IOB_WIN numIncr = delta > maxSendPzCountBlock + minSendPzCountBlock ? maxSendPzCountBlock : delta - minSendPzCountBlock; // invio il num max di pezzi ammesso in blocco! lastUrl = $"{urlAddPzCount}{numIncr}"; - string resp = HttpService.CallUrlNow(lastUrl); + string resp = utils.callUrlNow(lastUrl); if (!string.IsNullOrEmpty(resp)) { // dalla risposta (come numero) capisco SE ha aggiunto i pezzi (e quanti) @@ -4651,7 +4651,7 @@ namespace IOB_WIN contapezziIOB += qtyAdded; lgInfo($"Inviato incremento contapezzi: send: {numIncr} | resp: {qtyAdded} | contapezziIOB: {contapezziIOB}"); // invio conferma contapezzi.. - string retVal = HttpService.CallUrl($"{urlSetPzCount}{contapezziIOB}"); + string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); // verifica se tutto OK if (retVal != contapezziIOB.ToString()) { diff --git a/IOB-WIN/IobOSAI.cs b/IOB-WIN/IobOSAI.cs index b463435f..fb7cd067 100644 --- a/IOB-WIN/IobOSAI.cs +++ b/IOB-WIN/IobOSAI.cs @@ -214,7 +214,7 @@ namespace IOB_WIN { try { - currODL = HttpService.CallUrl(urlGetCurrODL); + currODL = utils.callUrl(urlGetCurrODL); // solo SE HO un ODL... if (string.IsNullOrEmpty(currODL) || currODL == "0") { @@ -272,7 +272,7 @@ namespace IOB_WIN } // invio a server contapezzi (aggiornato) - string retVal = HttpService.CallUrl(urlSetPzCount + contapezziIOB.ToString()); + string retVal = utils.callUrl(urlSetPzCount + contapezziIOB.ToString()); // verifica se tutto OK if (retVal.Trim() != $"{contapezziIOB}") { diff --git a/IOB-WIN/IobSimula.cs b/IOB-WIN/IobSimula.cs index 4560f9aa..dfd82792 100644 --- a/IOB-WIN/IobSimula.cs +++ b/IOB-WIN/IobSimula.cs @@ -246,7 +246,7 @@ namespace IOB_WIN contapezziIOB++; needRefreshPzCount = true; // invio conferma contapezzi.. - string retVal = HttpService.CallUrl($"{urlSetPzCount}{contapezziIOB}"); + string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); // verifica salvataggio if (retVal != contapezziIOB.ToString()) { @@ -258,7 +258,7 @@ namespace IOB_WIN } else { - string retVal = HttpService.CallUrl($"{urlSetPzCount}{contapezziIOB}"); + string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}"); if (retVal != contapezziIOB.ToString()) { // errore salvataggio contapezzi @@ -398,7 +398,7 @@ namespace IOB_WIN if (checkServerAlive) { // invio a server contapezzi (aggiornato) - string retVal = HttpService.CallUrl(urlSetPzCount + contapezziIOB.ToString()); + string retVal = utils.callUrl(urlSetPzCount + contapezziIOB.ToString()); // verifica se tutto OK if (retVal != contapezziIOB.ToString()) { diff --git a/IOB-WIN/MainForm.cs b/IOB-WIN/MainForm.cs index 77a7c6a3..b827dccf 100644 --- a/IOB-WIN/MainForm.cs +++ b/IOB-WIN/MainForm.cs @@ -330,7 +330,7 @@ namespace IOB_WIN try { // chiamo URL, se restituisce "OK" è alive! - string callResp = HttpService.CallUrl(urlAlive); + string callResp = utils.callUrl(urlAlive); answ = (callResp == "OK"); } catch (Exception exc) @@ -563,7 +563,7 @@ namespace IOB_WIN // invio in locale! url2call = $"{urlDownloadFile}{currIob}"; } - rawData = HttpService.CallUrlNow(url2call); + rawData = utils.callUrlNow(url2call); if (!string.IsNullOrEmpty(rawData)) { // deserializzo @@ -894,12 +894,12 @@ namespace IOB_WIN if (utils.CRB("ConfToCloud")) { // invio su cloud... - answ = HttpService.CallUrl($"{urlUploadFileCloud}{currIob}", rawData); + answ = utils.callUrl($"{urlUploadFileCloud}{currIob}", rawData); } else { // invio in locale! - answ = HttpService.CallUrl($"{urlUploadFile}{currIob}", rawData); + answ = utils.callUrl($"{urlUploadFile}{currIob}", rawData); } } catch (Exception exc) @@ -1026,7 +1026,7 @@ namespace IOB_WIN if (pingStatus == IPStatus.Success) { // segnalo reboot (programma)... - HttpService.CallUrl(urlReboot); + utils.callUrl(urlReboot); } else { From 7e234807a7073bc4b1ac6e1f50ebd7a9ecd5f45f Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 18:40:54 +0200 Subject: [PATCH 02/39] Spostamento servizi in proj base + continuo reorg classi --- IOB-UT-NEXT/Config/IobConfTree.cs | 2 +- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 8 +++---- IOB-UT-NEXT/Iob/BaseObj.cs | 24 +++++++++++++++++++ .../{Iob => }/Services/DataSerializer.cs | 2 +- IOB-UT-NEXT/{Iob => }/Services/HttpService.cs | 2 +- IOB-UT-NEXT/{Iob => }/Services/NetService.cs | 2 +- .../{Iob => }/Services/XmlDataSerializer.cs | 2 +- IOB-UT-NEXT/utils.cs | 2 +- IOB-WIN-FANUC/Iob/Fanuc.cs | 2 +- IOB-WIN-FORM/AdapterForm.cs | 2 +- IOB-WIN-FORM/Iob/Generic.cs | 21 ++++++++-------- IOB-WIN-FORM/Iob/Simula.cs | 2 +- IOB-WIN-FORM/MainForm.cs | 2 +- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 2 +- IOB-WIN-MTC/Iob/MTConn.cs | 2 +- IOB-WIN-OMRON/Iob/Omron.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs | 2 +- IOB-WIN-OSAI/Iob/OSAI.cs | 2 +- IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs | 2 +- IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs | 2 +- IOB-WIN-SQL/IobSql/SqlServLantek.cs | 2 +- IOB-WIN-SQL/IobSql/SqlServPama.cs | 2 +- 25 files changed, 61 insertions(+), 36 deletions(-) rename IOB-UT-NEXT/{Iob => }/Services/DataSerializer.cs (98%) rename IOB-UT-NEXT/{Iob => }/Services/HttpService.cs (99%) rename IOB-UT-NEXT/{Iob => }/Services/NetService.cs (98%) rename IOB-UT-NEXT/{Iob => }/Services/XmlDataSerializer.cs (98%) diff --git a/IOB-UT-NEXT/Config/IobConfTree.cs b/IOB-UT-NEXT/Config/IobConfTree.cs index ee6d6996..bd244244 100644 --- a/IOB-UT-NEXT/Config/IobConfTree.cs +++ b/IOB-UT-NEXT/Config/IobConfTree.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; using IOB_UT_NEXT.Config.Special; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 298588a6..3b634451 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -202,10 +202,10 @@ - - - - + + + + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index c53a9160..ec58c572 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -588,6 +588,30 @@ namespace IOB_UT_NEXT.Iob #endregion Protected Methods + #region Protected Serialization Helpers + + /// + /// Serializza un oggetto in formato JSON. + /// + protected string JsonSerialize(T obj) => IOB_UT_NEXT.Services.DataSerializer.Serialize(obj); + + /// + /// Deserializza una stringa JSON in un oggetto. + /// + protected T JsonDeserialize(string json) => IOB_UT_NEXT.Services.DataSerializer.Deserialize(json); + + /// + /// Serializza un oggetto in formato XML. + /// + protected string XmlSerialize(T obj) => IOB_UT_NEXT.Services.XmlDataSerializer.Serialize(obj); + + /// + /// Deserializza una stringa XML in un oggetto. + /// + protected T XmlDeserialize(string xml) => IOB_UT_NEXT.Services.XmlDataSerializer.Deserialize(xml); + + #endregion Protected Serialization Helpers + #region Private Fields /// diff --git a/IOB-UT-NEXT/Iob/Services/DataSerializer.cs b/IOB-UT-NEXT/Services/DataSerializer.cs similarity index 98% rename from IOB-UT-NEXT/Iob/Services/DataSerializer.cs rename to IOB-UT-NEXT/Services/DataSerializer.cs index d0bdad59..e1e2a1dd 100644 --- a/IOB-UT-NEXT/Iob/Services/DataSerializer.cs +++ b/IOB-UT-NEXT/Services/DataSerializer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json; -namespace IOB_UT_NEXT.Iob.Services +namespace IOB_UT_NEXT.Services { /// /// Gestisce tutte le operazioni di serializzazione e deserializzazione dei dati. diff --git a/IOB-UT-NEXT/Iob/Services/HttpService.cs b/IOB-UT-NEXT/Services/HttpService.cs similarity index 99% rename from IOB-UT-NEXT/Iob/Services/HttpService.cs rename to IOB-UT-NEXT/Services/HttpService.cs index 9a2e5ab0..e3766674 100644 --- a/IOB-UT-NEXT/Iob/Services/HttpService.cs +++ b/IOB-UT-NEXT/Services/HttpService.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace IOB_UT_NEXT.Iob.Services +namespace IOB_UT_NEXT.Services { /// /// Servizio dedicato alla gestione delle chiamate HTTP. diff --git a/IOB-UT-NEXT/Iob/Services/NetService.cs b/IOB-UT-NEXT/Services/NetService.cs similarity index 98% rename from IOB-UT-NEXT/Iob/Services/NetService.cs rename to IOB-UT-NEXT/Services/NetService.cs index 3ef5415c..56e58227 100644 --- a/IOB-UT-NEXT/Iob/Services/NetService.cs +++ b/IOB-UT-NEXT/Services/NetService.cs @@ -6,7 +6,7 @@ using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT.Iob.Services +namespace IOB_UT_NEXT.Services { public class NetService { diff --git a/IOB-UT-NEXT/Iob/Services/XmlDataSerializer.cs b/IOB-UT-NEXT/Services/XmlDataSerializer.cs similarity index 98% rename from IOB-UT-NEXT/Iob/Services/XmlDataSerializer.cs rename to IOB-UT-NEXT/Services/XmlDataSerializer.cs index e05e339c..27ae4e77 100644 --- a/IOB-UT-NEXT/Iob/Services/XmlDataSerializer.cs +++ b/IOB-UT-NEXT/Services/XmlDataSerializer.cs @@ -2,7 +2,7 @@ using System; using System.IO; using System.Xml.Serialization; -namespace IOB_UT_NEXT.Iob.Services +namespace IOB_UT_NEXT.Services { /// /// Gestisce le operazioni di serializzazione e deserializzazione in formato XML. diff --git a/IOB-UT-NEXT/utils.cs b/IOB-UT-NEXT/utils.cs index cc7bd526..e7b9fc96 100644 --- a/IOB-UT-NEXT/utils.cs +++ b/IOB-UT-NEXT/utils.cs @@ -1,4 +1,4 @@ -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using System; using System.IO; using System.Linq; diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index 0e1d71ed..022a6c74 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -2,7 +2,7 @@ using EgwProxy.MultiCncLib.CNC; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using NLog; using System; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 613b9706..6b640176 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index da8cc0ef..4a3faf93 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1,7 +1,7 @@ using EgwProxy.Ftp; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using MathNet.Numerics.Statistics; using Newtonsoft.Json; @@ -369,7 +369,7 @@ namespace IOB_WIN_FORM.Iob /// public string urlReboot { - get => $@"{urlCommandIobFile("sendReboot")}?mac={GetMACAddress()}"; + get => $@"{urlCommandIobFile("sendReboot")}?mac={NetService.GetMACAddress()}"; } /// @@ -404,13 +404,13 @@ namespace IOB_WIN_FORM.Iob #region Public Methods /// - /// Esegue conversione in un dizionario di tipo string/string serializzando e deserializzando + /// Conversione in un dizionario di tipo string/string serializzando e deserializzando /// /// /// public static Dictionary ConvertToStringDict(Dictionary input) { - return DataSerializer.ToDictionary(input); + return IOB_UT_NEXT.Services.DataSerializer.ToDictionary(input); } /// @@ -544,7 +544,7 @@ namespace IOB_WIN_FORM.Iob } BaseRawTransf newVal = new BaseRawTransf(DateTime.Now, njObj, mesType); - string encodedVal = DataSerializer.Serialize(newVal); + string encodedVal = JsonSerialize(newVal); // --> accodo (valore già formattato)! QueueRawTransf.Enqueue(encodedVal); // se abilitato controllo coda Max (superiore a 0...) @@ -1047,20 +1047,21 @@ namespace IOB_WIN_FORM.Iob foreach (var rawJob in listaValori) { // deserializzo... - JobTaskData jobTaskReq = JsonConvert.DeserializeObject(rawJob); + JobTaskData jobTaskReq = JsonDeserialize(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); + string serVal = JsonSerialize(taskDone); accodaServResp(jobTaskReq.CodTav, serVal); } } - - // svuoto! - QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); + + // svuoto! + QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); } } } diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 992dd3ec..1ca5d7e4 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-FORM/MainForm.cs b/IOB-WIN-FORM/MainForm.cs index 72ec1d1d..f0bcd492 100644 --- a/IOB-WIN-FORM/MainForm.cs +++ b/IOB-WIN-FORM/MainForm.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using MathNet.Numerics.Distributions; using Newtonsoft.Json; diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index 876916e9..50d0ac44 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index 3a3c8cd2..fdc546ac 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using MTConnect.Assets; using MTConnect.Clients; diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index 5b67ebbc..5292f148 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index c0c8b7fb..74ad5c40 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.DataModel; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs index f6bb14ad..4ee6a60d 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using Opc.Ua; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs index a54941a9..ae368359 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs index 22c99d53..638599ae 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using Opc.Ua; diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index c1e11386..491073ae 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -2,7 +2,7 @@ using EgwProxy.OsaiCncLib; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs index a1f7504d..e543d62f 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using System; using System.Collections.Generic; using System.Linq; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs index 11d18196..9a7d8c26 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using System; using System.Collections.Generic; using System.Linq; diff --git a/IOB-WIN-SQL/IobSql/SqlServLantek.cs b/IOB-WIN-SQL/IobSql/SqlServLantek.cs index f292a883..f7336a92 100644 --- a/IOB-WIN-SQL/IobSql/SqlServLantek.cs +++ b/IOB-WIN-SQL/IobSql/SqlServLantek.cs @@ -2,7 +2,7 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index 5d0648c6..93fc6115 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -2,7 +2,7 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Iob.Services; +using IOB_UT_NEXT.Services; using MapoSDK; using Newtonsoft.Json; using System; From 648e03c52e315a264e8d682b5f0bc1271a29978b Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 18:48:17 +0200 Subject: [PATCH 03/39] Ancora spostamento servizi base --- IOB-UT-NEXT/Config/IobConfTree.cs | 2 +- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 13 ++++++++----- IOB-UT-NEXT/Iob/BaseObj.cs | 8 ++++---- IOB-UT-NEXT/Services/{ => Data}/DataSerializer.cs | 2 +- .../Services/{ => Data}/XmlDataSerializer.cs | 2 +- .../Services/{ => Networking}/HttpService.cs | 2 +- IOB-UT-NEXT/Services/{ => Networking}/NetService.cs | 2 +- IOB-UT-NEXT/utils.cs | 2 +- IOB-WIN-FANUC/Iob/Fanuc.cs | 2 +- IOB-WIN-FORM/AdapterForm.cs | 2 +- IOB-WIN-FORM/Iob/Generic.cs | 5 +++-- IOB-WIN-FORM/Iob/Simula.cs | 2 +- IOB-WIN-FORM/MainForm.cs | 2 +- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 2 +- IOB-WIN-MTC/Iob/MTConn.cs | 2 +- IOB-WIN-OMRON/Iob/Omron.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs | 2 +- IOB-WIN-OSAI/Iob/OSAI.cs | 2 +- IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs | 2 +- IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs | 2 +- IOB-WIN-SQL/IobSql/SqlServLantek.cs | 2 +- IOB-WIN-SQL/IobSql/SqlServPama.cs | 2 +- 25 files changed, 37 insertions(+), 33 deletions(-) rename IOB-UT-NEXT/Services/{ => Data}/DataSerializer.cs (98%) rename IOB-UT-NEXT/Services/{ => Data}/XmlDataSerializer.cs (98%) rename IOB-UT-NEXT/Services/{ => Networking}/HttpService.cs (99%) rename IOB-UT-NEXT/Services/{ => Networking}/NetService.cs (98%) diff --git a/IOB-UT-NEXT/Config/IobConfTree.cs b/IOB-UT-NEXT/Config/IobConfTree.cs index bd244244..95b7c715 100644 --- a/IOB-UT-NEXT/Config/IobConfTree.cs +++ b/IOB-UT-NEXT/Config/IobConfTree.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; using IOB_UT_NEXT.Config.Special; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 3b634451..43d605eb 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -202,10 +202,10 @@ - - - - + + + + @@ -249,7 +249,10 @@ Always - + + + + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index ec58c572..d221558a 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -593,22 +593,22 @@ namespace IOB_UT_NEXT.Iob /// /// Serializza un oggetto in formato JSON. /// - protected string JsonSerialize(T obj) => IOB_UT_NEXT.Services.DataSerializer.Serialize(obj); + protected string JsonSerialize(T obj) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj); /// /// Deserializza una stringa JSON in un oggetto. /// - protected T JsonDeserialize(string json) => IOB_UT_NEXT.Services.DataSerializer.Deserialize(json); + protected T JsonDeserialize(string json) => IOB_UT_NEXT.Services.Data.DataSerializer.Deserialize(json); /// /// Serializza un oggetto in formato XML. /// - protected string XmlSerialize(T obj) => IOB_UT_NEXT.Services.XmlDataSerializer.Serialize(obj); + protected string XmlSerialize(T obj) => IOB_UT_NEXT.Services.Data.XmlDataSerializer.Serialize(obj); /// /// Deserializza una stringa XML in un oggetto. /// - protected T XmlDeserialize(string xml) => IOB_UT_NEXT.Services.XmlDataSerializer.Deserialize(xml); + protected T XmlDeserialize(string xml) => IOB_UT_NEXT.Services.Data.XmlDataSerializer.Deserialize(xml); #endregion Protected Serialization Helpers diff --git a/IOB-UT-NEXT/Services/DataSerializer.cs b/IOB-UT-NEXT/Services/Data/DataSerializer.cs similarity index 98% rename from IOB-UT-NEXT/Services/DataSerializer.cs rename to IOB-UT-NEXT/Services/Data/DataSerializer.cs index e1e2a1dd..3046b9c8 100644 --- a/IOB-UT-NEXT/Services/DataSerializer.cs +++ b/IOB-UT-NEXT/Services/Data/DataSerializer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json; -namespace IOB_UT_NEXT.Services +namespace IOB_UT_NEXT.Services.Data { /// /// Gestisce tutte le operazioni di serializzazione e deserializzazione dei dati. diff --git a/IOB-UT-NEXT/Services/XmlDataSerializer.cs b/IOB-UT-NEXT/Services/Data/XmlDataSerializer.cs similarity index 98% rename from IOB-UT-NEXT/Services/XmlDataSerializer.cs rename to IOB-UT-NEXT/Services/Data/XmlDataSerializer.cs index 27ae4e77..edb84ff9 100644 --- a/IOB-UT-NEXT/Services/XmlDataSerializer.cs +++ b/IOB-UT-NEXT/Services/Data/XmlDataSerializer.cs @@ -2,7 +2,7 @@ using System; using System.IO; using System.Xml.Serialization; -namespace IOB_UT_NEXT.Services +namespace IOB_UT_NEXT.Services.Data { /// /// Gestisce le operazioni di serializzazione e deserializzazione in formato XML. diff --git a/IOB-UT-NEXT/Services/HttpService.cs b/IOB-UT-NEXT/Services/Networking/HttpService.cs similarity index 99% rename from IOB-UT-NEXT/Services/HttpService.cs rename to IOB-UT-NEXT/Services/Networking/HttpService.cs index e3766674..91a2e01a 100644 --- a/IOB-UT-NEXT/Services/HttpService.cs +++ b/IOB-UT-NEXT/Services/Networking/HttpService.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace IOB_UT_NEXT.Services +namespace IOB_UT_NEXT.Services.Networking { /// /// Servizio dedicato alla gestione delle chiamate HTTP. diff --git a/IOB-UT-NEXT/Services/NetService.cs b/IOB-UT-NEXT/Services/Networking/NetService.cs similarity index 98% rename from IOB-UT-NEXT/Services/NetService.cs rename to IOB-UT-NEXT/Services/Networking/NetService.cs index 56e58227..dc2c71b7 100644 --- a/IOB-UT-NEXT/Services/NetService.cs +++ b/IOB-UT-NEXT/Services/Networking/NetService.cs @@ -6,7 +6,7 @@ using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT.Services +namespace IOB_UT_NEXT.Services.Networking { public class NetService { diff --git a/IOB-UT-NEXT/utils.cs b/IOB-UT-NEXT/utils.cs index e7b9fc96..b8069604 100644 --- a/IOB-UT-NEXT/utils.cs +++ b/IOB-UT-NEXT/utils.cs @@ -1,4 +1,4 @@ -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using System; using System.IO; using System.Linq; diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index 022a6c74..6cd9523d 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -2,7 +2,7 @@ using EgwProxy.MultiCncLib.CNC; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using NLog; using System; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 6b640176..15dd9866 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 4a3faf93..cda2d21a 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1,7 +1,8 @@ using EgwProxy.Ftp; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MathNet.Numerics.Statistics; using Newtonsoft.Json; @@ -410,7 +411,7 @@ namespace IOB_WIN_FORM.Iob /// public static Dictionary ConvertToStringDict(Dictionary input) { - return IOB_UT_NEXT.Services.DataSerializer.ToDictionary(input); + return IOB_UT_NEXT.Services.Data.DataSerializer.ToDictionary(input); } /// diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 1ca5d7e4..744e92ca 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-FORM/MainForm.cs b/IOB-WIN-FORM/MainForm.cs index f0bcd492..f46ee82c 100644 --- a/IOB-WIN-FORM/MainForm.cs +++ b/IOB-WIN-FORM/MainForm.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MathNet.Numerics.Distributions; using Newtonsoft.Json; diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index 50d0ac44..765e4011 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index fdc546ac..5c7d7635 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MTConnect.Assets; using MTConnect.Clients; diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index 5292f148..e8a34f85 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 74ad5c40..6faeec24 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -1,7 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.DataModel; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using NLog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs index 4ee6a60d..23e278db 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpwRama.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using Opc.Ua; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs index ae368359..becd145e 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBHCimolai.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs index 638599ae..74425e59 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using Opc.Ua; diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 491073ae..6aa48741 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -2,7 +2,7 @@ using EgwProxy.OsaiCncLib; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs index e543d62f..6ff38b52 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using System; using System.Collections.Generic; using System.Linq; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs index 9a7d8c26..3bbd9cd4 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs @@ -1,6 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using System; using System.Collections.Generic; using System.Linq; diff --git a/IOB-WIN-SQL/IobSql/SqlServLantek.cs b/IOB-WIN-SQL/IobSql/SqlServLantek.cs index f7336a92..56731f8d 100644 --- a/IOB-WIN-SQL/IobSql/SqlServLantek.cs +++ b/IOB-WIN-SQL/IobSql/SqlServLantek.cs @@ -2,7 +2,7 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index 93fc6115..3666cd3f 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -2,7 +2,7 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Services; +using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using System; From 243b863d9cc668ea1aa3bf685111ef727eed8211 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 18:55:29 +0200 Subject: [PATCH 04/39] Ancora riorganizzazione classi e servizi --- IOB-UT-NEXT/{ => Config}/GenActConf.cs | 2 +- .../Config/{ => Special}/ExtToolConf.cs | 2 +- .../{ => Config/Special}/FtpActConf.cs | 2 +- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 17 +++++++-------- IOB-UT-NEXT/Iob/BaseObj.cs | 1 + IOB-UT-NEXT/Objects.cs | 4 +++- IOB-UT-NEXT/{ => Services/Data}/DataExport.cs | 2 +- IOB-UT-NEXT/{ => Services/Data}/DataQueue.cs | 2 +- IOB-UT-NEXT/{ => Services/Files}/Eurom63.cs | 2 +- .../Files/FileMover.cs} | 12 +++++------ .../{ => Services/Files}/FileProcMan.cs | 2 +- IOB-UT-NEXT/baseUtils.cs | 21 ++++++++++--------- IOB-WIN-FORM/AdapterForm.cs | 3 ++- IOB-WIN-FORM/Iob/Generic.cs | 3 ++- IOB-WIN-FORM/Iob/PingWatchDog.cs | 1 + IOB-WIN-FORM/MainForm.cs | 3 ++- 16 files changed, 43 insertions(+), 36 deletions(-) rename IOB-UT-NEXT/{ => Config}/GenActConf.cs (98%) rename IOB-UT-NEXT/Config/{ => Special}/ExtToolConf.cs (98%) rename IOB-UT-NEXT/{ => Config/Special}/FtpActConf.cs (96%) rename IOB-UT-NEXT/{ => Services/Data}/DataExport.cs (99%) rename IOB-UT-NEXT/{ => Services/Data}/DataQueue.cs (98%) rename IOB-UT-NEXT/{ => Services/Files}/Eurom63.cs (99%) rename IOB-UT-NEXT/{fileMover.cs => Services/Files/FileMover.cs} (99%) rename IOB-UT-NEXT/{ => Services/Files}/FileProcMan.cs (99%) diff --git a/IOB-UT-NEXT/GenActConf.cs b/IOB-UT-NEXT/Config/GenActConf.cs similarity index 98% rename from IOB-UT-NEXT/GenActConf.cs rename to IOB-UT-NEXT/Config/GenActConf.cs index cac92fbe..eed4447f 100644 --- a/IOB-UT-NEXT/GenActConf.cs +++ b/IOB-UT-NEXT/Config/GenActConf.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Config { public class GenActConf { diff --git a/IOB-UT-NEXT/Config/ExtToolConf.cs b/IOB-UT-NEXT/Config/Special/ExtToolConf.cs similarity index 98% rename from IOB-UT-NEXT/Config/ExtToolConf.cs rename to IOB-UT-NEXT/Config/Special/ExtToolConf.cs index 9ba5b529..cfb7f5e4 100644 --- a/IOB-UT-NEXT/Config/ExtToolConf.cs +++ b/IOB-UT-NEXT/Config/Special/ExtToolConf.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT.Config +namespace IOB_UT_NEXT.Config.Special { /// /// Implementazione di riferimento x un file di configurazione x esecuzione task tramite EgwCApp diff --git a/IOB-UT-NEXT/FtpActConf.cs b/IOB-UT-NEXT/Config/Special/FtpActConf.cs similarity index 96% rename from IOB-UT-NEXT/FtpActConf.cs rename to IOB-UT-NEXT/Config/Special/FtpActConf.cs index 1d7d3e80..d915f6a1 100644 --- a/IOB-UT-NEXT/FtpActConf.cs +++ b/IOB-UT-NEXT/Config/Special/FtpActConf.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Config.Special { public class FtpActConf : GenActConf { diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 43d605eb..af28ecdd 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -176,7 +176,7 @@ - + @@ -188,17 +188,17 @@ - - + + - - + + - - + + @@ -217,7 +217,7 @@ - + @@ -251,7 +251,6 @@ - diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index d221558a..31e37bb8 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -1,4 +1,5 @@ using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Data; using NLog; using System; using System.Collections.Generic; diff --git a/IOB-UT-NEXT/Objects.cs b/IOB-UT-NEXT/Objects.cs index e58db839..f7ab65e4 100644 --- a/IOB-UT-NEXT/Objects.cs +++ b/IOB-UT-NEXT/Objects.cs @@ -1,4 +1,6 @@ -using MapoSDK; +using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Special; +using MapoSDK; using System; using System.Collections.Generic; using System.Linq; diff --git a/IOB-UT-NEXT/DataExport.cs b/IOB-UT-NEXT/Services/Data/DataExport.cs similarity index 99% rename from IOB-UT-NEXT/DataExport.cs rename to IOB-UT-NEXT/Services/Data/DataExport.cs index c422549b..951b4060 100644 --- a/IOB-UT-NEXT/DataExport.cs +++ b/IOB-UT-NEXT/Services/Data/DataExport.cs @@ -5,7 +5,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Data { public class DataExport { diff --git a/IOB-UT-NEXT/DataQueue.cs b/IOB-UT-NEXT/Services/Data/DataQueue.cs similarity index 98% rename from IOB-UT-NEXT/DataQueue.cs rename to IOB-UT-NEXT/Services/Data/DataQueue.cs index 4b8ecd32..99534ac2 100644 --- a/IOB-UT-NEXT/DataQueue.cs +++ b/IOB-UT-NEXT/Services/Data/DataQueue.cs @@ -4,7 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Data { /// /// Classe gestione code, a seconda della conf come LIST redis o come concurrent queue in memoria diff --git a/IOB-UT-NEXT/Eurom63.cs b/IOB-UT-NEXT/Services/Files/Eurom63.cs similarity index 99% rename from IOB-UT-NEXT/Eurom63.cs rename to IOB-UT-NEXT/Services/Files/Eurom63.cs index 77a276c4..4a4f5e8c 100644 --- a/IOB-UT-NEXT/Eurom63.cs +++ b/IOB-UT-NEXT/Services/Files/Eurom63.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Files { /// /// Classe di helper per metodi Euromap63 tramite File Exchange diff --git a/IOB-UT-NEXT/fileMover.cs b/IOB-UT-NEXT/Services/Files/FileMover.cs similarity index 99% rename from IOB-UT-NEXT/fileMover.cs rename to IOB-UT-NEXT/Services/Files/FileMover.cs index 0506555c..a453f66d 100644 --- a/IOB-UT-NEXT/fileMover.cs +++ b/IOB-UT-NEXT/Services/Files/FileMover.cs @@ -6,19 +6,19 @@ using System.Linq; using System.Net; using System.Reflection; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Files { /// /// Accesso in lettura e scrittura al filesystem per gestione files upload e download /// - public class fileMover + public class FileMover { #region Public Fields /// - /// versione statica (singleton) del'oggetto fileMover + /// versione statica (singleton) del'oggetto FileMover /// - public static fileMover obj = new fileMover(); + public static FileMover obj = new FileMover(); /// /// oggetto WebClient @@ -34,7 +34,7 @@ namespace IOB_UT_NEXT /// /// /// non serve +... x retrocompatibilit�... - public fileMover(string _path, string _log) + public FileMover(string _path, string _log) { setDirs(_path); WebCli = new WebClient(); @@ -43,7 +43,7 @@ namespace IOB_UT_NEXT /// /// metodo di avvio empty /// - public fileMover() + public FileMover() { WebCli = new WebClient(); } diff --git a/IOB-UT-NEXT/FileProcMan.cs b/IOB-UT-NEXT/Services/Files/FileProcMan.cs similarity index 99% rename from IOB-UT-NEXT/FileProcMan.cs rename to IOB-UT-NEXT/Services/Files/FileProcMan.cs index cc6bd0f2..c4a73ac7 100644 --- a/IOB-UT-NEXT/FileProcMan.cs +++ b/IOB-UT-NEXT/Services/Files/FileProcMan.cs @@ -3,7 +3,7 @@ using System; using System.Diagnostics; using System.IO; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Files { public class FileProcMan { diff --git a/IOB-UT-NEXT/baseUtils.cs b/IOB-UT-NEXT/baseUtils.cs index aab1b465..95549488 100644 --- a/IOB-UT-NEXT/baseUtils.cs +++ b/IOB-UT-NEXT/baseUtils.cs @@ -1,3 +1,4 @@ +using IOB_UT_NEXT.Services.Files; using NLog; using RestSharp; using System; @@ -412,12 +413,12 @@ namespace IOB_UT_NEXT public static void shrinkDir(string dirPath) { // obj filemover... - fileMover.obj.setDirectory(dirPath); - float dirSizeMb = fileMover.obj.totalMb(); + FileMover.obj.setDirectory(dirPath); + float dirSizeMb = FileMover.obj.totalMb(); lg.Info("Inizio shrinkDir LOG folder: {0} Mb", dirSizeMb); // ottengo elenco files *.txt - FileInfo[] _fis = fileMover.obj.elencoFiles_FI("*.log"); + FileInfo[] _fis = FileMover.obj.elencoFiles_FI("*.log"); int numDdMax = 2; try { @@ -429,17 +430,17 @@ namespace IOB_UT_NEXT { if (_file.LastWriteTime < DateTime.Now.AddDays(-1)) // zippo files + vecchi di 2 gg... { - fileMover.obj.zippaSingoloFile(_file); + FileMover.obj.zippaSingoloFile(_file); // cancello l'originale... - fileMover.obj.eliminaFile(_file); + FileMover.obj.eliminaFile(_file); } } // inizio con eliminare file + vecchi della data indicata... int maxLogDays = CRI("maxLogDays"); - fileMover.obj.deleteOlderThan(maxLogDays); + FileMover.obj.deleteOlderThan(maxLogDays); // ora controllo SE sia superata la dim max della directory --> in tal caso cancello dal // + vecchio... - dirSizeMb = fileMover.obj.totalMb(); + dirSizeMb = FileMover.obj.totalMb(); int maxLogDirSize = CRI("maxLogDirSize"); int maxTry = 100; // controllo se serva eliminare... @@ -448,11 +449,11 @@ namespace IOB_UT_NEXT lg.Info("Continuo shrinkDir LOG folder: {0} Mb --> ELIMINAZIONE FILES", dirSizeMb); while (dirSizeMb > maxLogDirSize) { - fileMover.obj.deleteOldest(); + FileMover.obj.deleteOldest(); maxTry--; if (maxTry > 0) { - dirSizeMb = fileMover.obj.totalMb(); + dirSizeMb = FileMover.obj.totalMb(); } else { @@ -460,7 +461,7 @@ namespace IOB_UT_NEXT dirSizeMb = maxLogDirSize - 1; } } - dirSizeMb = fileMover.obj.totalMb(); + dirSizeMb = FileMover.obj.totalMb(); lg.Info("Completata shrinkDir LOG folder: {0} Mb", dirSizeMb); } } diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 15dd9866..238440eb 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; @@ -1963,7 +1964,7 @@ namespace IOB_WIN_FORM // out di cosa faccio... displayTaskAndLog($"[STARTUP] Loading ConfFile: {iniConfFile}"); var appVers = Assembly.GetExecutingAssembly().GetName().Version; - string path = fileMover.GetExecutingDirectoryName(); // Directory.GetCurrentDirectory(); + string path = FileMover.GetExecutingDirectoryName(); // Directory.GetCurrentDirectory(); string fileName = Path.GetFileName(iniConfFile).Replace(".ini", ".iob"); string dirPath = Path.Combine(path, "DATA", "CONF_RUN"); // verifica directory diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index cda2d21a..1d6241c5 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MathNet.Numerics.Statistics; @@ -8879,7 +8880,7 @@ namespace IOB_WIN_FORM.Iob bool fatto = false; if (Directory.Exists(folderPath)) { - fatto = fileMover.zippaDirectory(folderPath, zipName); + fatto = FileMover.zippaDirectory(folderPath, zipName); // se sent elimino vecchia directory... if (fatto) { diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index 56843e48..33c87f78 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Data; using MapoSDK; using System; using System.Diagnostics; diff --git a/IOB-WIN-FORM/MainForm.cs b/IOB-WIN-FORM/MainForm.cs index f46ee82c..6746702c 100644 --- a/IOB-WIN-FORM/MainForm.cs +++ b/IOB-WIN-FORM/MainForm.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MathNet.Numerics.Distributions; @@ -845,7 +846,7 @@ namespace IOB_WIN_FORM // salvo! foreach (var item in objFiles.fileList) { - fileMover.obj.salvaFileString(utils.confDir, item.fileName, item.content); + FileMover.obj.salvaFileString(utils.confDir, item.fileName, item.content); } displayTaskAndLog("Download conf files aggiornati"); } From 4f24c54e9bc413849d160697ef905d5c88f48784 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:01:35 +0200 Subject: [PATCH 05/39] Ancora spostamenti e riorganizzaizone classi e servizi --- IOB-UT-NEXT/Config/IobConfTree.cs | 1 + IOB-UT-NEXT/{ => Config/Mem}/plcMemMapExt.cs | 9 ++------- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 14 ++++++-------- IOB-UT-NEXT/Iob/BaseObj.cs | 1 + IOB-UT-NEXT/{ => Services/Core}/BinaryUtils.cs | 2 +- .../{ => Services/Core}/BitConditionCheck.cs | 2 +- IOB-UT-NEXT/{ => Services/Core}/BitUtils.cs | 8 +------- .../{ => Services/Core}/ByteDataConverter.cs | 3 ++- IOB-UT-NEXT/baseUtils.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 2 ++ IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 3 ++- IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 1 + 12 files changed, 21 insertions(+), 26 deletions(-) rename IOB-UT-NEXT/{ => Config/Mem}/plcMemMapExt.cs (92%) rename IOB-UT-NEXT/{ => Services/Core}/BinaryUtils.cs (98%) rename IOB-UT-NEXT/{ => Services/Core}/BitConditionCheck.cs (98%) rename IOB-UT-NEXT/{ => Services/Core}/BitUtils.cs (80%) rename IOB-UT-NEXT/{ => Services/Core}/ByteDataConverter.cs (97%) diff --git a/IOB-UT-NEXT/Config/IobConfTree.cs b/IOB-UT-NEXT/Config/IobConfTree.cs index 95b7c715..c92faadb 100644 --- a/IOB-UT-NEXT/Config/IobConfTree.cs +++ b/IOB-UT-NEXT/Config/IobConfTree.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; +using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Config.Special; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-UT-NEXT/plcMemMapExt.cs b/IOB-UT-NEXT/Config/Mem/plcMemMapExt.cs similarity index 92% rename from IOB-UT-NEXT/plcMemMapExt.cs rename to IOB-UT-NEXT/Config/Mem/plcMemMapExt.cs index 74d9fa29..7395aeba 100644 --- a/IOB-UT-NEXT/plcMemMapExt.cs +++ b/IOB-UT-NEXT/Config/Mem/plcMemMapExt.cs @@ -1,12 +1,7 @@ -using IOB_UT_NEXT.Config.Special; -using MapoSDK; -using System; +using MapoSDK; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Config.Mem { public class plcMemMapExt : plcMemMap { diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index af28ecdd..e4f658e1 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -156,8 +156,8 @@ - - + + @@ -195,7 +195,7 @@ - + @@ -208,14 +208,14 @@ - + - + @@ -249,9 +249,7 @@ Always - - - + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 31e37bb8..61d8d3aa 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -1,4 +1,5 @@ using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Services.Data; using NLog; using System; diff --git a/IOB-UT-NEXT/BinaryUtils.cs b/IOB-UT-NEXT/Services/Core/BinaryUtils.cs similarity index 98% rename from IOB-UT-NEXT/BinaryUtils.cs rename to IOB-UT-NEXT/Services/Core/BinaryUtils.cs index d7edbc87..6a55bca0 100644 --- a/IOB-UT-NEXT/BinaryUtils.cs +++ b/IOB-UT-NEXT/Services/Core/BinaryUtils.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.Linq; using System.Numerics; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { public class BinaryUtils : IFormatProvider, ICustomFormatter { diff --git a/IOB-UT-NEXT/BitConditionCheck.cs b/IOB-UT-NEXT/Services/Core/BitConditionCheck.cs similarity index 98% rename from IOB-UT-NEXT/BitConditionCheck.cs rename to IOB-UT-NEXT/Services/Core/BitConditionCheck.cs index 07224b3f..688e34f8 100644 --- a/IOB-UT-NEXT/BitConditionCheck.cs +++ b/IOB-UT-NEXT/Services/Core/BitConditionCheck.cs @@ -1,4 +1,4 @@ -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { public class BitConditionCheck { diff --git a/IOB-UT-NEXT/BitUtils.cs b/IOB-UT-NEXT/Services/Core/BitUtils.cs similarity index 80% rename from IOB-UT-NEXT/BitUtils.cs rename to IOB-UT-NEXT/Services/Core/BitUtils.cs index 3193bdd5..fe3ff7ee 100644 --- a/IOB-UT-NEXT/BitUtils.cs +++ b/IOB-UT-NEXT/Services/Core/BitUtils.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { public class BitUtils { diff --git a/IOB-UT-NEXT/ByteDataConverter.cs b/IOB-UT-NEXT/Services/Core/ByteDataConverter.cs similarity index 97% rename from IOB-UT-NEXT/ByteDataConverter.cs rename to IOB-UT-NEXT/Services/Core/ByteDataConverter.cs index 047dff32..dc13295f 100644 --- a/IOB-UT-NEXT/ByteDataConverter.cs +++ b/IOB-UT-NEXT/Services/Core/ByteDataConverter.cs @@ -1,4 +1,5 @@ -namespace IOB_UT_NEXT + +namespace IOB_UT_NEXT.Services.Core { public class ByteDataConverter { diff --git a/IOB-UT-NEXT/baseUtils.cs b/IOB-UT-NEXT/baseUtils.cs index 95549488..8cc20d8e 100644 --- a/IOB-UT-NEXT/baseUtils.cs +++ b/IOB-UT-NEXT/baseUtils.cs @@ -1,3 +1,4 @@ +using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Files; using NLog; using RestSharp; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 1d6241c5..964e7ba4 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1,6 +1,8 @@ using EgwProxy.Ftp; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 6faeec24..e3bfc4e1 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.DataModel; using IOB_UT_NEXT.Services.Networking; using MapoSDK; @@ -16,7 +17,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using static IOB_UT_NEXT.ByteDataConverter; +using static IOB_UT_NEXT.Services.Core.ByteDataConverter; namespace IOB_WIN_OPC_UA.IobOpc { diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index 571b1505..440a7c25 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; using MapoSDK; using Newtonsoft.Json; using S7.Net; From 07711dc06c2a1578524ee2c276a0a9d7a3e369ec Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:17:47 +0200 Subject: [PATCH 06/39] Ancora spostamenti classi Servizi --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 10 ++++---- IOB-UT-NEXT/Iob/BaseObj.cs | 2 ++ .../{ => Services/Cache}/RedisIobCache.cs | 2 +- .../{ => Services/Core}/JobTask2Exe.cs | 7 +----- IOB-UT-NEXT/{ => Services/Core}/TCMan.cs | 2 +- IOB-UT-NEXT/{ => Services/Core}/TimerMan.cs | 5 +--- IOB-UT-NEXT/Services/Data/DataQueue.cs | 3 ++- .../Services/{Files => Protocols}/Eurom63.cs | 2 +- IOB-WIN-FILE/IobFile/FileEurom63.cs | 1 + IOB-WIN-FORM/AdapterForm.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 23 ++++++++----------- IOB-WIN-MTC/Iob/MTConn.cs | 5 ---- 12 files changed, 26 insertions(+), 37 deletions(-) rename IOB-UT-NEXT/{ => Services/Cache}/RedisIobCache.cs (99%) rename IOB-UT-NEXT/{ => Services/Core}/JobTask2Exe.cs (90%) rename IOB-UT-NEXT/{ => Services/Core}/TCMan.cs (99%) rename IOB-UT-NEXT/{ => Services/Core}/TimerMan.cs (94%) rename IOB-UT-NEXT/Services/{Files => Protocols}/Eurom63.cs (99%) diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index e4f658e1..8a4cbaed 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -156,6 +156,7 @@ + @@ -198,7 +199,6 @@ - @@ -206,12 +206,13 @@ - + - + + - + @@ -221,7 +222,6 @@ - diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 61d8d3aa..43c3f59f 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -1,5 +1,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Services.Cache; +using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using NLog; using System; diff --git a/IOB-UT-NEXT/RedisIobCache.cs b/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs similarity index 99% rename from IOB-UT-NEXT/RedisIobCache.cs rename to IOB-UT-NEXT/Services/Cache/RedisIobCache.cs index 884d02f0..a020f48c 100644 --- a/IOB-UT-NEXT/RedisIobCache.cs +++ b/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Cache { public class RedisIobCache { diff --git a/IOB-UT-NEXT/JobTask2Exe.cs b/IOB-UT-NEXT/Services/Core/JobTask2Exe.cs similarity index 90% rename from IOB-UT-NEXT/JobTask2Exe.cs rename to IOB-UT-NEXT/Services/Core/JobTask2Exe.cs index c43adffc..e9343c44 100644 --- a/IOB-UT-NEXT/JobTask2Exe.cs +++ b/IOB-UT-NEXT/Services/Core/JobTask2Exe.cs @@ -1,12 +1,7 @@ using Newtonsoft.Json; -using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { /// /// Classe per gestione Job Task2Exe x una macchina diff --git a/IOB-UT-NEXT/TCMan.cs b/IOB-UT-NEXT/Services/Core/TCMan.cs similarity index 99% rename from IOB-UT-NEXT/TCMan.cs rename to IOB-UT-NEXT/Services/Core/TCMan.cs index f7af3916..86c6ee48 100644 --- a/IOB-UT-NEXT/TCMan.cs +++ b/IOB-UT-NEXT/Services/Core/TCMan.cs @@ -1,6 +1,6 @@ using System; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { /// /// TCiclo management class diff --git a/IOB-UT-NEXT/TimerMan.cs b/IOB-UT-NEXT/Services/Core/TimerMan.cs similarity index 94% rename from IOB-UT-NEXT/TimerMan.cs rename to IOB-UT-NEXT/Services/Core/TimerMan.cs index d03f3244..813d72e7 100644 --- a/IOB-UT-NEXT/TimerMan.cs +++ b/IOB-UT-NEXT/Services/Core/TimerMan.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Core { /// /// Classe di gestione info Timer per esecuzione ordinata processi secondo priorità diff --git a/IOB-UT-NEXT/Services/Data/DataQueue.cs b/IOB-UT-NEXT/Services/Data/DataQueue.cs index 99534ac2..02db92cb 100644 --- a/IOB-UT-NEXT/Services/Data/DataQueue.cs +++ b/IOB-UT-NEXT/Services/Data/DataQueue.cs @@ -1,4 +1,5 @@ -using StackExchange.Redis; +using IOB_UT_NEXT.Services.Cache; +using StackExchange.Redis; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/IOB-UT-NEXT/Services/Files/Eurom63.cs b/IOB-UT-NEXT/Services/Protocols/Eurom63.cs similarity index 99% rename from IOB-UT-NEXT/Services/Files/Eurom63.cs rename to IOB-UT-NEXT/Services/Protocols/Eurom63.cs index 4a4f5e8c..f5bf860f 100644 --- a/IOB-UT-NEXT/Services/Files/Eurom63.cs +++ b/IOB-UT-NEXT/Services/Protocols/Eurom63.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace IOB_UT_NEXT.Services.Files +namespace IOB_UT_NEXT.Services.Protocols { /// /// Classe di helper per metodi Euromap63 tramite File Exchange diff --git a/IOB-WIN-FILE/IobFile/FileEurom63.cs b/IOB-WIN-FILE/IobFile/FileEurom63.cs index 283a00db..de92a7e7 100644 --- a/IOB-WIN-FILE/IobFile/FileEurom63.cs +++ b/IOB-WIN-FILE/IobFile/FileEurom63.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Protocols; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 238440eb..86189112 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; +using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 964e7ba4..15e6fec5 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; @@ -11,7 +12,6 @@ using MathNet.Numerics.Statistics; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; -using NLog.Targets; using System; using System.Collections; using System.Collections.Generic; @@ -23,14 +23,11 @@ using System.Net; 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 YamlDotNet.Core.Tokens; using static IOB_UT_NEXT.BaseAlarmConf; using static IOB_UT_NEXT.CustomObj; using static IOB_UT_NEXT.DataModel.Fimat; @@ -4365,16 +4362,16 @@ namespace IOB_WIN_FORM.Iob List answ = new List(); string redKeyFLog = redisMan.redHash($"IOB:CurrData:{IOBConfFull.General.FilenameIOB}:LogFile:FluxLog"); var rawData = redisMan.getRSV(redKeyFLog); - if (!string.IsNullOrEmpty(rawData)) - { - answ = JsonConvert.DeserializeObject>(rawData); - } + if (!string.IsNullOrEmpty(rawData)) + { + answ = JsonDeserialize>(rawData); + } return answ; } set { string redKeyFLog = redisMan.redHash($"IOB:CurrData:{IOBConfFull.General.FilenameIOB}:LogFile:FluxLog"); - string fluxLogRaw = JsonConvert.SerializeObject(value); + string fluxLogRaw = JsonSerialize(value); redisMan.setRSV(redKeyFLog, fluxLogRaw); } } @@ -4530,7 +4527,7 @@ namespace IOB_WIN_FORM.Iob { try { - answ = JsonConvert.DeserializeObject>(rawData); + answ = JsonDeserialize>(rawData); lgInfo($"Rilettura status POdlSentFileArch: trovati {answ.Count} record"); } catch (Exception exc) @@ -4542,7 +4539,7 @@ namespace IOB_WIN_FORM.Iob } set { - string rawVal = JsonConvert.SerializeObject(value); + string rawVal = JsonSerialize(value); string redKey = GetPOdlSentKey(); redisMan.setRSV(redKey, rawVal); lgDebug($"Salvataggio status POdlSentFileArch | {value.Count} record"); @@ -5133,7 +5130,7 @@ namespace IOB_WIN_FORM.Iob { JobTaskData jobTask = new JobTaskData(codTav, newReq); // accodo richiesta serializzata - string serVal = JsonConvert.SerializeObject(jobTask); + string serVal = JsonSerialize(jobTask); QueueSrvReq.Enqueue(serVal); // loggo! lgDebug($"[QUEUE] | QueueSrvReq len: {QueueSrvReq.Count}"); @@ -5148,7 +5145,7 @@ namespace IOB_WIN_FORM.Iob { JobTaskData jobTask = new JobTaskData(codTav, rawData); // accodo richiesta serializzata - string serVal = JsonConvert.SerializeObject(jobTask); + string serVal = JsonSerialize(jobTask); QueueSrvResp.Enqueue(serVal); // loggo! lgDebug($"[QUEUE] | QueueSrvResp len: {QueueSrvResp.Count}"); diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index 5c7d7635..c605c896 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -2,7 +2,6 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Services.Networking; using MapoSDK; -using MTConnect.Assets; using MTConnect.Clients; using MTConnect.Devices; using MTConnect.Observations; @@ -10,15 +9,11 @@ using MTConnect.Streams; using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.Data.SqlClient; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Threading; -using System.Threading.Tasks; using System.Windows.Forms; -using System.Xml.Linq; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace IOB_WIN_MTC.Iob { From e5a2b1de7f9f99528d444853e2c40e9e4599ddc5 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:25:40 +0200 Subject: [PATCH 07/39] Fix errori compile IOB minori --- IOB-WIN-FORM/Iob/Generic.cs | 4 ++-- IOB-WIN-FTP/Iob/Ftp.cs | 3 +++ IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 2 ++ IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 1 + IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 1 + IOB-WIN-WS/IobWs/EmmegiFPW.cs | 6 +++--- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 15e6fec5..312d1cf3 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -7352,8 +7352,8 @@ namespace IOB_WIN_FORM.Iob } // invio e salvo... string remUrl = urlSaveMachIobConf; - string dictPayload = JsonConvert.SerializeObject(currDict); - await HttpService.CallUrlAsync(remUrl, dictPayload); + string dictPayload = JsonSerialize(currDict); + await HttpService.CallUrlAsync(remUrl, dictPayload); lgTrace("Invio MachineConf effettuato"); } } diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index cc0f6c71..220d8f76 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -1,6 +1,9 @@ using EgwProxy.Ftp; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Config.Special; +using IOB_UT_NEXT.Services.Data; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index 1ecd3227..43617c13 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -1,6 +1,8 @@ using EasyModbus; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Services.Core; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 68bf4da1..1bc443e9 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -3,6 +3,7 @@ using EgwProxy.Shelly.Clients; using EgwProxy.Shelly.Options; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Data; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index 2ee2301a..5947a641 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -3,6 +3,7 @@ using EgwProxy.Shelly.Clients; using EgwProxy.Shelly.Options; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Data; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-WS/IobWs/EmmegiFPW.cs b/IOB-WIN-WS/IobWs/EmmegiFPW.cs index bcc1170d..83292f1a 100644 --- a/IOB-WIN-WS/IobWs/EmmegiFPW.cs +++ b/IOB-WIN-WS/IobWs/EmmegiFPW.cs @@ -1,15 +1,15 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Special; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Globalization; -using System.IdentityModel.Tokens; using System.IO; using System.Linq; using System.Net.NetworkInformation; -using System.Runtime.Remoting.Contexts; using System.Threading.Tasks; using System.Xml.Serialization; using static IOB_UT_NEXT.CustomObj; @@ -53,7 +53,7 @@ namespace IOB_WIN_WS.IobWs if (!string.IsNullOrEmpty(FileConfPath)) { // fix exclToolDirPath - exclToolDirPath = Path.Combine(fileMover.GetExecutingDirectoryName(), "Tools"); + exclToolDirPath = Path.Combine(FileMover.GetExecutingDirectoryName(), "Tools"); string fullPath = Path.Combine(exclToolDirPath, FileConfPath); if (File.Exists(fullPath)) { From 1feedd43f439bc4fda7ec71a9d88d01fe7d75848 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:33:58 +0200 Subject: [PATCH 08/39] Ancora riorganizzazione servizi --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 12 +-- .../Core/SingleThreadTaskScheduler.cs | 92 +++++++++++++++++++ .../Monitoring}/CallMetricsCollector.cs | 2 +- .../Services/{Core => Utility}/BitUtils.cs | 2 +- .../Utility}/IntConditionCheck.cs | 2 +- .../{ => Services/Utility}/MeasureUtils.cs | 6 +- .../{ => Services/Utility}/TimeUtils.cs | 2 +- IOB-UT-NEXT/SingleThreadTaskScheduler.cs | 47 ---------- IOB-UT-NEXT/baseUtils.cs | 1 + IOB-WIN-FORM/AdapterForm.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 4 +- IOB-WIN-FTP/Iob/Ftp.cs | 1 + IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 1 + 13 files changed, 110 insertions(+), 63 deletions(-) create mode 100644 IOB-UT-NEXT/Services/Core/SingleThreadTaskScheduler.cs rename IOB-UT-NEXT/{ => Services/Monitoring}/CallMetricsCollector.cs (99%) rename IOB-UT-NEXT/Services/{Core => Utility}/BitUtils.cs (93%) rename IOB-UT-NEXT/{ => Services/Utility}/IntConditionCheck.cs (98%) rename IOB-UT-NEXT/{ => Services/Utility}/MeasureUtils.cs (93%) rename IOB-UT-NEXT/{ => Services/Utility}/TimeUtils.cs (90%) delete mode 100644 IOB-UT-NEXT/SingleThreadTaskScheduler.cs diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 8a4cbaed..ea3f7698 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -157,9 +157,9 @@ - + - + @@ -195,7 +195,7 @@ - + @@ -207,13 +207,13 @@ - + - + - + diff --git a/IOB-UT-NEXT/Services/Core/SingleThreadTaskScheduler.cs b/IOB-UT-NEXT/Services/Core/SingleThreadTaskScheduler.cs new file mode 100644 index 00000000..ac4b7528 --- /dev/null +++ b/IOB-UT-NEXT/Services/Core/SingleThreadTaskScheduler.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace IOB_UT_NEXT.Services.Core +{ + public class SingleThreadTaskScheduler : TaskScheduler, IDisposable + { + #region Public Constructors + + public SingleThreadTaskScheduler(string threadName) + { + var tcs = new TaskCompletionSource(); + _thread = new Thread(() => + { + // Creiamo il contesto di sincronizzazione personalizzato + SyncContext = new SingleThreadSynchronizationContext(this); + SynchronizationContext.SetSynchronizationContext(SyncContext); + tcs.SetResult(true); + + foreach (var task in _tasks.GetConsumingEnumerable()) + { + base.TryExecuteTask(task); + } + }) + { IsBackground = true, Name = threadName }; + _thread.Start(); + tcs.Task.Wait(); // Aspetta che il thread sia pronto col suo contesto + } + + #endregion Public Constructors + + #region Public Properties + + public override int MaximumConcurrencyLevel => 1; + public SynchronizationContext SyncContext { get; private set; } + + #endregion Public Properties + + #region Public Methods + + public void Dispose() => _tasks.CompleteAdding(); + + #endregion Public Methods + + #region Protected Methods + + protected override IEnumerable GetScheduledTasks() => _tasks; + + protected override void QueueTask(Task task) => _tasks.Add(task); + + protected override bool TryExecuteTaskInline(Task task, bool prev) + => Thread.CurrentThread == _thread && base.TryExecuteTask(task); + + #endregion Protected Methods + + #region Private Fields + + private readonly BlockingCollection _tasks = new BlockingCollection(); + private readonly Thread _thread; + + #endregion Private Fields + + #region Private Classes + + private class SingleThreadSynchronizationContext : SynchronizationContext + { + #region Public Constructors + + public SingleThreadSynchronizationContext(SingleThreadTaskScheduler sch) => _sch = sch; + + #endregion Public Constructors + + #region Public Methods + + public override void Post(SendOrPostCallback d, object state) + => Task.Factory.StartNew(() => d(state), CancellationToken.None, TaskCreationOptions.None, _sch); + + #endregion Public Methods + + #region Private Fields + + private readonly SingleThreadTaskScheduler _sch; + + #endregion Private Fields + } + + #endregion Private Classes + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/CallMetricsCollector.cs b/IOB-UT-NEXT/Services/Monitoring/CallMetricsCollector.cs similarity index 99% rename from IOB-UT-NEXT/CallMetricsCollector.cs rename to IOB-UT-NEXT/Services/Monitoring/CallMetricsCollector.cs index 6b5891e1..e161cf5f 100644 --- a/IOB-UT-NEXT/CallMetricsCollector.cs +++ b/IOB-UT-NEXT/Services/Monitoring/CallMetricsCollector.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Monitoring { public static class CallMetricsCollector { diff --git a/IOB-UT-NEXT/Services/Core/BitUtils.cs b/IOB-UT-NEXT/Services/Utility/BitUtils.cs similarity index 93% rename from IOB-UT-NEXT/Services/Core/BitUtils.cs rename to IOB-UT-NEXT/Services/Utility/BitUtils.cs index fe3ff7ee..ccb7ef7f 100644 --- a/IOB-UT-NEXT/Services/Core/BitUtils.cs +++ b/IOB-UT-NEXT/Services/Utility/BitUtils.cs @@ -1,4 +1,4 @@ -namespace IOB_UT_NEXT.Services.Core +namespace IOB_UT_NEXT.Services.Utility { public class BitUtils { diff --git a/IOB-UT-NEXT/IntConditionCheck.cs b/IOB-UT-NEXT/Services/Utility/IntConditionCheck.cs similarity index 98% rename from IOB-UT-NEXT/IntConditionCheck.cs rename to IOB-UT-NEXT/Services/Utility/IntConditionCheck.cs index 3d305482..8f400fe0 100644 --- a/IOB-UT-NEXT/IntConditionCheck.cs +++ b/IOB-UT-NEXT/Services/Utility/IntConditionCheck.cs @@ -1,4 +1,4 @@ -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Utility { public class IntConditionCheck { diff --git a/IOB-UT-NEXT/MeasureUtils.cs b/IOB-UT-NEXT/Services/Utility/MeasureUtils.cs similarity index 93% rename from IOB-UT-NEXT/MeasureUtils.cs rename to IOB-UT-NEXT/Services/Utility/MeasureUtils.cs index bd7bf830..951302f3 100644 --- a/IOB-UT-NEXT/MeasureUtils.cs +++ b/IOB-UT-NEXT/Services/Utility/MeasureUtils.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Utility { public class MeasureUtils { diff --git a/IOB-UT-NEXT/TimeUtils.cs b/IOB-UT-NEXT/Services/Utility/TimeUtils.cs similarity index 90% rename from IOB-UT-NEXT/TimeUtils.cs rename to IOB-UT-NEXT/Services/Utility/TimeUtils.cs index c46e0744..509706f3 100644 --- a/IOB-UT-NEXT/TimeUtils.cs +++ b/IOB-UT-NEXT/Services/Utility/TimeUtils.cs @@ -1,6 +1,6 @@ using System; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Utility { public class TimeUtils { diff --git a/IOB-UT-NEXT/SingleThreadTaskScheduler.cs b/IOB-UT-NEXT/SingleThreadTaskScheduler.cs deleted file mode 100644 index 7c70f915..00000000 --- a/IOB-UT-NEXT/SingleThreadTaskScheduler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; - -public class SingleThreadTaskScheduler : TaskScheduler, IDisposable -{ - private readonly BlockingCollection _tasks = new BlockingCollection(); - private readonly Thread _thread; - public SynchronizationContext SyncContext { get; private set; } - - public SingleThreadTaskScheduler(string threadName) - { - var tcs = new TaskCompletionSource(); - _thread = new Thread(() => - { - // Creiamo il contesto di sincronizzazione personalizzato - SyncContext = new SingleThreadSynchronizationContext(this); - SynchronizationContext.SetSynchronizationContext(SyncContext); - tcs.SetResult(true); - - foreach (var task in _tasks.GetConsumingEnumerable()) - { - base.TryExecuteTask(task); - } - }) - { IsBackground = true, Name = threadName }; - _thread.Start(); - tcs.Task.Wait(); // Aspetta che il thread sia pronto col suo contesto - } - - private class SingleThreadSynchronizationContext : SynchronizationContext - { - private readonly SingleThreadTaskScheduler _sch; - public SingleThreadSynchronizationContext(SingleThreadTaskScheduler sch) => _sch = sch; - public override void Post(SendOrPostCallback d, object state) - => Task.Factory.StartNew(() => d(state), CancellationToken.None, TaskCreationOptions.None, _sch); - } - - protected override void QueueTask(Task task) => _tasks.Add(task); - protected override bool TryExecuteTaskInline(Task task, bool prev) - => Thread.CurrentThread == _thread && base.TryExecuteTask(task); - protected override IEnumerable GetScheduledTasks() => _tasks; - public override int MaximumConcurrencyLevel => 1; - public void Dispose() => _tasks.CompleteAdding(); -} \ No newline at end of file diff --git a/IOB-UT-NEXT/baseUtils.cs b/IOB-UT-NEXT/baseUtils.cs index 8cc20d8e..eec5c16c 100644 --- a/IOB-UT-NEXT/baseUtils.cs +++ b/IOB-UT-NEXT/baseUtils.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Files; +using IOB_UT_NEXT.Services.Monitoring; using NLog; using RestSharp; using System; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 86189112..594f2ab3 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -3,6 +3,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Files; +using IOB_UT_NEXT.Services.Monitoring; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 312d1cf3..e303c497 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -6,7 +6,9 @@ using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; +using IOB_UT_NEXT.Services.Monitoring; using IOB_UT_NEXT.Services.Networking; +using IOB_UT_NEXT.Services.Utility; using MapoSDK; using MathNet.Numerics.Statistics; using Newtonsoft.Json; @@ -9529,7 +9531,7 @@ namespace IOB_WIN_FORM.Iob foreach (var rawJob in listaValori) { // deserializzo... - JobTaskData jobTask = JsonConvert.DeserializeObject(rawJob); + JobTaskData jobTask = JsonDeserialize(rawJob); // ora chiamo la cancellazione dei task eseguiti... var taskDict = JobTaskData.TaskDict(jobTask.RawData); foreach (var item in taskDict) diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index 220d8f76..b8de9248 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -4,6 +4,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Config.Special; using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Services.Utility; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index 43617c13..cdac1693 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -3,6 +3,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Services.Core; +using IOB_UT_NEXT.Services.Utility; using MapoSDK; using Newtonsoft.Json; using System; From 41eb15015e1fe27ab250867400239463f6ed3260 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:38:04 +0200 Subject: [PATCH 09/39] Altra riorganizzazione pesante --- IOB-UT-NEXT/Config/Base/AlarmDto.cs | 2 +- IOB-UT-NEXT/{ => Config}/IniFile.cs | 2 +- IOB-UT-NEXT/Config/IobConfTree.cs | 2 +- IOB-UT-NEXT/{ => Config}/ToMapo.cs | 3 +-- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 4 ++-- IOB-UT-NEXT/Iob/BaseObj.cs | 4 +--- IOB-WIN-FORM/Iob/BaseObj.cs | 6 ------ IOB-WIN-FORM/Iob/Generic.cs | 4 ++-- 8 files changed, 9 insertions(+), 18 deletions(-) rename IOB-UT-NEXT/{ => Config}/IniFile.cs (99%) rename IOB-UT-NEXT/{ => Config}/ToMapo.cs (99%) diff --git a/IOB-UT-NEXT/Config/Base/AlarmDto.cs b/IOB-UT-NEXT/Config/Base/AlarmDto.cs index deb05245..2a52e6d2 100644 --- a/IOB-UT-NEXT/Config/Base/AlarmDto.cs +++ b/IOB-UT-NEXT/Config/Base/AlarmDto.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using static IOB_UT_NEXT.BaseAlarmConf; +using static IOB_UT_NEXT.Config.BaseAlarmConf; namespace IOB_UT_NEXT.Config.Base { diff --git a/IOB-UT-NEXT/IniFile.cs b/IOB-UT-NEXT/Config/IniFile.cs similarity index 99% rename from IOB-UT-NEXT/IniFile.cs rename to IOB-UT-NEXT/Config/IniFile.cs index 66114c6a..7675e142 100644 --- a/IOB-UT-NEXT/IniFile.cs +++ b/IOB-UT-NEXT/Config/IniFile.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.Runtime.InteropServices; using System.Text; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Config { /// /// Create a new INI file to store or load data diff --git a/IOB-UT-NEXT/Config/IobConfTree.cs b/IOB-UT-NEXT/Config/IobConfTree.cs index c92faadb..62f30ff3 100644 --- a/IOB-UT-NEXT/Config/IobConfTree.cs +++ b/IOB-UT-NEXT/Config/IobConfTree.cs @@ -17,7 +17,7 @@ using System.Runtime.InteropServices.ComTypes; using System.Threading; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -using static IOB_UT_NEXT.BaseAlarmConf; +using static IOB_UT_NEXT.Config.BaseAlarmConf; using static IOB_UT_NEXT.Config.EnumConf; using static System.Net.Mime.MediaTypeNames; diff --git a/IOB-UT-NEXT/ToMapo.cs b/IOB-UT-NEXT/Config/ToMapo.cs similarity index 99% rename from IOB-UT-NEXT/ToMapo.cs rename to IOB-UT-NEXT/Config/ToMapo.cs index cd5d82d8..34b333a0 100644 --- a/IOB-UT-NEXT/ToMapo.cs +++ b/IOB-UT-NEXT/Config/ToMapo.cs @@ -2,9 +2,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System.Collections.Generic; -using System; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Config { /// /// Classe gestione configurazione parametri di base x allarmi diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index ea3f7698..b1820939 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -194,7 +194,7 @@ - + @@ -214,7 +214,7 @@ - + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 43c3f59f..46b08ee8 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -9,9 +9,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; -using System.Threading; -using System.Threading.Tasks; -using static IOB_UT_NEXT.BaseAlarmConf; +using static IOB_UT_NEXT.Config.BaseAlarmConf; namespace IOB_UT_NEXT.Iob { diff --git a/IOB-WIN-FORM/Iob/BaseObj.cs b/IOB-WIN-FORM/Iob/BaseObj.cs index 938c072e..1fb37ed3 100644 --- a/IOB-WIN-FORM/Iob/BaseObj.cs +++ b/IOB-WIN-FORM/Iob/BaseObj.cs @@ -1,12 +1,6 @@ using IOB_UT_NEXT; using NLog; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net.NetworkInformation; -using System.Threading; -using static IOB_UT_NEXT.BaseAlarmConf; namespace IOB_WIN_FORM.Iob { diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index e303c497..29da454e 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -30,7 +30,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Windows.Forms; -using static IOB_UT_NEXT.BaseAlarmConf; +using static IOB_UT_NEXT.Config.BaseAlarmConf; using static IOB_UT_NEXT.CustomObj; using static IOB_UT_NEXT.DataModel.Fimat; using static MapoSDK.WharehouseData; @@ -3216,7 +3216,7 @@ namespace IOB_WIN_FORM.Iob } } - string rawData = JsonConvert.SerializeObject(ActiveAlarmList); + string rawData = JsonSerialize(ActiveAlarmList); string resp = HttpService.CallUrlPost(lastUrl, rawData); //string resp = HttpService.CallUrlAsync(lastUrl, rawData); if (resp != null) From f8c1d448b30d71adc1dec50497f42438e46c668e Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:46:56 +0200 Subject: [PATCH 10/39] Altra riorganizzazione servizi --- IOB-UT-NEXT/Config/IobConfTree.cs | 1 + IOB-UT-NEXT/IOB-UT-NEXT.csproj | 2 +- IOB-UT-NEXT/Iob/BaseObj.cs | 1 + IOB-UT-NEXT/{ => Services/Files}/utils.cs | 32 ++++--------------- IOB-WIN-FANUC/Iob/Fanuc.cs | 1 + IOB-WIN-FILE/IobFile/FileEurom63.cs | 1 + IOB-WIN-FILE/IobFile/IobFileSoitaab.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 2 +- IOB-WIN-FORM/Iob/PingWatchDog.cs | 1 + IOB-WIN-FORM/Iob/Simula.cs | 1 + IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 1 + IOB-WIN-MTC/Iob/MTConn.cs | 1 + IOB-WIN-OMRON/Iob/Omron.cs | 1 + IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 1 + IOB-WIN-OSAI/Iob/OSAI.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs | 1 + IOB-WIN-WS/IobWs/Citizen.cs | 1 + IOB-WIN-WS/IobWs/Gomba.cs | 1 + IOB-WIN-WS/IobWs/IcoelSoap.cs | 1 + IOB-WIN-WS/IobWs/RestBase.cs | 1 + 23 files changed, 29 insertions(+), 27 deletions(-) rename IOB-UT-NEXT/{ => Services/Files}/utils.cs (70%) diff --git a/IOB-UT-NEXT/Config/IobConfTree.cs b/IOB-UT-NEXT/Config/IobConfTree.cs index 62f30ff3..b092ac15 100644 --- a/IOB-UT-NEXT/Config/IobConfTree.cs +++ b/IOB-UT-NEXT/Config/IobConfTree.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT.Config.Base; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Config.Special; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index b1820939..2c5ed3af 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -222,7 +222,7 @@ - + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 46b08ee8..c68f4aa0 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -3,6 +3,7 @@ using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Services.Files; using NLog; using System; using System.Collections.Generic; diff --git a/IOB-UT-NEXT/utils.cs b/IOB-UT-NEXT/Services/Files/utils.cs similarity index 70% rename from IOB-UT-NEXT/utils.cs rename to IOB-UT-NEXT/Services/Files/utils.cs index b8069604..ed5756ad 100644 --- a/IOB-UT-NEXT/utils.cs +++ b/IOB-UT-NEXT/Services/Files/utils.cs @@ -3,7 +3,7 @@ using System; using System.IO; using System.Linq; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Services.Files { public class utils : IOB_UT_NEXT.baseUtils { @@ -12,50 +12,32 @@ namespace IOB_UT_NEXT /// /// folder archiviazione dati configurazione (DATA\CONF) /// - public static string confDir - { - get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataConfPath")); - } + public static string confDir => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataConfPath")); /// /// folder archiviazione dati storici giornalieri (DATA\DAT) /// - public static string dataDatDir - { - get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataDatPath")); - } + public static string dataDatDir => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataDatPath")); /// /// folder archiviazione dati (DATA) /// - public static string dataDir - { - get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataPath")); - } + public static string dataDir => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("dataPath")); /// /// File icona default /// - public static string defIconFilePath - { - get => Path.Combine(utils.resxDir, "SteamWare.ico"); - } + public static string defIconFilePath => Path.Combine(utils.resxDir, "SteamWare.ico"); /// /// File configurazione default x MAIN /// - public static string mainConfFilePath - { - get=> Path.Combine(utils.confDir, utils.CRS("mainConfFile")); - } + public static string mainConfFilePath => Path.Combine(utils.confDir, utils.CRS("mainConfFile")); /// /// folder archiviazione dati configurazione (DATA\CONF) /// - public static string resxDir - { - get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("resxPath")); - } + public static string resxDir => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CRS("resxPath")); #endregion Public Properties diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index 6cd9523d..cc69ed8d 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -2,6 +2,7 @@ using EgwProxy.MultiCncLib.CNC; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using NLog; diff --git a/IOB-WIN-FILE/IobFile/FileEurom63.cs b/IOB-WIN-FILE/IobFile/FileEurom63.cs index de92a7e7..9db34630 100644 --- a/IOB-WIN-FILE/IobFile/FileEurom63.cs +++ b/IOB-WIN-FILE/IobFile/FileEurom63.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Protocols; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs index 87ecc819..2d5fc113 100644 --- a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs +++ b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs @@ -3,6 +3,7 @@ using EgwProxy.SqlDb.DbModels; #endif using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 29da454e..68eec222 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -6122,7 +6122,7 @@ namespace IOB_WIN_FORM.Iob lgInfo("loadMemConf.04"); try { - memMap = JsonConvert.DeserializeObject(jsonData); + memMap = JsonDeserialize(jsonData); } catch (Exception exc) { diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index 33c87f78..c53e2ed4 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Diagnostics; diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 744e92ca..1d710b31 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index 765e4011..d5ca203b 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index c605c896..311da6dd 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using MTConnect.Clients; diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index e8a34f85..6baa06d5 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index e3bfc4e1..4dcc7518 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.DataModel; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 6aa48741..86382e4c 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -2,6 +2,7 @@ using EgwProxy.OsaiCncLib; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using System; diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index 440a7c25..b0c8fe7a 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; using S7.Net; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs index 7afc1b42..c6de9d0d 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs index 9692d257..7f9cfb8f 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs index 3beefdde..e53c940e 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-WS/IobWs/Citizen.cs b/IOB-WIN-WS/IobWs/Citizen.cs index 061084e2..a0274c52 100644 --- a/IOB-WIN-WS/IobWs/Citizen.cs +++ b/IOB-WIN-WS/IobWs/Citizen.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-WS/IobWs/Gomba.cs b/IOB-WIN-WS/IobWs/Gomba.cs index 68547ad5..0144c208 100644 --- a/IOB-WIN-WS/IobWs/Gomba.cs +++ b/IOB-WIN-WS/IobWs/Gomba.cs @@ -1,6 +1,7 @@ using EgwProxy.Gomba.GombaServ; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; using System; diff --git a/IOB-WIN-WS/IobWs/IcoelSoap.cs b/IOB-WIN-WS/IobWs/IcoelSoap.cs index 7c357b9c..9fd720e0 100644 --- a/IOB-WIN-WS/IobWs/IcoelSoap.cs +++ b/IOB-WIN-WS/IobWs/IcoelSoap.cs @@ -2,6 +2,7 @@ using EgwProxy.Icoel.SizerService; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-WS/IobWs/RestBase.cs b/IOB-WIN-WS/IobWs/RestBase.cs index c7004071..931343c0 100644 --- a/IOB-WIN-WS/IobWs/RestBase.cs +++ b/IOB-WIN-WS/IobWs/RestBase.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; using RestSharp; From f34b68f2e047f3890c1af7653be13579348f259e Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 19:56:49 +0200 Subject: [PATCH 11/39] Altro spostamento object (in blocco, da spaccare) --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 2 +- IOB-UT-NEXT/{ => Objects}/Objects.cs | 2 +- IOB-UT-NEXT/Services/Cache/RedisIobCache.cs | 3 +- IOB-UT-NEXT/Services/Protocols/Eurom63.cs | 3 +- IOB-UT-NEXT/iobRefreshedEventArgs.cs | 3 +- IOB-WIN-FANUC/Iob/Fanuc.cs | 1 + IOB-WIN-FILE/IobFile/FileEurom63.cs | 1 + IOB-WIN-FILE/IobFile/FileGen.cs | 1 + IOB-WIN-FILE/IobFile/IobFileSoitaab.cs | 1 + IOB-WIN-FORM/AdapterForm.cs | 1 + IOB-WIN-FORM/Iob/BaseObj.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 3 +- IOB-WIN-FORM/Iob/PingWatchDog.cs | 1 + IOB-WIN-FORM/Iob/Simula.cs | 1 + IOB-WIN-FTP/Iob/Ftp.cs | 1 + IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 1 + IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 2 + IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs | 1 + IOB-WIN-OMRON/Iob/Omron.cs | 40 +------------------ IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 1 + IOB-WIN-OSAI/Iob/OSAI.cs | 1 + IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 1 + IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs | 1 + IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs | 1 + IOB-WIN-SQL/Iob/IobFileSoitaab.cs | 2 + IOB-WIN-SQL/IobSql/IcoelDb.cs | 2 + IOB-WIN-SQL/IobSql/SqlServLantek.cs | 2 + IOB-WIN-SQL/IobSql/SqlServPama.cs | 2 + IOB-WIN-WS/IobWs/Citizen.cs | 1 + IOB-WIN-WS/IobWs/EmmegiFPW.cs | 1 + IOB-WIN-WS/IobWs/Gomba.cs | 1 + IOB-WIN-WS/IobWs/IcoelSoap.cs | 1 + IOB-WIN-WS/IobWs/RestBase.cs | 1 + 35 files changed, 44 insertions(+), 45 deletions(-) rename IOB-UT-NEXT/{ => Objects}/Objects.cs (99%) diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 2c5ed3af..edb5f975 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -220,7 +220,7 @@ - + diff --git a/IOB-UT-NEXT/Objects.cs b/IOB-UT-NEXT/Objects/Objects.cs similarity index 99% rename from IOB-UT-NEXT/Objects.cs rename to IOB-UT-NEXT/Objects/Objects.cs index f7ab65e4..5a2ca465 100644 --- a/IOB-UT-NEXT/Objects.cs +++ b/IOB-UT-NEXT/Objects/Objects.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace IOB_UT_NEXT +namespace IOB_UT_NEXT.Objects { /// /// informazioni di produzione diff --git a/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs b/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs index a020f48c..f0abbb2d 100644 --- a/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs +++ b/IOB-UT-NEXT/Services/Cache/RedisIobCache.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using IOB_UT_NEXT.Objects; +using Newtonsoft.Json; using StackExchange.Redis; using System; using System.Collections.Generic; diff --git a/IOB-UT-NEXT/Services/Protocols/Eurom63.cs b/IOB-UT-NEXT/Services/Protocols/Eurom63.cs index f5bf860f..abe7e751 100644 --- a/IOB-UT-NEXT/Services/Protocols/Eurom63.cs +++ b/IOB-UT-NEXT/Services/Protocols/Eurom63.cs @@ -1,4 +1,5 @@ -using System; +using IOB_UT_NEXT.Objects; +using System; using System.Collections.Generic; namespace IOB_UT_NEXT.Services.Protocols diff --git a/IOB-UT-NEXT/iobRefreshedEventArgs.cs b/IOB-UT-NEXT/iobRefreshedEventArgs.cs index 7b7b9d4a..abd513f5 100644 --- a/IOB-UT-NEXT/iobRefreshedEventArgs.cs +++ b/IOB-UT-NEXT/iobRefreshedEventArgs.cs @@ -1,4 +1,5 @@ -using System; +using IOB_UT_NEXT.Objects; +using System; namespace IOB_UT_NEXT { diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index cc69ed8d..14716a33 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -2,6 +2,7 @@ using EgwProxy.MultiCncLib.CNC; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-FILE/IobFile/FileEurom63.cs b/IOB-WIN-FILE/IobFile/FileEurom63.cs index 9db34630..8dda8c5b 100644 --- a/IOB-WIN-FILE/IobFile/FileEurom63.cs +++ b/IOB-WIN-FILE/IobFile/FileEurom63.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Protocols; using MapoSDK; diff --git a/IOB-WIN-FILE/IobFile/FileGen.cs b/IOB-WIN-FILE/IobFile/FileGen.cs index 3cc15258..928ffcf4 100644 --- a/IOB-WIN-FILE/IobFile/FileGen.cs +++ b/IOB-WIN-FILE/IobFile/FileGen.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs index 2d5fc113..49e8a916 100644 --- a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs +++ b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs @@ -3,6 +3,7 @@ using EgwProxy.SqlDb.DbModels; #endif using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 594f2ab3..7e67bab7 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Base; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Monitoring; diff --git a/IOB-WIN-FORM/Iob/BaseObj.cs b/IOB-WIN-FORM/Iob/BaseObj.cs index 1fb37ed3..d5800620 100644 --- a/IOB-WIN-FORM/Iob/BaseObj.cs +++ b/IOB-WIN-FORM/Iob/BaseObj.cs @@ -1,4 +1,5 @@ using IOB_UT_NEXT; +using IOB_UT_NEXT.Objects; using NLog; using System; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 68eec222..dbccbe43 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; @@ -6189,7 +6190,7 @@ namespace IOB_WIN_FORM.Iob var rawListPODL = await HttpService.CallUrlAsync(urlGetNextPODL); if (!string.IsNullOrEmpty(rawListPODL)) { - reqPOdlList = JsonConvert.DeserializeObject>(rawListPODL) ?? new List(); + reqPOdlList = JsonDeserialize>(rawListPODL) ?? new List(); } }) .GetAwaiter() diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index c53e2ed4..c1cc77e8 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; using MapoSDK; diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 1d710b31..a1d089fe 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index b8de9248..4c5a2a6b 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -3,6 +3,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Config.Special; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Utility; using MapoSDK; diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index d5ca203b..e75c3600 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index cdac1693..a24aac2a 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -2,7 +2,9 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Core; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Utility; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs index 00cc7cf9..26fa5647 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using System; using System.Globalization; using System.IO; diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index 6baa06d5..4ce1f9e2 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; @@ -62,24 +63,6 @@ namespace IOB_WIN_OMRON.Iob /// public override void processContapezzi() { - if (utils.CRB("enableContapezzi")) - { -#if false - try - { - // hard coded... !!!FARE!!! rivedere megio conf - contapezziPLC = pzCounter; - // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area) - if (cIobConf.optPar.Count > 0 && IOBConfFull.Device.PzCountMode != "") - { - } - } - catch (Exception exc) - { - lgError(exc, "Errore in contapezzi OMRON"); - } -#endif - } } /// @@ -153,27 +136,6 @@ namespace IOB_WIN_OMRON.Iob public override bool setcontapezziPLC(int newPzCount, string codTav) { bool answ = false; -#if false - // ...SE abilitato da conf IOB - if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE") - { - // scrivo valore 0 x il contapezzi - try - { - pzCounter = newPzCount; - } - catch (Exception exc) - { - lgError(exc, "Errore in SET contapezzi OMRON"); - connectionOk = false; - } - answ = true; - } - else - { - lgError("Impossibile effettuare SET contapezzi OMRON, mancanza parametro OPT:ENABLE_PZ_RESET"); - } -#endif return answ; } diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 4dcc7518..3c7913a8 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -2,6 +2,7 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.DataModel; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 86382e4c..3ca99a89 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -2,6 +2,7 @@ using EgwProxy.OsaiCncLib; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 1bc443e9..092bddad 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -3,6 +3,7 @@ using EgwProxy.Shelly.Clients; using EgwProxy.Shelly.Options; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Data; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index 5947a641..56777586 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -3,6 +3,7 @@ using EgwProxy.Shelly.Clients; using EgwProxy.Shelly.Options; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Data; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index b0c8fe7a..379eaadd 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs index c6de9d0d..089890c3 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensAprochim.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs index 7f9cfb8f..dac4568b 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; diff --git a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs index b18b422d..a9bc1b2f 100644 --- a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs +++ b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs @@ -3,6 +3,8 @@ using EgwProxy.SqlDb.DbModels; #endif using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SQL/IobSql/IcoelDb.cs b/IOB-WIN-SQL/IobSql/IcoelDb.cs index adac4967..4630a9f3 100644 --- a/IOB-WIN-SQL/IobSql/IcoelDb.cs +++ b/IOB-WIN-SQL/IobSql/IcoelDb.cs @@ -1,6 +1,8 @@ using EgwProxy.Icoel; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-SQL/IobSql/SqlServLantek.cs b/IOB-WIN-SQL/IobSql/SqlServLantek.cs index 56731f8d..3dced9b0 100644 --- a/IOB-WIN-SQL/IobSql/SqlServLantek.cs +++ b/IOB-WIN-SQL/IobSql/SqlServLantek.cs @@ -2,6 +2,8 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index 3666cd3f..9ef490bd 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -2,6 +2,8 @@ using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-WS/IobWs/Citizen.cs b/IOB-WIN-WS/IobWs/Citizen.cs index a0274c52..da764e01 100644 --- a/IOB-WIN-WS/IobWs/Citizen.cs +++ b/IOB-WIN-WS/IobWs/Citizen.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-WS/IobWs/EmmegiFPW.cs b/IOB-WIN-WS/IobWs/EmmegiFPW.cs index 83292f1a..b88e895a 100644 --- a/IOB-WIN-WS/IobWs/EmmegiFPW.cs +++ b/IOB-WIN-WS/IobWs/EmmegiFPW.cs @@ -1,6 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Special; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-WS/IobWs/Gomba.cs b/IOB-WIN-WS/IobWs/Gomba.cs index 0144c208..d21984c5 100644 --- a/IOB-WIN-WS/IobWs/Gomba.cs +++ b/IOB-WIN-WS/IobWs/Gomba.cs @@ -1,6 +1,7 @@ using EgwProxy.Gomba.GombaServ; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; diff --git a/IOB-WIN-WS/IobWs/IcoelSoap.cs b/IOB-WIN-WS/IobWs/IcoelSoap.cs index 9fd720e0..d79c69ba 100644 --- a/IOB-WIN-WS/IobWs/IcoelSoap.cs +++ b/IOB-WIN-WS/IobWs/IcoelSoap.cs @@ -2,6 +2,7 @@ using EgwProxy.Icoel.SizerService; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; diff --git a/IOB-WIN-WS/IobWs/RestBase.cs b/IOB-WIN-WS/IobWs/RestBase.cs index 931343c0..aff50844 100644 --- a/IOB-WIN-WS/IobWs/RestBase.cs +++ b/IOB-WIN-WS/IobWs/RestBase.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using Newtonsoft.Json; From fd5cd9048bbdb24861a5b1088357dbceb4777e16 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 21 May 2026 20:05:29 +0200 Subject: [PATCH 12/39] Split oggetti da classe master --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 21 +- IOB-UT-NEXT/Objects/CachedInt.cs | 17 + IOB-UT-NEXT/Objects/CachedString.cs | 18 + IOB-UT-NEXT/Objects/DynDataItem.cs | 37 ++ IOB-UT-NEXT/Objects/EVData.cs | 33 + IOB-UT-NEXT/Objects/Endian.cs | 38 ++ IOB-UT-NEXT/Objects/FtpTaskList.cs | 13 + IOB-UT-NEXT/Objects/GenLogRow.cs | 15 + IOB-UT-NEXT/Objects/GenTaskList.cs | 13 + IOB-UT-NEXT/Objects/MonitoredItemsConf.cs | 18 + IOB-UT-NEXT/Objects/Objects.cs | 701 ---------------------- IOB-UT-NEXT/Objects/ProdBatchData.cs | 16 + IOB-UT-NEXT/Objects/ServerMpStatus.cs | 29 + IOB-UT-NEXT/Objects/StatusItem.cs | 17 + IOB-UT-NEXT/Objects/TimeRec.cs | 78 +++ IOB-UT-NEXT/Objects/TimingData.cs | 63 ++ IOB-UT-NEXT/Objects/VCData.cs | 45 ++ IOB-UT-NEXT/Objects/newDisplayData.cs | 76 +++ IOB-UT-NEXT/Objects/otherData.cs | 37 ++ IOB-UT-NEXT/Objects/prodData.cs | 22 + IOB-UT-NEXT/Objects/sampleVect.cs | 177 ++++++ IOB-UT-NEXT/Objects/srvData.cs | 16 + IOB-WIN-FORM/Iob/Generic.cs | 4 +- 23 files changed, 800 insertions(+), 704 deletions(-) create mode 100644 IOB-UT-NEXT/Objects/CachedInt.cs create mode 100644 IOB-UT-NEXT/Objects/CachedString.cs create mode 100644 IOB-UT-NEXT/Objects/DynDataItem.cs create mode 100644 IOB-UT-NEXT/Objects/EVData.cs create mode 100644 IOB-UT-NEXT/Objects/Endian.cs create mode 100644 IOB-UT-NEXT/Objects/FtpTaskList.cs create mode 100644 IOB-UT-NEXT/Objects/GenLogRow.cs create mode 100644 IOB-UT-NEXT/Objects/GenTaskList.cs create mode 100644 IOB-UT-NEXT/Objects/MonitoredItemsConf.cs delete mode 100644 IOB-UT-NEXT/Objects/Objects.cs create mode 100644 IOB-UT-NEXT/Objects/ProdBatchData.cs create mode 100644 IOB-UT-NEXT/Objects/ServerMpStatus.cs create mode 100644 IOB-UT-NEXT/Objects/StatusItem.cs create mode 100644 IOB-UT-NEXT/Objects/TimeRec.cs create mode 100644 IOB-UT-NEXT/Objects/TimingData.cs create mode 100644 IOB-UT-NEXT/Objects/VCData.cs create mode 100644 IOB-UT-NEXT/Objects/newDisplayData.cs create mode 100644 IOB-UT-NEXT/Objects/otherData.cs create mode 100644 IOB-UT-NEXT/Objects/prodData.cs create mode 100644 IOB-UT-NEXT/Objects/sampleVect.cs create mode 100644 IOB-UT-NEXT/Objects/srvData.cs diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index edb5f975..12a9b114 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -156,6 +156,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -220,7 +240,6 @@ - diff --git a/IOB-UT-NEXT/Objects/CachedInt.cs b/IOB-UT-NEXT/Objects/CachedInt.cs new file mode 100644 index 00000000..3226847d --- /dev/null +++ b/IOB-UT-NEXT/Objects/CachedInt.cs @@ -0,0 +1,17 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Cache a tempo valori INT + /// + public class CachedInt + { + #region Public Properties + + public DateTime ValidUntil { get; set; } = DateTime.Now; + public int Value { get; set; } = 0; + + #endregion Public Properties + } +} diff --git a/IOB-UT-NEXT/Objects/CachedString.cs b/IOB-UT-NEXT/Objects/CachedString.cs new file mode 100644 index 00000000..944834b9 --- /dev/null +++ b/IOB-UT-NEXT/Objects/CachedString.cs @@ -0,0 +1,18 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Cache a tempo valori String + /// + public class CachedString + { + #region Public Properties + + public DateTime ValidUntil { get; set; } = DateTime.Now; + public string Value { get; set; } = ""; + + #endregion Public Properties + } + +} diff --git a/IOB-UT-NEXT/Objects/DynDataItem.cs b/IOB-UT-NEXT/Objects/DynDataItem.cs new file mode 100644 index 00000000..724f3d75 --- /dev/null +++ b/IOB-UT-NEXT/Objects/DynDataItem.cs @@ -0,0 +1,37 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe conf x item DynData + /// + public class DynDataItem + { + #region Public Fields + + /// + /// DataOra scadenza invio forzato + /// + public DateTime DTScad = DateTime.Now; + + #endregion Public Fields + + #region Public Properties + + /// + /// Valore effettivo da salvare + /// + public string actVal { get; set; } = ""; + + public string func { get; set; } = ""; + public string key { get; set; } = ""; + public int maxVal { get; set; } + public int minVal { get; set; } + public string name { get; set; } = ""; + public int sPeriod { get; set; } = 60; + public string unit { get; set; } = ""; + public string val { get; set; } = ""; + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/Objects/EVData.cs b/IOB-UT-NEXT/Objects/EVData.cs new file mode 100644 index 00000000..92a0b7af --- /dev/null +++ b/IOB-UT-NEXT/Objects/EVData.cs @@ -0,0 +1,33 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Configurazione per Eventi/Variabili + /// + public class EVData + { + #region Public Fields + + /// + /// DataOra scadenza invio forzato + /// + public DateTime DTScad = DateTime.Now; + + #endregion Public Fields + + #region Public Properties + + /// + /// Unità di misura + /// + public string UM { get; set; } = "num"; + + /// + /// Valore salvato + /// + public string Val { get; set; } = ""; + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/Objects/Endian.cs b/IOB-UT-NEXT/Objects/Endian.cs new file mode 100644 index 00000000..4fe43678 --- /dev/null +++ b/IOB-UT-NEXT/Objects/Endian.cs @@ -0,0 +1,38 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Gestione Endianness + /// + public static class Endian + { + #region Public Methods + + /// + /// Scambia MSB/LSB per 16bit + /// + /// + /// + public static UInt16 SwapUInt16(UInt16 inValue) + { + return (UInt16)(((inValue & 0xff00) >> 8) | + ((inValue & 0x00ff) << 8)); + } + + /// + /// Scambia MSB/LSB per 32bit + /// + /// + /// + public static UInt32 SwapUInt32(UInt32 inValue) + { + return ((inValue & 0xff000000) >> 24) | + ((inValue & 0x00ff0000) >> 8) | + ((inValue & 0x0000ff00) << 8) | + ((inValue & 0x000000ff) << 24); + } + + #endregion Public Methods + } +} diff --git a/IOB-UT-NEXT/Objects/FtpTaskList.cs b/IOB-UT-NEXT/Objects/FtpTaskList.cs new file mode 100644 index 00000000..27522c59 --- /dev/null +++ b/IOB-UT-NEXT/Objects/FtpTaskList.cs @@ -0,0 +1,13 @@ +using IOB_UT_NEXT.Config.Special; +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Elenco task di tipo FTP + /// + public class FtpTaskList + { + public List ListTask { get; set; } = new List(); + } +} diff --git a/IOB-UT-NEXT/Objects/GenLogRow.cs b/IOB-UT-NEXT/Objects/GenLogRow.cs new file mode 100644 index 00000000..0635cb0f --- /dev/null +++ b/IOB-UT-NEXT/Objects/GenLogRow.cs @@ -0,0 +1,15 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Generico evento log + /// + public class GenLogRow + { + public DateTime dtRif { get; set; } = DateTime.Now; + + public string valString { get; set; } = ""; + } + +} diff --git a/IOB-UT-NEXT/Objects/GenTaskList.cs b/IOB-UT-NEXT/Objects/GenTaskList.cs new file mode 100644 index 00000000..37739f36 --- /dev/null +++ b/IOB-UT-NEXT/Objects/GenTaskList.cs @@ -0,0 +1,13 @@ +using IOB_UT_NEXT.Config; +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Elenco task generici + /// + public class GenTaskList + { + public List ListTask { get; set; } = new List(); + } +} diff --git a/IOB-UT-NEXT/Objects/MonitoredItemsConf.cs b/IOB-UT-NEXT/Objects/MonitoredItemsConf.cs new file mode 100644 index 00000000..8cfd7717 --- /dev/null +++ b/IOB-UT-NEXT/Objects/MonitoredItemsConf.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Elenco oggetti del monitoraggio (DynData, Status) per WPS + /// + public class MonitoredItemsConf + { + #region Public Properties + + public List DynData { get; set; } + public srvData SrvData { get; set; } + public List Status { get; set; } + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/Objects/Objects.cs b/IOB-UT-NEXT/Objects/Objects.cs deleted file mode 100644 index 5a2ca465..00000000 --- a/IOB-UT-NEXT/Objects/Objects.cs +++ /dev/null @@ -1,701 +0,0 @@ -using IOB_UT_NEXT.Config; -using IOB_UT_NEXT.Config.Special; -using MapoSDK; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IOB_UT_NEXT.Objects -{ - /// - /// informazioni di produzione - /// - public struct prodData - { - #region Public Fields - - public int AccTime; - public bool EmrStop; - public string FuncMode; - public string MessageCode; - public string MessageText; - public string Operator; - - public int Power; - public bool Status; - - #endregion Public Fields - } - - /// - /// Gestione Endianness - /// - public static class Endian - { - #region Public Methods - - /// - /// Scambia MSB/LSB per 16bit - /// - /// - /// - public static UInt16 SwapUInt16(UInt16 inValue) - { - return (UInt16)(((inValue & 0xff00) >> 8) | - ((inValue & 0x00ff) << 8)); - } - - /// - /// Scambia MSB/LSB per 32bit - /// - /// - /// - public static UInt32 SwapUInt32(UInt32 inValue) - { - return ((inValue & 0xff000000) >> 24) | - ((inValue & 0x00ff0000) >> 8) | - ((inValue & 0x0000ff00) << 8) | - ((inValue & 0x000000ff) << 24); - } - - #endregion Public Methods - } - - /// - /// Elenco task di tipo FTP - /// - public class FtpTaskList - { - public List ListTask { get; set; } = new List(); - } - - /// - /// Elenco task generici - /// - public class GenTaskList - { - public List ListTask { get; set; } = new List(); - } - - /// - /// Gestione dati di timing - /// - public static class TimingData - { - #region Public Fields - - public static List results = new List(); - - #endregion Public Fields - - #region Public Methods - - /// - /// aggiorno vettore aggiungendo risultato - /// - /// Codice chiamante - /// Codice da registrare (univoco con chiamante) - /// Tempo esecuzione in ticks - public static void addResult(string caller, string codice, long ticks) - { - if (results.Count == 0) - { - results.Add(new TimeRec(caller, codice, ticks)); - } - int indice = -1; - for (int i = 0; i < results.Count; i++) - { - // se il codice è quello cercato... - if (results[i].codCall == codice && results[i].classCall == caller) - { - indice = i; - } - } - // se c'è aggiorno... - if (indice >= 0) - { - results[indice].numCall++; - results[indice].totMsec = results[indice].totMsec.Add(new TimeSpan(ticks)); - } - // altrimenti aggiungo... - else - { - results.Add(new TimeRec(caller, codice, ticks)); - } - } - - /// - /// Resetta i dati registrati (ad avvio adapter...) - /// - public static void resetData() - { - results = new List(); - } - - #endregion Public Methods - } - - /// - /// Cache a tempo valori INT - /// - public class CachedInt - { - #region Public Properties - - public DateTime ValidUntil { get; set; } = DateTime.Now; - public int Value { get; set; } = 0; - - #endregion Public Properties - } - - /// - /// Cache a tempo valori String - /// - public class CachedString - { - #region Public Properties - - public DateTime ValidUntil { get; set; } = DateTime.Now; - public string Value { get; set; } = ""; - - #endregion Public Properties - } - - /// - /// Classe conf x item DynData - /// - public class DynDataItem - { - #region Public Fields - - /// - /// DataOra scadenza invio forzato - /// - public DateTime DTScad = DateTime.Now; - - #endregion Public Fields - - #region Public Properties - - /// - /// Valore effettivo da salvare - /// - public string actVal { get; set; } = ""; - - public string func { get; set; } = ""; - public string key { get; set; } = ""; - public int maxVal { get; set; } - public int minVal { get; set; } - public string name { get; set; } = ""; - public int sPeriod { get; set; } = 60; - public string unit { get; set; } = ""; - public string val { get; set; } = ""; - - #endregion Public Properties - } - - /// - /// Configurazione per Eventi/Variabili - /// - public class EVData - { - #region Public Fields - - /// - /// DataOra scadenza invio forzato - /// - public DateTime DTScad = DateTime.Now; - - #endregion Public Fields - - #region Public Properties - - /// - /// Unità di misura - /// - public string UM { get; set; } = "num"; - - /// - /// Valore salvato - /// - public string Val { get; set; } = ""; - - #endregion Public Properties - } - - /// - /// Elenco oggetti del monitoraggio (DynData, Status) per WPS - /// - public class MonitoredItemsConf - { - #region Public Properties - - public List DynData { get; set; } - public srvData SrvData { get; set; } - public List Status { get; set; } - - #endregion Public Properties - } - - /// - /// Classe che contiene tutte le NUOVE informazioni da aggiornare sulla form - /// - public class newDisplayData - { - #region Public Properties - - /// - /// Oggetto COUTNER generico (pezzi, portata...) - /// - public int counter { get; set; } = -9999; - - /// - /// Bitmap attuale segnali letti - /// - public string currBitmap { get; set; } = ""; - - /// - /// Verifica se contenga valori (NON default/empty) - /// - public bool hasData - { - get - { - bool answ = false; - // true se qualcosa NON E' come default - if (!string.IsNullOrWhiteSpace(newInData) || !string.IsNullOrWhiteSpace(newSignalData) || !string.IsNullOrWhiteSpace(newFLogData) || !string.IsNullOrWhiteSpace(newUrlCallData) || !string.IsNullOrWhiteSpace(newLiveLogData) || counter > -9999 || !string.IsNullOrWhiteSpace(currBitmap) || semIn != Semaforo.ND || semOut != Semaforo.ND) - { - answ = true; - } - return answ; - } - } - - /// - /// Dati tipo FluxLog - /// - public string newFLogData { get; set; } = ""; - - /// - /// Dati tipo IN (RAW) - /// - public string newInData { get; set; } = ""; - - /// - /// Dati tipo LiveLog - /// - public string newLiveLogData { get; set; } = ""; - - /// - /// Dati tipo Signal - /// - public string newSignalData { get; set; } = ""; - - /// - /// Dati tipo UrlCall - /// - public string newUrlCallData { get; set; } = ""; - - /// - /// Stato semaforo IN verso PLC - /// - public Semaforo semIn { get; set; } = Semaforo.ND; - - /// - /// Stato semaforo OUT verso MES - /// - public Semaforo semOut { get; set; } = Semaforo.ND; - - #endregion Public Properties - } - - /// - /// Dato generico (per decodifica) - /// - public class otherData - { - #region Public Fields - - public string codNum; - public string dataType; - public string memAddr; - public string varName; - - #endregion Public Fields - - #region Public Constructors - - public otherData() - { - codNum = ""; - memAddr = ""; - varName = ""; - dataType = ""; - } - - public otherData(string _codNum, string _memAddr, string _varName, string _dataType) - { - codNum = _codNum; - memAddr = _memAddr; - varName = _varName; - dataType = _dataType; - } - - #endregion Public Constructors - } - - /// - /// Classe gestione valori campionati su periodo - /// - public class sampleVect - { - #region Public Constructors - - /// - /// Inizializzo l'oggetto - /// - public sampleVect() - { - // init valori default... - windSize = baseUtils.CRI("countWindSize") > 0 ? baseUtils.CRI("countWindSize") : 60; - lTime = new List(); - lVal = new List(); - } - - #endregion Public Constructors - - #region Public Properties - - /// - /// Calcola il valore mediano... - /// - public double vcMedian - { - get - { - double answ = 0; - // restituisce la mediana SE valida, altrimenti null... - if (numElem > 2 && flWindSize > windSize) - { - try - { - // calcolo mediana! - //answ = Statistics.Median(lVal.ToArray()); - - // rif: https://blogs.msmvps.com/deborahk/linq-mean-median-and-mode/ - var sortedNumbers = lVal.OrderBy(n => n); - int numCount = lVal.Count; - int indice50 = lVal.Count / 2; - if ((numCount % 2) == 0) - { - answ = ((sortedNumbers.ElementAt(indice50) + sortedNumbers.ElementAt(indice50 - 1)) / 2); - } - else - { - answ = sortedNumbers.ElementAt(indice50); - } - } - catch - { } - } - return answ; - } - } - - /// - /// Verifica se la vc sia valida (ovvero almeno 2 valori e intervallo > window richiesta) - /// - public bool vcValid - { - get - { - return (flWindSize > windSize && numElem > 1); - } - } - - #endregion Public Properties - - #region Public Methods - - /// - /// Aggiunge un valore alla serie ed eventualmente elimina i valori superflui a garantirne - /// una finestra temporale valida - /// - /// - /// - public void addValue(DateTime tempo, int valore) - { - lTime.Add(tempo); - lVal.Add(valore); - // verifico se siano da accorciare le serie... ovvero i 2 intervalli ENTRAMBI sono - // superiori al periodo minimo (in tal caso riduco.. - while (flWindSize > windSize && slWindSize > windSize) - { - // elimino i 2 valori + vecchi - lTime.RemoveAt(0); - lVal.RemoveAt(0); - // ora ricontrollo... - } - } - - #endregion Public Methods - - #region Protected Fields - - /// - /// vettore valori temporali della serie - /// - protected List lTime; - - /// - /// vettore valori puntuali della serie - /// - protected List lVal; - - /// - /// Dimensione finestra di campionamento (secondi) - /// - protected int windSize; - - #endregion Protected Fields - - #region Protected Properties - - /// - /// Verifica ampiezza finestra valori First-Last - /// - protected double flWindSize - { - get - { - double answ = 0; - if (numElem > 1) - { - answ = lTime.Last().Subtract(lTime[0]).TotalSeconds; - } - return answ; - } - } - - /// - /// Conteggio elementi - /// - protected int numElem - { - get - { - int answ = 0; - try - { - answ = lTime.Count; - } - catch - { } - return answ; - } - } - - /// - /// Verifica ampiezza finestra valori Second-Last - /// - protected double slWindSize - { - get - { - double answ = 0; - if (numElem > 2) // altrimenti SE non ne ho almeno 3 NON posso avere secondo/ultimo... - { - answ = lTime.Last().Subtract(lTime[1]).TotalSeconds; - } - return answ; - } - } - - #endregion Protected Properties - } - - /// - /// Classe x descrivere status server MP - /// - public class ServerMpStatus - { - #region Public Properties - - /// - /// IP server - /// - public string IP { get; set; } - - /// - /// DataOra ultima comunicazione OUT (con MP Server) - /// - public DateTime lastUpdate { get; set; } = DateTime.Now.AddDays(-1); - - /// - /// Status del server - /// - public bool online { get; set; } = false; - - #endregion Public Properties - } - - /// - /// Classe conf server html - /// - public class srvData - { - #region Public Properties - - public string baseUri { get; set; } = ""; - public string driverName { get; set; } = ""; - - #endregion Public Properties - } - - /// - /// Classe conf x decodifica status - /// - public class StatusItem : DynDataItem - { - #region Public Fields - - public Dictionary codeMapping; - - #endregion Public Fields - } - - /// - /// Oggetto timing x archiviazione dati perfomances - /// - public class TimeRec - { - #region Public Fields - - /// - /// Classe chiamante della funzione (es codice univoco IOB) - /// - public string classCall; - - /// - /// Codice univoco chiamata: tipo R4 (read 4 byte), W2 (write 2 Byte) - /// - public string codCall; - - /// - /// Num chiamate totale - /// - public int numCall; - - /// - /// Totale Msec accumulati - /// - public TimeSpan totMsec; - - #endregion Public Fields - - #region Public Constructors - - /// - /// Classe record timing - /// - public TimeRec() - { - codCall = ""; - numCall = 0; - totMsec = new TimeSpan(0); - } - - /// - /// Classe record timing - /// - /// - /// - /// - public TimeRec(string caller, string codice, long nTicks) - { - classCall = caller; - codCall = codice; - numCall = 1; - totMsec = new TimeSpan(nTicks); - } - - #endregion Public Constructors - - #region Public Properties - - /// - /// Tempo medio chiamata - /// - public double avgMsec - { - get - { - return totMsec.TotalMilliseconds / numCall; - } - } - - #endregion Public Properties - } - - /// - /// Configurazione per Variabili Casuali - /// - public class VCData - { - #region Public Fields - - /// - /// Array dati per calcolo - /// - public List dataArray; - - /// - /// DataOra inizio periodo di elaborazione - /// - public DateTime DTStart; - - #endregion Public Fields - - #region Public Properties - - /// - /// Tipologia di funzione da applicare - /// - public VC_func Funzione { get; set; } = VC_func.POINT; - - /// - /// Periodo di riferimento - /// - public int Period { get; set; } = 60; - - /// - /// UM parametro, impiegato anche x conversione (es epoch --> datetime) - /// - public string UM { get; set; } = ""; - - #endregion Public Properties - } - - /// - /// Generico evento log - /// - public class GenLogRow - { - public DateTime dtRif { get; set; } = DateTime.Now; - - public string valString { get; set; } = ""; - } - - - /// - /// Informazioni generiche su batch produzione rilevati - /// - public class ProdBatchData - { - public DateTime dtStart { get; set; } = DateTime.Now; - public DateTime? dtEnd { get; set; } = null; - - public string codArt { get; set; } = ""; - public int numPz { get; set; } = 0; - } -} \ No newline at end of file diff --git a/IOB-UT-NEXT/Objects/ProdBatchData.cs b/IOB-UT-NEXT/Objects/ProdBatchData.cs new file mode 100644 index 00000000..a888b7c9 --- /dev/null +++ b/IOB-UT-NEXT/Objects/ProdBatchData.cs @@ -0,0 +1,16 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Informazioni generiche su batch produzione rilevati + /// + public class ProdBatchData + { + public DateTime dtStart { get; set; } = DateTime.Now; + public DateTime? dtEnd { get; set; } = null; + + public string codArt { get; set; } = ""; + public int numPz { get; set; } = 0; + } +} diff --git a/IOB-UT-NEXT/Objects/ServerMpStatus.cs b/IOB-UT-NEXT/Objects/ServerMpStatus.cs new file mode 100644 index 00000000..0c32d47e --- /dev/null +++ b/IOB-UT-NEXT/Objects/ServerMpStatus.cs @@ -0,0 +1,29 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe x descrivere status server MP + /// + public class ServerMpStatus + { + #region Public Properties + + /// + /// IP server + /// + public string IP { get; set; } + + /// + /// DataOra ultima comunicazione OUT (con MP Server) + /// + public DateTime lastUpdate { get; set; } = DateTime.Now.AddDays(-1); + + /// + /// Status del server + /// + public bool online { get; set; } = false; + + #endregion Public Properties + } +} diff --git a/IOB-UT-NEXT/Objects/StatusItem.cs b/IOB-UT-NEXT/Objects/StatusItem.cs new file mode 100644 index 00000000..05227535 --- /dev/null +++ b/IOB-UT-NEXT/Objects/StatusItem.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe conf x decodifica status + /// + public class StatusItem : DynDataItem + { + #region Public Fields + + public Dictionary codeMapping; + + #endregion Public Fields + } + +} diff --git a/IOB-UT-NEXT/Objects/TimeRec.cs b/IOB-UT-NEXT/Objects/TimeRec.cs new file mode 100644 index 00000000..b736111d --- /dev/null +++ b/IOB-UT-NEXT/Objects/TimeRec.cs @@ -0,0 +1,78 @@ +using System; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Oggetto timing x archiviazione dati perfomances + /// + public class TimeRec + { + #region Public Fields + + /// + /// Classe chiamante della funzione (es codice univoco IOB) + /// + public string classCall; + + /// + /// Codice univoco chiamata: tipo R4 (read 4 byte), W2 (write 2 Byte) + /// + public string codCall; + + /// + /// Num chiamate totale + /// + public int numCall; + + /// + /// Totale Msec accumulati + /// + public TimeSpan totMsec; + + #endregion Public Fields + + #region Public Constructors + + /// + /// Classe record timing + /// + public TimeRec() + { + codCall = ""; + numCall = 0; + totMsec = new TimeSpan(0); + } + + /// + /// Classe record timing + /// + /// + /// + /// + public TimeRec(string caller, string codice, long nTicks) + { + classCall = caller; + codCall = codice; + numCall = 1; + totMsec = new TimeSpan(nTicks); + } + + #endregion Public Constructors + + #region Public Properties + + /// + /// Tempo medio chiamata + /// + public double avgMsec + { + get + { + return totMsec.TotalMilliseconds / numCall; + } + } + + #endregion Public Properties + } + +} diff --git a/IOB-UT-NEXT/Objects/TimingData.cs b/IOB-UT-NEXT/Objects/TimingData.cs new file mode 100644 index 00000000..ac8c612b --- /dev/null +++ b/IOB-UT-NEXT/Objects/TimingData.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Gestione dati di timing + /// + public static class TimingData + { + #region Public Fields + + public static List results = new List(); + + #endregion Public Fields + + #region Public Methods + + /// + /// aggiorno vettore aggiungendo risultato + /// + /// Codice chiamante + /// Codice da registrare (univoco con chiamante) + /// Tempo esecuzione in ticks + public static void addResult(string caller, string codice, long ticks) + { + if (results.Count == 0) + { + results.Add(new TimeRec(caller, codice, ticks)); + } + int indice = -1; + for (int i = 0; i < results.Count; i++) + { + // se il codice è quello cercato... + if (results[i].codCall == codice && results[i].classCall == caller) + { + indice = i; + } + } + // se c'è aggiorno... + if (indice >= 0) + { + results[indice].numCall++; + results[indice].totMsec = results[indice].totMsec.Add(new TimeSpan(ticks)); + } + // altrimenti aggiungo... + else + { + results.Add(new TimeRec(caller, codice, ticks)); + } + } + + /// + /// Resetta i dati registrati (ad avvio adapter...) + /// + public static void resetData() + { + results = new List(); + } + + #endregion Public Methods + } +} diff --git a/IOB-UT-NEXT/Objects/VCData.cs b/IOB-UT-NEXT/Objects/VCData.cs new file mode 100644 index 00000000..1ef149a8 --- /dev/null +++ b/IOB-UT-NEXT/Objects/VCData.cs @@ -0,0 +1,45 @@ +using MapoSDK; +using System; +using System.Collections.Generic; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Configurazione per Variabili Casuali + /// + public class VCData + { + #region Public Fields + + /// + /// Array dati per calcolo + /// + public List dataArray; + + /// + /// DataOra inizio periodo di elaborazione + /// + public DateTime DTStart; + + #endregion Public Fields + + #region Public Properties + + /// + /// Tipologia di funzione da applicare + /// + public VC_func Funzione { get; set; } = VC_func.POINT; + + /// + /// Periodo di riferimento + /// + public int Period { get; set; } = 60; + + /// + /// UM parametro, impiegato anche x conversione (es epoch --> datetime) + /// + public string UM { get; set; } = ""; + + #endregion Public Properties + } +} diff --git a/IOB-UT-NEXT/Objects/newDisplayData.cs b/IOB-UT-NEXT/Objects/newDisplayData.cs new file mode 100644 index 00000000..92abd646 --- /dev/null +++ b/IOB-UT-NEXT/Objects/newDisplayData.cs @@ -0,0 +1,76 @@ +using MapoSDK; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe che contiene tutte le NUOVE informazioni da aggiornare sulla form + /// + public class newDisplayData + { + #region Public Properties + + /// + /// Oggetto COUTNER generico (pezzi, portata...) + /// + public int counter { get; set; } = -9999; + + /// + /// Bitmap attuale segnali letti + /// + public string currBitmap { get; set; } = ""; + + /// + /// Verifica se contenga valori (NON default/empty) + /// + public bool hasData + { + get + { + bool answ = false; + // true se qualcosa NON E' come default + if (!string.IsNullOrWhiteSpace(newInData) || !string.IsNullOrWhiteSpace(newSignalData) || !string.IsNullOrWhiteSpace(newFLogData) || !string.IsNullOrWhiteSpace(newUrlCallData) || !string.IsNullOrWhiteSpace(newLiveLogData) || counter > -9999 || !string.IsNullOrWhiteSpace(currBitmap) || semIn != Semaforo.ND || semOut != Semaforo.ND) + { + answ = true; + } + return answ; + } + } + + /// + /// Dati tipo FluxLog + /// + public string newFLogData { get; set; } = ""; + + /// + /// Dati tipo IN (RAW) + /// + public string newInData { get; set; } = ""; + + /// + /// Dati tipo LiveLog + /// + public string newLiveLogData { get; set; } = ""; + + /// + /// Dati tipo Signal + /// + public string newSignalData { get; set; } = ""; + + /// + /// Dati tipo UrlCall + /// + public string newUrlCallData { get; set; } = ""; + + /// + /// Stato semaforo IN verso PLC + /// + public Semaforo semIn { get; set; } = Semaforo.ND; + + /// + /// Stato semaforo OUT verso MES + /// + public Semaforo semOut { get; set; } = Semaforo.ND; + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/Objects/otherData.cs b/IOB-UT-NEXT/Objects/otherData.cs new file mode 100644 index 00000000..cbd21e13 --- /dev/null +++ b/IOB-UT-NEXT/Objects/otherData.cs @@ -0,0 +1,37 @@ +namespace IOB_UT_NEXT.Objects +{ + /// + /// Dato generico (per decodifica) + /// + public class otherData + { + #region Public Fields + + public string codNum; + public string dataType; + public string memAddr; + public string varName; + + #endregion Public Fields + + #region Public Constructors + + public otherData() + { + codNum = ""; + memAddr = ""; + varName = ""; + dataType = ""; + } + + public otherData(string _codNum, string _memAddr, string _varName, string _dataType) + { + codNum = _codNum; + memAddr = _memAddr; + varName = _varName; + dataType = _dataType; + } + + #endregion Public Constructors + } +} diff --git a/IOB-UT-NEXT/Objects/prodData.cs b/IOB-UT-NEXT/Objects/prodData.cs new file mode 100644 index 00000000..12923ac5 --- /dev/null +++ b/IOB-UT-NEXT/Objects/prodData.cs @@ -0,0 +1,22 @@ +namespace IOB_UT_NEXT.Objects +{ + /// + /// informazioni di produzione + /// + public struct prodData + { + #region Public Fields + + public int AccTime; + public bool EmrStop; + public string FuncMode; + public string MessageCode; + public string MessageText; + public string Operator; + + public int Power; + public bool Status; + + #endregion Public Fields + } +} diff --git a/IOB-UT-NEXT/Objects/sampleVect.cs b/IOB-UT-NEXT/Objects/sampleVect.cs new file mode 100644 index 00000000..f5924b54 --- /dev/null +++ b/IOB-UT-NEXT/Objects/sampleVect.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe gestione valori campionati su periodo + /// + public class sampleVect + { + #region Public Constructors + + /// + /// Inizializzo l'oggetto + /// + public sampleVect() + { + // init valori default... + windSize = baseUtils.CRI("countWindSize") > 0 ? baseUtils.CRI("countWindSize") : 60; + lTime = new List(); + lVal = new List(); + } + + #endregion Public Constructors + + #region Public Properties + + /// + /// Calcola il valore mediano... + /// + public double vcMedian + { + get + { + double answ = 0; + // restituisce la mediana SE valida, altrimenti null... + if (numElem > 2 && flWindSize > windSize) + { + try + { + // calcolo mediana! + //answ = Statistics.Median(lVal.ToArray()); + + // rif: https://blogs.msmvps.com/deborahk/linq-mean-median-and-mode/ + var sortedNumbers = lVal.OrderBy(n => n); + int numCount = lVal.Count; + int indice50 = lVal.Count / 2; + if ((numCount % 2) == 0) + { + answ = ((sortedNumbers.ElementAt(indice50) + sortedNumbers.ElementAt(indice50 - 1)) / 2); + } + else + { + answ = sortedNumbers.ElementAt(indice50); + } + } + catch + { } + } + return answ; + } + } + + /// + /// Verifica se la vc sia valida (ovvero almeno 2 valori e intervallo > window richiesta) + /// + public bool vcValid + { + get + { + return (flWindSize > windSize && numElem > 1); + } + } + + #endregion Public Properties + + #region Public Methods + + /// + /// Aggiunge un valore alla serie ed eventualmente elimina i valori superflui a garantirne + /// una finestra temporale valida + /// + /// + /// + public void addValue(DateTime tempo, int valore) + { + lTime.Add(tempo); + lVal.Add(valore); + // verifico se siano da accorciare le serie... ovvero i 2 intervalli ENTRAMBI sono + // superiori al periodo minimo (in tal caso riduco.. + while (flWindSize > windSize && slWindSize > windSize) + { + // elimino i 2 valori + vecchi + lTime.RemoveAt(0); + lVal.RemoveAt(0); + // ora ricontrollo... + } + } + + #endregion Public Methods + + #region Protected Fields + + /// + /// vettore valori temporali della serie + /// + protected List lTime; + + /// + /// vettore valori puntuali della serie + /// + protected List lVal; + + /// + /// Dimensione finestra di campionamento (secondi) + /// + protected int windSize; + + #endregion Protected Fields + + #region Protected Properties + + /// + /// Verifica ampiezza finestra valori First-Last + /// + protected double flWindSize + { + get + { + double answ = 0; + if (numElem > 1) + { + answ = lTime.Last().Subtract(lTime[0]).TotalSeconds; + } + return answ; + } + } + + /// + /// Conteggio elementi + /// + protected int numElem + { + get + { + int answ = 0; + try + { + answ = lTime.Count; + } + catch + { } + return answ; + } + } + + /// + /// Verifica ampiezza finestra valori Second-Last + /// + protected double slWindSize + { + get + { + double answ = 0; + if (numElem > 2) // altrimenti SE non ne ho almeno 3 NON posso avere secondo/ultimo... + { + answ = lTime.Last().Subtract(lTime[1]).TotalSeconds; + } + return answ; + } + } + + #endregion Protected Properties + } + +} diff --git a/IOB-UT-NEXT/Objects/srvData.cs b/IOB-UT-NEXT/Objects/srvData.cs new file mode 100644 index 00000000..01c417fe --- /dev/null +++ b/IOB-UT-NEXT/Objects/srvData.cs @@ -0,0 +1,16 @@ +namespace IOB_UT_NEXT.Objects +{ + /// + /// Classe conf server html + /// + public class srvData + { + #region Public Properties + + public string baseUri { get; set; } = ""; + public string driverName { get; set; } = ""; + + #endregion Public Properties + } + +} diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index dbccbe43..18f9c730 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -6350,7 +6350,7 @@ namespace IOB_WIN_FORM.Iob string rawData = File.ReadAllText(fileItem); if (!string.IsNullOrEmpty(rawData)) { - var convData = JsonConvert.DeserializeObject>(rawData); + var convData = JsonDeserialize>(rawData); if (convData != null) { list2Send = convData; @@ -6409,7 +6409,7 @@ namespace IOB_WIN_FORM.Iob { try { - writeList = JsonConvert.DeserializeObject>(resp); + writeList = JsonDeserialize>(resp); // se ho da fare chiamo esecuzione.. if (writeList.Count > 0) { From 66912add19556bd4475f65e69ba6d0ee166ed3b1 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 06:48:03 +0200 Subject: [PATCH 13/39] Update objects in adapter non ancora sistemati --- IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs | 2 ++ IOB-WIN-MTC/Iob/MTConn.cs | 1 + IOB-WIN-NEXT/Iob/GenericNext.cs | 6 +----- IOB-WIN-WPS/IobNet/WebPageScrap.cs | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs index 8ac4eed5..950de20b 100644 --- a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs +++ b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs @@ -1,5 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index 311da6dd..f7fb6bf7 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; diff --git a/IOB-WIN-NEXT/Iob/GenericNext.cs b/IOB-WIN-NEXT/Iob/GenericNext.cs index 1cabd677..33de61ef 100644 --- a/IOB-WIN-NEXT/Iob/GenericNext.cs +++ b/IOB-WIN-NEXT/Iob/GenericNext.cs @@ -1,7 +1,4 @@ -#if false -using EgwProxy.Ftp; -#endif -using EgwProxy.MultiCncLib.App.Native; +using EgwProxy.MultiCncLib.App.Native; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_WIN_FORM; @@ -23,7 +20,6 @@ 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; diff --git a/IOB-WIN-WPS/IobNet/WebPageScrap.cs b/IOB-WIN-WPS/IobNet/WebPageScrap.cs index 84902de4..cc9013f1 100644 --- a/IOB-WIN-WPS/IobNet/WebPageScrap.cs +++ b/IOB-WIN-WPS/IobNet/WebPageScrap.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; using MapoSDK; using Newtonsoft.Json; using OpenQA.Selenium; From 83223d2dbd53d479b7f5807ae122bca34c91d128 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 10:22:25 +0200 Subject: [PATCH 14/39] Continuo pulizia metodi refusi --- IOB-UT-NEXT/Iob/BaseObj.cs | 6 ++++ IOB-UT-NEXT/Services/Data/DataSerializer.cs | 11 ++++++ IOB-WIN-FILE/IobFile/FileGen.cs | 35 ------------------- IOB-WIN-FORM/Iob/Generic.cs | 38 ++++++++++----------- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 22 ------------ 5 files changed, 36 insertions(+), 76 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index c68f4aa0..131bde02 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -4,6 +4,7 @@ using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; +using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; @@ -598,6 +599,11 @@ namespace IOB_UT_NEXT.Iob /// protected string JsonSerialize(T obj) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj); + /// + /// Serializza un oggetto in formato JSON con opzioni serializzazione. + /// + protected string JsonSerialize(T obj, Formatting reqFormat) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj, reqFormat); + /// /// Deserializza una stringa JSON in un oggetto. /// diff --git a/IOB-UT-NEXT/Services/Data/DataSerializer.cs b/IOB-UT-NEXT/Services/Data/DataSerializer.cs index 3046b9c8..ea32800b 100644 --- a/IOB-UT-NEXT/Services/Data/DataSerializer.cs +++ b/IOB-UT-NEXT/Services/Data/DataSerializer.cs @@ -23,6 +23,17 @@ namespace IOB_UT_NEXT.Services.Data }); } + /// + /// Serializza un oggetto in una stringa JSON utilizzando la cultura invariante. + /// + public static string Serialize(T obj, Formatting reqFormat) + { + if (obj == null) return null; + // Utilizzo di CultureInfo.InvariantCulture per garantire la coerenza dei formati (es. decimali) + return JsonConvert.SerializeObject(obj, reqFormat); + } + + /// /// Deserializza una stringa JSON in un oggetto del tipo specificato. /// diff --git a/IOB-WIN-FILE/IobFile/FileGen.cs b/IOB-WIN-FILE/IobFile/FileGen.cs index 928ffcf4..56ff29cf 100644 --- a/IOB-WIN-FILE/IobFile/FileGen.cs +++ b/IOB-WIN-FILE/IobFile/FileGen.cs @@ -111,41 +111,6 @@ namespace IOB_WIN_FILE.IobFile // ciclo! try { -#if false - // controllo SE il driver SIA attivo... - if (driver != null) - { - string cKey = ""; - string cVal = ""; - // IPOTESI: un UNICO oggetto decodifica status - if (monitoredItems.Status.Count == 1) - { - var item = monitoredItems.Status[0]; - // cerco elemento indicato - element = driver.FindElement(By.Id(item.val)); - cKey = element.Text; - // verifico se mancasse il mapping... - if (!item.codeMapping.ContainsKey(cKey)) - { - processUnknStatus(cKey); - } - else - { - // ora decodifico da variabile status a valore secondo impostazione "codeMapping" - cVal = item.codeMapping[cKey]; - B_input = int.Parse(cVal, System.Globalization.NumberStyles.HexNumber); - if (currDispData != null) - { - currDispData.semIn = Semaforo.SV; - } - } - } - } - else - { - lgError("Errore: driver non pronto (null)"); - } -#endif // riporto bitmap... reportRawInput(ref currDispData); } diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 18f9c730..c1d7bf58 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1825,7 +1825,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonConvert.SerializeObject(fullFlObj); + answ = JsonSerialize(fullFlObj); } catch (Exception exc) { @@ -1856,7 +1856,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonConvert.SerializeObject(fullEvObj); + answ = JsonSerialize(fullEvObj); } catch (Exception exc) { @@ -1915,7 +1915,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonConvert.SerializeObject(fullUlObj); + answ = JsonSerialize(fullUlObj); } catch (Exception exc) { @@ -1942,11 +1942,11 @@ namespace IOB_WIN_FORM.Iob string rawVal = redisMan.redGetHashField(lastSendKey, keyReq); if (!string.IsNullOrEmpty(rawVal)) { - lastSend = DataSerializer.Deserialize(rawVal); + lastSend = JsonDeserialize(rawVal); } else { - rawVal = DataSerializer.Serialize(lastSend); + rawVal = JsonSerialize(lastSend); KeyValuePair[] hashFields = new KeyValuePair[1]; hashFields[0] = new KeyValuePair(keyReq, rawVal); redisMan.redSaveHash(lastSendKey, hashFields); @@ -1962,7 +1962,7 @@ namespace IOB_WIN_FORM.Iob public bool LastSendSet(string keyReq, DateTime dtRif) { string lastSendKey = GetStatusField("LastSend"); - string rawVal = DataSerializer.Serialize(dtRif); + string rawVal = JsonSerialize(dtRif); KeyValuePair[] hashFields = new KeyValuePair[1]; hashFields[0] = new KeyValuePair(keyReq, rawVal); bool fatto = redisMan.redSaveHash(lastSendKey, hashFields); @@ -5771,7 +5771,7 @@ namespace IOB_WIN_FORM.Iob item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus)); // salvo in redis... string alarmHash = redisMan.redHash($"IOB:ALARM_STATUS:{IOBConfFull.General.FilenameIOB}:{item.memAddr}"); - string rawAlarms = JsonConvert.SerializeObject(item.alarmsState); + string rawAlarms = JsonSerialize(item.alarmsState); redisMan.setRSV(alarmHash, rawAlarms); } else @@ -6439,7 +6439,7 @@ namespace IOB_WIN_FORM.Iob // richiamo scrittura parametri su PLC plcWriteParams(ref updatedPar); // invio su cloud parametri! - string rawData = JsonConvert.SerializeObject(updatedPar); + string rawData = JsonSerialize(updatedPar); HttpService.CallUrl($"{urlUpdateWriteParams}", rawData); lgInfo($"Notifica a server scrittura {updatedPar.Count} parametri"); } @@ -6642,7 +6642,7 @@ namespace IOB_WIN_FORM.Iob // leggo contenuto XML string rawData = File.ReadAllText(recipeFile); // deserializza file XML x recuperare righe consumo - var currRecipe = XmlDataSerializer.Deserialize(rawData); + var currRecipe = XmlDeserialize(rawData); if (currRecipe != null) { // recupero data-ora ricetta da campo string @@ -6721,7 +6721,7 @@ namespace IOB_WIN_FORM.Iob CurrStatus = "Ricevute", ActionList = stdActList }; - string rawWeek = JsonConvert.SerializeObject(cPerInfo); + string rawWeek = JsonSerialize(cPerInfo); redHashWeek.Add(cWeek, rawWeek); } // salvo in redis... @@ -6730,7 +6730,7 @@ namespace IOB_WIN_FORM.Iob // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; - string dictPayload = JsonConvert.SerializeObject(redHashWeek); + string dictPayload = JsonSerialize(redHashWeek); await HttpService.CallUrlAsync(remUrl, dictPayload); } } @@ -7505,7 +7505,7 @@ namespace IOB_WIN_FORM.Iob if (!string.IsNullOrEmpty(rawVal)) { // provo a convertire - var lastState = JsonConvert.DeserializeObject(rawVal); + var lastState = JsonDeserialize(rawVal); if (lastState != null && lastState.Length > 0) { item.loadPrev(lastState); @@ -7561,7 +7561,7 @@ namespace IOB_WIN_FORM.Iob lgDebug($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write"); if (utils.CRB("verbose")) { - string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented); + string rawMemConf = JsonSerialize(memMap, Formatting.Indented); lgDebug($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}"); } // se ho variabili read --> genero dati TSVC... @@ -7601,7 +7601,7 @@ namespace IOB_WIN_FORM.Iob if (memMap != null) { // invio su cloud conf memoria... - string rawData = JsonConvert.SerializeObject(memMap, Formatting.Indented); + string rawData = JsonSerialize(memMap, Formatting.Indented); // controllo ping al server... if (serverOk) { @@ -7677,7 +7677,7 @@ namespace IOB_WIN_FORM.Iob } // invio su cloud parametri SE sono connesso alla macchina... in pratica reset parametri... string tipoCall = urlSaveAllParams; - rawData = JsonConvert.SerializeObject(allParam, Formatting.Indented); + rawData = JsonSerialize(allParam, Formatting.Indented); if (serverOk) { // verifica se sia un IOB "parziale" --> salva solo update ai parametri @@ -9212,7 +9212,7 @@ namespace IOB_WIN_FORM.Iob lgInfo($"Chiamata di plcWriteParams da processMem2Write: {updatedPar.Count} updatedPar"); plcWriteParams(ref updatedPar); // invio su cloud parametri! - string rawData = JsonConvert.SerializeObject(updatedPar); + string rawData = JsonSerialize(updatedPar); HttpService.CallUrlPost($"{urlUpdateWriteParams}", rawData); lgInfo($"Notificato a server scrittura {updatedPar.Count} parametri"); } @@ -9222,7 +9222,7 @@ namespace IOB_WIN_FORM.Iob if (currWritePar.Count > 0 && (lastWriteParamsUpsert.AddMinutes(vetoSendWriteUpsert) < adesso)) { // invio su cloud parametri! - string rawData = JsonConvert.SerializeObject(currWritePar); + string rawData = JsonSerialize(currWritePar); var res = HttpService.CallUrlPost($"{urlUpdateWriteParams}", rawData); lgInfo($"Reinviato a server stato {updatedPar.Count} parametri WRITE"); lastWriteParamsUpsert = adesso; @@ -9348,7 +9348,7 @@ namespace IOB_WIN_FORM.Iob string rawConfFile = File.ReadAllText(confSetupPath); if (!string.IsNullOrEmpty(rawConfFile)) { - currConf = JsonConvert.DeserializeObject(rawConfFile); + currConf = JsonDeserialize(rawConfFile); if (currConf != null) { addHeader = currConf.addHeader; @@ -9510,7 +9510,7 @@ namespace IOB_WIN_FORM.Iob Dictionary currDict = redisMan.redGetHashDict(fullKey); // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; - string dictPayload = JsonConvert.SerializeObject(currDict); + string dictPayload = JsonSerialize(currDict); await HttpService.CallUrlAsync(remUrl, dictPayload); //await callUrlWithPayloadAsync(remUrl, dictPayload, true); //await callUrlWithPayloadAsync(remUrl, dictPayload, false); diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index e75c3600..b45b9186 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -326,28 +326,6 @@ namespace IOB_WIN_KAWASAKI.Iob { // impiego override metodo set... bool answ = setcontapezziPLC(0, codTav); -#if false - bool answ = false; - // ...SE abilitato da conf IOB - if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE") - { - // scrivo valore 0 x il contapezzi - try - { - pzCounter = 0; - } - catch (Exception exc) - { - lgError(exc, "Errore in RESET contapezzi KAWASAKI"); - connectionOk = false; - } - answ = true; - } - else - { - lgError("Impossibile effettuare RESET contapezzi KAWASAKI, mancanza parametro OPT:ENABLE_PZ_RESET"); - } -#endif return answ; } From 9cc6b853ecda15000505f599aab405f5de2910e3 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 11:00:19 +0200 Subject: [PATCH 15/39] Aggiunta script test compilazioni --- build_all.ps1 | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 build_all.ps1 diff --git a/build_all.ps1 b/build_all.ps1 new file mode 100644 index 00000000..dbbe56e7 --- /dev/null +++ b/build_all.ps1 @@ -0,0 +1,56 @@ +# Cerca ricorsivamente solo le soluzioni che iniziano con 'IOB-WIN-' +$pattern = "IOB-WIN-*.sln" +$solutions = Get-ChildItem -Path . -Filter $pattern -Recurse + +if ($solutions.Count -eq 0) { + Write-Host "⚠️ Nessuna soluzione trovata che corrisponde al pattern: $pattern" -ForegroundColor Yellow + Exit +} + +Write-Host "🚀 Trovate $($solutions.Count) soluzioni da verificare." -ForegroundColor Magenta + +# Inizializzazione variabili per il riepilogo +$successCount = 0 +$failCount = 0 +$failedSolutions = @() + +foreach ($sol in $solutions) { + Write-Host "`n--------------------------------------------------" -ForegroundColor Cyan + Write-Host "Compilazione in corso: $($sol.Name)" -ForegroundColor White + Write-Host "--------------------------------------------------" -ForegroundColor Cyan + + # Esegue la compilazione + dotnet build $sol.FullName --configuration Debug --no-incremental + + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Errore nella compilazione di $($sol.Name)" -ForegroundColor Red + $failCount++ + $failedSolutions += $sol.Name + } + else { + Write-Host "✅ $($sol.Name) compilata con successo!" -ForegroundColor Green + $successCount++ + } +} + +# Determina il colore del testo per i fallimenti in modo retrocompatibile +$failColor = "Gray" +if ($failCount -gt 0) { $failColor = "Red" } + +# --- RIEPILOGO FINALE --- +Write-Host "`n==================================================" -ForegroundColor Magenta +Write-Host " 🏁 Processo di verifica completato!" -ForegroundColor Magenta +Write-Host "==================================================" -ForegroundColor Magenta +Write-Host " Successi: $successCount" -ForegroundColor Green +Write-Host " Falliti: $failCount" -ForegroundColor $failColor + +if ($failCount -gt 0) { + Write-Host "`n❌ Elenco delle soluzioni fallite:" -ForegroundColor Red + foreach ($failed in $failedSolutions) { + Write-Host " - $failed" -ForegroundColor Red + } +} +else { + Write-Host "`n🎉 Ottimo! Tutte le soluzioni sono state compilate senza errori." -ForegroundColor Green +} +Write-Host "==================================================" -ForegroundColor Magenta \ No newline at end of file From f53eb87bbc3e8720240b2a207813434201ab89d7 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 11:21:19 +0200 Subject: [PATCH 16/39] Fix gestione beckup x compilazione aggiornata --- IOB-WIN-BECKHOFF/AdapterFormNext.cs | 17 +- IOB-WIN-BECKHOFF/App.config | 234 +++++++++--------- IOB-WIN-BECKHOFF/IOB-WIN-BECKHOFF.csproj | 8 +- IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs | 1 + IOB-WIN-BECKHOFF/IobBeckhoff/BeckhoffCpa.cs | 2 + .../Properties/Resources.Designer.cs | 44 ++-- .../Properties/Settings.Designer.cs | 22 +- IOB-WIN-BECKHOFF/packages.config | 8 +- 8 files changed, 166 insertions(+), 170 deletions(-) diff --git a/IOB-WIN-BECKHOFF/AdapterFormNext.cs b/IOB-WIN-BECKHOFF/AdapterFormNext.cs index d3365b26..05afee65 100644 --- a/IOB-WIN-BECKHOFF/AdapterFormNext.cs +++ b/IOB-WIN-BECKHOFF/AdapterFormNext.cs @@ -21,7 +21,7 @@ namespace IOB_WIN_BECKHOFF /// /// carica IOB richiesto /// - protected override void loadIobType() + protected override async Task loadIobType() { if (IOBConfFull != null) { @@ -40,16 +40,11 @@ namespace IOB_WIN_BECKHOFF btnStart.Enabled = false; break; } - lblCncText = $"CNC: {IOBConfFull.General.IobType} [{IOBConfFull.Device.Connect.IpAddr}:{IOBConfFull.Device.Connect.Port}]"; - lblSrvUrlText = $"SRV: {IOBConfFull.MapoMesConf.IpAddr} | URL: {IOBConfFull.MapoMesConf.ApiUrl("")}"; - - // aggancio evento refresh - iobObj.eh_refreshed += IobObj_eh_refreshed; - - // carico i default values su interfaccia - setDefaults(); - - displayTaskAndLog($"Caricata conf per adapter {tipoScelto}"); + if (!await iobInitAsync()) + { + return; + } + UpdateDisplTypeIobSel(); } } } diff --git a/IOB-WIN-BECKHOFF/App.config b/IOB-WIN-BECKHOFF/App.config index d6fcfea3..f66d9734 100644 --- a/IOB-WIN-BECKHOFF/App.config +++ b/IOB-WIN-BECKHOFF/App.config @@ -1,178 +1,186 @@ - + -
+
- + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - + + - - - - + + + + - + - - - + + + - - - + + + - + - - - + + + - - + + - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - - - + + + + + + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + - + - + - \ No newline at end of file + diff --git a/IOB-WIN-BECKHOFF/IOB-WIN-BECKHOFF.csproj b/IOB-WIN-BECKHOFF/IOB-WIN-BECKHOFF.csproj index 31bb9319..267ec3a9 100644 --- a/IOB-WIN-BECKHOFF/IOB-WIN-BECKHOFF.csproj +++ b/IOB-WIN-BECKHOFF/IOB-WIN-BECKHOFF.csproj @@ -8,10 +8,11 @@ WinExe IOB_WIN_BECKHOFF IOB-WIN-BECKHOFF - v4.6.2 + v4.7.2 512 true true + AnyCPU @@ -33,8 +34,8 @@ 4 - - ..\packages\MapoSDK.6.14.2505.2916\lib\MapoSDK.dll + + ..\packages\MapoSDK.6.14.2509.1018\lib\MapoSDK.dll @@ -81,6 +82,7 @@ True Resources.resx + True App.config diff --git a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs index 48a3cc61..9fdae387 100644 --- a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs +++ b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs @@ -1,5 +1,6 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Files; using System; using System.Collections.Generic; diff --git a/IOB-WIN-BECKHOFF/IobBeckhoff/BeckhoffCpa.cs b/IOB-WIN-BECKHOFF/IobBeckhoff/BeckhoffCpa.cs index ecfa6750..27984431 100644 --- a/IOB-WIN-BECKHOFF/IobBeckhoff/BeckhoffCpa.cs +++ b/IOB-WIN-BECKHOFF/IobBeckhoff/BeckhoffCpa.cs @@ -1,5 +1,7 @@ using IOB_UT_NEXT; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; diff --git a/IOB-WIN-BECKHOFF/Properties/Resources.Designer.cs b/IOB-WIN-BECKHOFF/Properties/Resources.Designer.cs index 0b54d965..8e53eb9b 100644 --- a/IOB-WIN-BECKHOFF/Properties/Resources.Designer.cs +++ b/IOB-WIN-BECKHOFF/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace IOB_WIN_BECKHOFF.Properties -{ - - +namespace IOB_WIN_BECKHOFF.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace IOB_WIN_BECKHOFF.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IOB_WIN_BECKHOFF.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/IOB-WIN-BECKHOFF/Properties/Settings.Designer.cs b/IOB-WIN-BECKHOFF/Properties/Settings.Designer.cs index cad87ef8..0474e04a 100644 --- a/IOB-WIN-BECKHOFF/Properties/Settings.Designer.cs +++ b/IOB-WIN-BECKHOFF/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace IOB_WIN_BECKHOFF.Properties -{ - - +namespace IOB_WIN_BECKHOFF.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/IOB-WIN-BECKHOFF/packages.config b/IOB-WIN-BECKHOFF/packages.config index b435bc4f..2dcb60a9 100644 --- a/IOB-WIN-BECKHOFF/packages.config +++ b/IOB-WIN-BECKHOFF/packages.config @@ -1,14 +1,14 @@  - + - + - - + + From eaa04afb5c7b09e3d09d55d95a48f3acd8648df9 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 11:21:41 +0200 Subject: [PATCH 17/39] fix build parallelo + rimozione sln duplicata --- IOB-WIN-NEXT/IOB-WIN-NEXT.sln | 31 ------------ build_all_par.ps1 | 94 +++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 31 deletions(-) delete mode 100644 IOB-WIN-NEXT/IOB-WIN-NEXT.sln create mode 100644 build_all_par.ps1 diff --git a/IOB-WIN-NEXT/IOB-WIN-NEXT.sln b/IOB-WIN-NEXT/IOB-WIN-NEXT.sln deleted file mode 100644 index 2e7483cd..00000000 --- a/IOB-WIN-NEXT/IOB-WIN-NEXT.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31205.134 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOB-WIN-NEXT", "IOB-WIN-NEXT.csproj", "{B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Debug|x86.ActiveCfg = Debug|x86 - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Debug|x86.Build.0 = Debug|x86 - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Release|Any CPU.Build.0 = Release|Any CPU - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Release|x86.ActiveCfg = Release|x86 - {B2ABB009-C046-4F9C-956C-52DCAA9FE5A9}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5BF50E92-A90E-4117-812C-2BFC803594DB} - EndGlobalSection -EndGlobal diff --git a/build_all_par.ps1 b/build_all_par.ps1 new file mode 100644 index 00000000..6c109f27 --- /dev/null +++ b/build_all_par.ps1 @@ -0,0 +1,94 @@ +# --- CONFIGURAZIONE --- +$pattern = "IOB-WIN-*.sln" +# Sostituisci questo percorso con il file .csproj del tuo progetto comune condiviso! +$sharedProjectPath = ".\IOB-WIN-FORM\IOB-WIN-FORM.csproj" + +# Cerca tutte le soluzioni +$solutions = Get-ChildItem -Path . -Filter $pattern -Recurse + +if ($solutions.Count -eq 0) { + Write-Host "⚠️ Nessuna soluzione trovata che corrisponde al pattern: $pattern" -ForegroundColor Yellow + Exit +} + +Write-Host "🚀 Trovate $($solutions.Count) soluzioni." -ForegroundColor Magenta + +# FASE 1: Compilazione preventiva del progetto comune +if (Test-Path $sharedProjectPath) { + Write-Host "`n📦 Fase 1: Compilazione del progetto comune condiviso..." -ForegroundColor Cyan + dotnet build $sharedProjectPath --configuration Debug + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Errore critico: Impossibile compilare il progetto comune. Interruzione." -ForegroundColor Red + Exit + } + Write-Host "✅ Progetto comune pronto." -ForegroundColor Green +} +else { + Write-Host "⚠️ Attenzione: Percorso del progetto comune non trovato nello script ($sharedProjectPath)." -ForegroundColor Yellow + Write-Host "Il parallelismo potrebbe causare conflitti di file." -ForegroundColor Yellow +} + +# FASE 2: Compilazione parallela delle soluzioni senza toccare le dipendenze esterne +Write-Host "`n🛠️ Fase 2: Avvio compilazione parallela delle soluzioni (Max 4)..." -ForegroundColor Magenta +Write-Host "==================================================" -ForegroundColor Magenta + +$results = $solutions | ForEach-Object -Parallel { + $solName = $_.Name + $solPath = $_.FullName + $sharedExists = $using:sharedProjectPath + + # Se abbiamo compilato prima il progetto comune, usiamo --no-dependencies per evitare lock sui file + if ($sharedExists) { + $log = dotnet build $solPath --configuration Debug --no-dependencies 2>&1 + } + else { + $log = dotnet build $solPath --configuration Debug 2>&1 + } + + [PSCustomObject]@{ + Name = $solName + Success = ($LASTEXITCODE -eq 0) + Log = $log + } +} -ThrottleLimit 4 + +# --- ELABORAZIONE DEI RISULTATI --- +$successCount = 0 +$failCount = 0 +$failedSolutions = @() + +foreach ($res in $results) { + if ($res.Success) { + Write-Host "✅ $($res.Name) compilata con successo!" -ForegroundColor Green + $successCount++ + } + else { + Write-Host "❌ Errore nella compilazione di $($res.Name)" -ForegroundColor Red + $failCount++ + $failedSolutions += $res.Name + + Write-Host "--- Dettagli Errore per $($res.Name) ---" -ForegroundColor DarkRed + $res.Log | Select-Object -Last 10 | Write-Host -ForegroundColor Gray + Write-Host "---------------------------------------" -ForegroundColor DarkRed + } +} + +$failColor = if ($failCount -gt 0) { "Red" } else { "Gray" } + +# --- RIEPILOGO FINALE --- +Write-Host "`n==================================================" -ForegroundColor Magenta +Write-Host "🏁 Processo di verifica completato!" -ForegroundColor Magenta +Write-Host "==================================================" -ForegroundColor Magenta +Write-Host " Successi: $successCount" -ForegroundColor Green +Write-Host " Falliti: $failCount" -ForegroundColor $failColor + +if ($failCount -gt 0) { + Write-Host "`n❌ Elenco delle soluzioni fallite:" -ForegroundColor Red + foreach ($failed in $failedSolutions) { + Write-Host " - $failed" -ForegroundColor Red + } +} +else { + Write-Host "`n🎉 Ottimo! Tutte le soluzioni sono state verificate con successo." -ForegroundColor Green +} +Write-Host "==================================================" -ForegroundColor Magenta \ No newline at end of file From f4e01b893729828465c2e195dbaf63a9d56f0261 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 11:24:08 +0200 Subject: [PATCH 18/39] Continuo fix simula + build_all usato x single test --- IOB-WIN-FORM/IOB-WIN-FORM.csproj | 4 ++++ IOB-WIN-FORM/Iob/Simula.cs | 32 ++++++++++++++++---------------- IOB-WIN-FORM/packages.config | 1 + build_all.ps1 | 2 +- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/IOB-WIN-FORM/IOB-WIN-FORM.csproj b/IOB-WIN-FORM/IOB-WIN-FORM.csproj index ca506eea..288b827c 100644 --- a/IOB-WIN-FORM/IOB-WIN-FORM.csproj +++ b/IOB-WIN-FORM/IOB-WIN-FORM.csproj @@ -14,6 +14,7 @@ + true true @@ -91,6 +92,9 @@ ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll + + ..\packages\System.Resources.Extensions.4.7.1\lib\net461\System.Resources.Extensions.dll + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index a1d089fe..0f70f550 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -973,10 +973,10 @@ namespace IOB_WIN_FORM.Iob item.reqValue = ""; MemBlock = new byte[byteSize]; - serObj = JsonConvert.SerializeObject(item, Formatting.Indented); - lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); - serObj = JsonConvert.SerializeObject(currMem, Formatting.Indented); - lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + serObj = JsonSerialize(item, Formatting.Indented); + lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + serObj = JsonSerialize(currMem, Formatting.Indented); + lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); lgInfo($"---------------MemBlock data---------------{Environment.NewLine}{BitConverter.ToString(MemBlock)}{Environment.NewLine}--------------- END data ---------------"); // salvo @@ -1611,7 +1611,7 @@ namespace IOB_WIN_FORM.Iob { try { - listaArt = JsonConvert.DeserializeObject>(rawListArt); + listaArt = JsonDeserialize>(rawListArt); okArt = listaArt.Count > 0; } catch (Exception exc) @@ -1627,7 +1627,7 @@ namespace IOB_WIN_FORM.Iob { try { - listaDoss = JsonConvert.DeserializeObject>(rawListDOSS); + listaDoss = JsonDeserialize>(rawListDOSS); okDoss = listaDoss.Count > 0; } catch (Exception exc) @@ -1643,7 +1643,7 @@ namespace IOB_WIN_FORM.Iob { try { - listaPODL = JsonConvert.DeserializeObject>(rawListPODL); + listaPODL = JsonDeserialize>(rawListPODL); okPodl = listaPODL.Count > 0; } catch (Exception exc) @@ -1659,7 +1659,7 @@ namespace IOB_WIN_FORM.Iob { try { - anagLVFasi = JsonConvert.DeserializeObject>(rawLVFasi); + anagLVFasi = JsonDeserialize>(rawLVFasi); dictAF = anagLVFasi.ToDictionary(x => x.value, x => x.label); okLVFasi = listaArt.Count > 0; } @@ -1760,8 +1760,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaArt = JsonConvert.DeserializeObject>(rawListArt); - okArt = listaArt.Count > 0; + listaArt = JsonDeserialize>(rawListArt); + okArt = listaArt.Count > 0; } catch (Exception exc) { @@ -1776,8 +1776,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaDoss = JsonConvert.DeserializeObject>(rawListDOSS); - okDoss = listaDoss.Count > 0; + listaDoss = JsonDeserialize>(rawListDOSS); + okDoss = listaDoss.Count > 0; } catch (Exception exc) { @@ -1792,8 +1792,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaPODL = JsonConvert.DeserializeObject>(rawListPODL); - okPodl = listaPODL.Count > 0; + listaPODL = JsonDeserialize>(rawListPODL); + okPodl = listaPODL.Count > 0; } catch (Exception exc) { @@ -1828,7 +1828,7 @@ namespace IOB_WIN_FORM.Iob if (selArt != null && selDoss != null) { // recupero il resultset dei valori FluxLog da caricare - DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject(selDoss.Valore); + DossierFluxLogDTO resultSet = JsonDeserialize(selDoss.Valore); List listFluxLog = resultSet.ODL; // preparo elenco parametri da inviare... @@ -2167,7 +2167,7 @@ namespace IOB_WIN_FORM.Iob /// private void sendDataItemsList(List dataItems) { - string rawData = JsonConvert.SerializeObject(dataItems); + string rawData = JsonSerialize(dataItems); HttpService.CallUrlPost($"{urlSaveDataItems}", rawData); } diff --git a/IOB-WIN-FORM/packages.config b/IOB-WIN-FORM/packages.config index 093b0468..18b5ff20 100644 --- a/IOB-WIN-FORM/packages.config +++ b/IOB-WIN-FORM/packages.config @@ -16,6 +16,7 @@ + diff --git a/build_all.ps1 b/build_all.ps1 index dbbe56e7..fbd77c4a 100644 --- a/build_all.ps1 +++ b/build_all.ps1 @@ -1,5 +1,5 @@ # Cerca ricorsivamente solo le soluzioni che iniziano con 'IOB-WIN-' -$pattern = "IOB-WIN-*.sln" +$pattern = "IOB-WIN-NEXT.sln" $solutions = Get-ChildItem -Path . -Filter $pattern -Recurse if ($solutions.Count -eq 0) { From 67f758a395b65f1886c64e890c65574ab6a70f68 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 11:42:46 +0200 Subject: [PATCH 19/39] Script compilazioniok! --- build_all.ps1 | 2 +- build_all_par.ps1 | 128 ++++++++++++++++++++++++++++++---------------- build_one.ps1 | 56 ++++++++++++++++++++ 3 files changed, 141 insertions(+), 45 deletions(-) create mode 100644 build_one.ps1 diff --git a/build_all.ps1 b/build_all.ps1 index fbd77c4a..dbbe56e7 100644 --- a/build_all.ps1 +++ b/build_all.ps1 @@ -1,5 +1,5 @@ # Cerca ricorsivamente solo le soluzioni che iniziano con 'IOB-WIN-' -$pattern = "IOB-WIN-NEXT.sln" +$pattern = "IOB-WIN-*.sln" $solutions = Get-ChildItem -Path . -Filter $pattern -Recurse if ($solutions.Count -eq 0) { diff --git a/build_all_par.ps1 b/build_all_par.ps1 index 6c109f27..f0b2ffaf 100644 --- a/build_all_par.ps1 +++ b/build_all_par.ps1 @@ -1,94 +1,134 @@ # --- CONFIGURAZIONE --- $pattern = "IOB-WIN-*.sln" -# Sostituisci questo percorso con il file .csproj del tuo progetto comune condiviso! $sharedProjectPath = ".\IOB-WIN-FORM\IOB-WIN-FORM.csproj" -# Cerca tutte le soluzioni -$solutions = Get-ChildItem -Path . -Filter $pattern -Recurse +# Avvia il cronometro per calcolare il tempo totale +$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + +# 1. Trova l'MSBuild ufficiale di Visual Studio 2022 (Gestione installazioni multiple corretta) +$vsPaths = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17.0,18.0)" -products * -requires Microsoft.Component.MSBuild -property installationPath +if (-not $vsPaths) { + Write-Host "❌ Impossibile trovare Visual Studio 2022!" -ForegroundColor Red + Exit +} +$msbuildPath = Join-Path $vsPaths[0] "MSBuild\Current\Bin\MSBuild.exe" +Write-Host "🎯 Usando MSBuild di VS2022: $msbuildPath" -ForegroundColor Gray + +# Cerca tutte le soluzioni escludendo cartelle di build +$solutions = Get-ChildItem -Path . -Filter $pattern -Recurse | Where-Object { + $_.FullName -notmatch '\\(bin|obj|\\.git|\\.vs)\\.' +} if ($solutions.Count -eq 0) { Write-Host "⚠️ Nessuna soluzione trovata che corrisponde al pattern: $pattern" -ForegroundColor Yellow Exit } -Write-Host "🚀 Trovate $($solutions.Count) soluzioni." -ForegroundColor Magenta +Write-Host "🚀 Trovate $($solutions.Count) soluzioni univoche." -ForegroundColor Magenta -# FASE 1: Compilazione preventiva del progetto comune +# FASE 1: Compilazione preventiva del progetto comune (Usando MSBuild) if (Test-Path $sharedProjectPath) { Write-Host "`n📦 Fase 1: Compilazione del progetto comune condiviso..." -ForegroundColor Cyan - dotnet build $sharedProjectPath --configuration Debug + & $msbuildPath $sharedProjectPath /p:Configuration=Debug /v:m if ($LASTEXITCODE -ne 0) { Write-Host "❌ Errore critico: Impossibile compilare il progetto comune. Interruzione." -ForegroundColor Red Exit } Write-Host "✅ Progetto comune pronto." -ForegroundColor Green } -else { - Write-Host "⚠️ Attenzione: Percorso del progetto comune non trovato nello script ($sharedProjectPath)." -ForegroundColor Yellow - Write-Host "Il parallelismo potrebbe causare conflitti di file." -ForegroundColor Yellow -} -# FASE 2: Compilazione parallela delle soluzioni senza toccare le dipendenze esterne +# FASE 2: Compilazione parallela iniziale Write-Host "`n🛠️ Fase 2: Avvio compilazione parallela delle soluzioni (Max 4)..." -ForegroundColor Magenta Write-Host "==================================================" -ForegroundColor Magenta -$results = $solutions | ForEach-Object -Parallel { +$parallelResults = $solutions | ForEach-Object -Parallel { $solName = $_.Name $solPath = $_.FullName - $sharedExists = $using:sharedProjectPath + $msb = $using:msbuildPath - # Se abbiamo compilato prima il progetto comune, usiamo --no-dependencies per evitare lock sui file - if ($sharedExists) { - $log = dotnet build $solPath --configuration Debug --no-dependencies 2>&1 - } - else { - $log = dotnet build $solPath --configuration Debug 2>&1 - } + # Esegue MSBuild nativo catturando i log + $log = & $msb $solPath /p:Configuration=Debug /m:1 /p:BuildInParallel=false /v:m 2>&1 [PSCustomObject]@{ - Name = $solName - Success = ($LASTEXITCODE -eq 0) - Log = $log + Name = $solName + FullName = $solPath + Success = ($LASTEXITCODE -eq 0) + Log = $log } } -ThrottleLimit 4 -# --- ELABORAZIONE DEI RISULTATI --- -$successCount = 0 -$failCount = 0 -$failedSolutions = @() +# --- ANALISI PRIMO ROUND E FASE 3 (RETRY SEQUENZIALE) --- +$successSolutions = @() +$failedToRetry = @() -foreach ($res in $results) { +foreach ($res in $parallelResults) { if ($res.Success) { - Write-Host "✅ $($res.Name) compilata con successo!" -ForegroundColor Green - $successCount++ + Write-Host "✅ $($res.Name) compilata con successo (in parallelo)!" -ForegroundColor Green + $successSolutions += $res.Name } else { - Write-Host "❌ Errore nella compilazione di $($res.Name)" -ForegroundColor Red - $failCount++ - $failedSolutions += $res.Name - - Write-Host "--- Dettagli Errore per $($res.Name) ---" -ForegroundColor DarkRed - $res.Log | Select-Object -Last 10 | Write-Host -ForegroundColor Gray - Write-Host "---------------------------------------" -ForegroundColor DarkRed + Write-Host "⚠️ $($res.Name) fallita in parallelo. Accodata per il recupero sequenziale..." -ForegroundColor Yellow + $failedToRetry += $res } } +# Se ci sono falliti, li rieseguiamo UNO ALLA VOLTA pulendo la cache +if ($failedToRetry.Count -gt 0) { + Write-Host "`n🔄 Fase 3: Riesecuzione sequenziale dei task falliti ($($failedToRetry.Count) soluzioni)..." -ForegroundColor Magenta + Write-Host "==================================================" -ForegroundColor Magenta + + foreach ($failedRes in $failedToRetry) { + Write-Host "⏳ Ripristino e compilazione sequenziale: $($failedRes.Name)..." -ForegroundColor Cyan + + # 1. Forza un Restore pulito delle dipendenze per questa soluzione + & $msbuildPath $failedRes.FullName /t:Restore /v:m > $null + # 2. Pulisce eventuali residui corrotti + & $msbuildPath $failedRes.FullName /t:Clean /v:m /p:Configuration=Debug > $null + # 3. Riprova la Build reale + $retryLog = & $msbuildPath $failedRes.FullName /t:Build /p:Configuration=Debug /v:m 2>&1 + + if ($LASTEXITCODE -eq 0) { + Write-Host "✅ FALSO ALLARME: $($failedRes.Name) compilata correttamente in sequenziale!" -ForegroundColor Green + $successSolutions += $failedRes.Name + } + else { + Write-Host "❌ ERRORE REALE: $($failedRes.Name) è fallita anche in sequenziale." -ForegroundColor Red + # Sovrascriviamo l'oggetto inserendo il log del fallimento sequenziale (più pulito) + $failedRes.Log = $retryLog + } + } +} + +# Ferma il cronometro e calcola il tempo trascorso +$stopwatch.Stop() +$elapsedTime = "{0:mm\:ss}" -f $stopwatch.Elapsed + +# --- ELABORAZIONE DEI RISULTATI FINALI --- +$totalCount = $solutions.Count +$successCount = $successSolutions.Count +$failCount = $totalCount - $successCount $failColor = if ($failCount -gt 0) { "Red" } else { "Gray" } # --- RIEPILOGO FINALE --- Write-Host "`n==================================================" -ForegroundColor Magenta -Write-Host "🏁 Processo di verifica completato!" -ForegroundColor Magenta +Write-Host "🏁 Processo di verifica completato in $elapsedTime!" -ForegroundColor Magenta Write-Host "==================================================" -ForegroundColor Magenta -Write-Host " Successi: $successCount" -ForegroundColor Green -Write-Host " Falliti: $failCount" -ForegroundColor $failColor +Write-Host " Soluzioni Totali: $totalCount" -ForegroundColor White +Write-Host " Successi totali: $successCount" -ForegroundColor Green +Write-Host " Errori reali: $failCount" -ForegroundColor $failColor +Write-Host " Tempo impiegato: $elapsedTime" -ForegroundColor Cyan if ($failCount -gt 0) { - Write-Host "`n❌ Elenco delle soluzioni fallite:" -ForegroundColor Red - foreach ($failed in $failedSolutions) { - Write-Host " - $failed" -ForegroundColor Red + Write-Host "`n❌ Elenco delle soluzioni con ERRORI REALI:" -ForegroundColor Red + foreach ($res in $parallelResults) { + if ($successSolutions -notcontains $res.Name) { + Write-Host " - $($res.Name)" -ForegroundColor Red + Write-Host " 👉 Ultimi dettagli errore:" -ForegroundColor DarkRed + $res.Log | Where-Object { $_ -match "error" } | Select-Object -First 3 | Write-Host -ForegroundColor Gray + } } } else { - Write-Host "`n🎉 Ottimo! Tutte le soluzioni sono state verificate con successo." -ForegroundColor Green + Write-Host "`n🎉 Eccellente! Tutte le soluzioni compilano senza errori (i problemi di lock parallelo sono stati superati)." -ForegroundColor Green } Write-Host "==================================================" -ForegroundColor Magenta \ No newline at end of file diff --git a/build_one.ps1 b/build_one.ps1 new file mode 100644 index 00000000..7c2e2555 --- /dev/null +++ b/build_one.ps1 @@ -0,0 +1,56 @@ +# --- CONFIGURAZIONE DI TEST --- +# Inserisci qui il percorso esatto del file .sln che vuoi testare +$solutionPath = "IOB-WIN-NEXT.sln" +# $solutionPath = "IOB-WIN-MITSUBISHI.sln" + + +# --- TROVA MSBUILD (Versione Corretta per installazioni multiple) --- +$vsPaths = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17.0,18.0)" -products * -requires Microsoft.Component.MSBuild -property installationPath + +if (-not $vsPaths) { + Write-Host "❌ Impossibile trovare Visual Studio 2022!" -ForegroundColor Red + Exit +} + +# Prendiamo SOLO LA PRIMA installazione trovata per evitare stringhe doppie +$firstVsPath = $vsPaths[0] +$msbuildPath = Join-Path $firstVsPath "MSBuild\Current\Bin\MSBuild.exe" + +if (-not (Test-Path $solutionPath)) { + Write-Host "❌ Il file della soluzione specificato non esiste: $solutionPath" -ForegroundColor Red + Exit +} + +Write-Host "🎯 Soluzione sotto test: $solutionPath" -ForegroundColor Cyan +Write-Host "🎯 Usando MSBuild: $msbuildPath" -ForegroundColor Gray +Write-Host "--------------------------------------------------" -ForegroundColor Gray + +# 1. NuGet Restore +Write-Host "📦 [1/3] Ripristino pacchetti NuGet in corso..." -ForegroundColor Yellow +& $msbuildPath $solutionPath /t:Restore /v:m +if ($LASTEXITCODE -ne 0) { Write-Host "❌ Fallito il ripristino NuGet!" -ForegroundColor Red; Exit } + +# 2. Clean +Write-Host "🧹 [2/3] Pulizia soluzione (Clean)..." -ForegroundColor Yellow +& $msbuildPath $solutionPath /t:Clean /v:m /p:Configuration=Debug + +# 3. Build reale +Write-Host "🚀 [3/3] Compilazione (Build)..." -ForegroundColor Yellow +Write-Host "==================================================" -ForegroundColor DarkGray + +# Eseguiamo catturando l'output +& $msbuildPath $solutionPath /t:Build /p:Configuration=Debug /v:normal + +# Controllo finale REALE sul codice di uscita dell'ultimo comando eseguito +$finalResult = $LASTEXITCODE + +if ($finalResult -eq 0) { + Write-Host "`n==================================================" -ForegroundColor Green + Write-Host "✅ COMPILAZIONE AVVENUTA CON SUCCESSO!" -ForegroundColor Green + Write-Host "==================================================" -ForegroundColor Green +} +else { + Write-Host "`n==================================================" -ForegroundColor Red + Write-Host "❌ COMPILAZIONE FALLITA!" -ForegroundColor Red + Write-Host "==================================================" -ForegroundColor Red +} \ No newline at end of file From b1cdce28d8f4f1683b8b05ea797dd31027898e15 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 16:13:28 +0200 Subject: [PATCH 20/39] Update preliminare prima di inserire nuovi metodi --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 3 + .../Iob/Services/CommunicationService.cs | 91 ++++++++++ .../Machine/MachineCommunicationService.cs | 89 ++++++++++ .../Networking/ServerCommunicationService.cs | 118 +++++++++++++ IOB-WIN-FORM/Iob/Generic.cs | 166 ++++++------------ 5 files changed, 358 insertions(+), 109 deletions(-) create mode 100644 IOB-UT-NEXT/Iob/Services/CommunicationService.cs create mode 100644 IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs create mode 100644 IOB-UT-NEXT/Iob/Services/Networking/ServerCommunicationService.cs diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 12a9b114..9b160f6d 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -156,6 +156,9 @@ + + + diff --git a/IOB-UT-NEXT/Iob/Services/CommunicationService.cs b/IOB-UT-NEXT/Iob/Services/CommunicationService.cs new file mode 100644 index 00000000..3ba5e6b9 --- /dev/null +++ b/IOB-UT-NEXT/Iob/Services/CommunicationService.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Cache; +using IOB_UT_NEXT.Services.Networking; +using NLog; + +namespace IOB_UT_NEXT.Services.Networking +{ + /// + /// Orchestratore delle comunicazioni. + /// Coordina HttpService e RedisIobCache per eseguire workflow di business. + /// Riduce la profondità dello stack in Generic.cs. + /// + public class CommunicationService + { + #region Public Constructors + + public CommunicationService(IobConfTree config, RedisIobCache redisMan) + { + _config = config; + _redisMan = redisMan; + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Esegue una chiamata HTTP e salva il risultato direttamente su Redis (Workflow orchestrato). + /// + public async Task CallAndSaveToRedisAsync(string url, string redisKey) + { + try + { + string result = await HttpService.CallUrlAsync(url); + if (!string.IsNullOrEmpty(result)) + { + _redisMan.setRSV(redisKey, result); + } + return result; + } + catch (Exception ex) + { + logger.Error(ex, $"Error in CallAndSaveToRedisAsync: URL={url}, Key={redisKey}"); + throw; + } + } + + /// + /// Recupera dati da una URL e li deserializza (Workflow orchestrato). + /// + public async Task GetAndDeserializeAsync(string url) + { + string raw = await HttpService.CallUrlAsync(url); + return IOB_UT_NEXT.Services.Data.DataSerializer.Deserialize(raw); + } + + /// + /// Esegue una chiamata POST e salva il payload/risposta (Workflow orchestrato). + /// + public async Task PostAndStoreAsync(string url, string payload, string redisKey) + { + try + { + string response = await Task.Run(() => HttpService.CallUrlPost(url, payload)); + if (!string.IsNullOrEmpty(response)) + { + _redisMan.setRSV(redisKey, response); + } + return response; + } + catch (Exception ex) + { + logger.Error(ex, $"Error in PostAndStoreAsync: URL={url}, Key={redisKey}"); + throw; + } + } + + #endregion Public Methods + + #region Private Fields + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private readonly IobConfTree _config; + private readonly RedisIobCache _redisMan; + + #endregion Private Fields + } +} \ No newline at end of file diff --git a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs new file mode 100644 index 00000000..364b622b --- /dev/null +++ b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Objects; +using IOB_UT_NEXT.Services.Core; +using MapoSDK; +using NLog; + +namespace IOB_UT_NEXT.Services.Machine +{ + /// + /// MachineCommunicationService: Orchestratore per il dominio MACCHINA (_machineThread). + /// Gestisce l'interazione a bassa latenza con PLC/CNC e la gestione della memoria condivisa (MemMap). + /// + public class MachineCommunicationService + { + private readonly IobConfTree _config; + private readonly TCMan _tcMan; + private readonly plcMemMap _memMap; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public MachineCommunicationService(IobConfTree config, TCMan tcMan, plcMemMap memMap) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + _tcMan = tcMan ?? throw new ArgumentNullException(nameof(tcMan)); + _memMap = memMap ?? throw new ArgumentNullException(nameof(memMap)); + } + + #region PLC / CNC Operations (Real-Time Domain) + + /// + /// Legge il conteggio pezzi attuale dal driver della macchina. + /// + public int GetPzCountIOB() => _tcMan.pzCountIOB; + + /// + /// Legge il conteggio pezzi attuale dal PLC. + /// + public int GetPzCountPLC() => _tcMan.pzCountPLC; + + /// + /// Ottiene la media dei tempi ciclo (TC) rilevati. + /// + public double GetAverageTc() => _tcMan.avgTC > 0 ? _tcMan.avgTC : 1.0; + + /// + /// Ottiene l'ultimo timestamp osservato dal PLC. + /// + public DateTime GetLastObservedData() => _tcMan.lastObservedData; + + #endregion + + #region Memory Map Operations (Shared Memory Domain) + + /// + /// Scrive un valore nella memoria condivisa (MemMap) per l'invio al PLC. + /// + public void WriteToMemMap(string key, string value) + { + if (_memMap != null && _memMap.mMapWrite != null) + { + if (_memMap.mMapWrite.ContainsKey(key)) + { + _memMap.mMapWrite[key].value = value; + logger.Debug($"[MachineComm] MemMap Write: {key} = {value}"); + } + else + { + logger.Warn($"[MachineComm] Attempted write to non-existent MemMap key: {key}"); + } + } + } + + /// + /// Legge un valore dalla memoria condivisa (MemMap) ricevuta dal PLC. + /// + public string ReadFromMemMap(string key) + { + if (_memMap != null && _memMap.mMapRead != null && _memMap.mMapRead.ContainsKey(key)) + { + return _memMap.mMapRead[key].value; + } + return null; + } + + #endregion + } +} diff --git a/IOB-UT-NEXT/Iob/Services/Networking/ServerCommunicationService.cs b/IOB-UT-NEXT/Iob/Services/Networking/ServerCommunicationService.cs new file mode 100644 index 00000000..200fc251 --- /dev/null +++ b/IOB-UT-NEXT/Iob/Services/Networking/ServerCommunicationService.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Services.Cache; +using IOB_UT_NEXT.Services.Networking; +using IOB_UT_NEXT.Services.Data; +using NLog; + +namespace IOB_UT_NEXT.Services.Networking +{ + /// + /// ServerCommunicationService: Orchestratore per i task del dominio SERVER (_workerTask). + /// Gestisce il coordinamento tra chiamate HTTP, persistenza Redis e code di comunicazione. + /// + public class ServerCommunicationService + { + #region Public Constructors + + public ServerCommunicationService(IobConfTree config, RedisIobCache redisMan) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + _redisMan = redisMan ?? throw new ArgumentNullException(nameof(redisMan)); + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Esegue una chiamata HTTP GET e deserializza il risultato. + /// + public async Task GetAndDeserializeAsync(string url) + { + try + { + string response = await HttpService.CallUrlAsync(url); + return JsonDeserialize(response); + } + catch (Exception ex) + { + logger.Error(ex, $"[ServerComm] Error in GetAndDeserializeAsync | URL: {url}"); + throw; + } + } + + /// + /// Recupera dati da un URL e li salva su Redis. + /// Workflow: HTTP GET -> Redis Set. + /// + public async Task GetAndPersistAsync(string url, string redisKey) + { + try + { + string response = await HttpService.CallUrlAsync(url); + + if (!string.IsNullOrEmpty(response) && !string.IsNullOrEmpty(redisKey)) + { + _redisMan.setRSV(redisKey, response); + } + + return response; + } + catch (Exception ex) + { + logger.Error(ex, $"[ServerComm] Error in GetAndPersistAsync | URL: {url} | Key: {redisKey}"); + throw; + } + } + + /// + /// Esegue una chiamata HTTP POST e salva il risultato su Redis. + /// Workflow: Serialize -> HTTP POST -> Redis Set. + /// + public async Task PostAndPersistAsync(string url, T payload, string redisKey) + { + try + { + string serializedPayload = JsonSerialize(payload); + logger.Debug($"[ServerComm] POST to {url} | Payload: {serializedPayload}"); + + string response = await HttpService.CallUrlAsync(url, serializedPayload); + + if (!string.IsNullOrEmpty(response) && !string.IsNullOrEmpty(redisKey)) + { + _redisMan.setRSV(redisKey, response); + logger.Info($"[ServerComm] Data persisted to Redis: {redisKey}"); + } + + return response; + } + catch (Exception ex) + { + logger.Error(ex, $"[ServerComm] Error in PostAndPersistAsync | URL: {url} | Key: {redisKey}"); + throw; + } + } + + #endregion Public Methods + + #region Private Fields + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private readonly IobConfTree _config; + private readonly RedisIobCache _redisMan; + + #endregion Private Fields + + #region Private Methods + + private T JsonDeserialize(string json) => IOB_UT_NEXT.Services.Data.DataSerializer.Deserialize(json); + + // Helper per mantenere la compatibilità con la logica di serializzazione esistente + private string JsonSerialize(T obj) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj); + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index c1d7bf58..279849d4 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -7,6 +7,7 @@ using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; +using IOB_UT_NEXT.Services.Machine; using IOB_UT_NEXT.Services.Monitoring; using IOB_UT_NEXT.Services.Networking; using IOB_UT_NEXT.Services.Utility; @@ -40,6 +41,13 @@ namespace IOB_WIN_FORM.Iob { public partial class Generic : BaseObj { + #region Private Fields + + private CommunicationService commService; + private MachineCommunicationService machineCommService; + + #endregion Private Fields + #region Public Fields public int numPzReqOdl = 0; @@ -65,6 +73,10 @@ namespace IOB_WIN_FORM.Iob // init oggetto redis... redisMan = new RedisIobCache(IobConfNew.MapoMes.IpAddr, IobConfNew.General.FilenameIOB, $"{IobConfNew.General.IobType}", IobConfNew.General.MinDeltaSec); + // init communication services + commService = new CommunicationService(IobConfNew, redisMan); + machineCommService = new MachineCommunicationService(IobConfNew, tcMan, memMap); + // init code SetupQueue(); @@ -131,14 +143,8 @@ namespace IOB_WIN_FORM.Iob /// public virtual bool connectionOk { - get - { - return _connOk || DemoIn; - } - set - { - _connOk = value; - } + get => _connOk || DemoIn; + set => _connOk = value; } /// @@ -146,14 +152,8 @@ namespace IOB_WIN_FORM.Iob /// public Int32 contapezziIOB { - get - { - return tcMan.pzCountIOB; - } - set - { - tcMan.pzCountIOB = value; - } + get => tcMan.pzCountIOB; + set => tcMan.pzCountIOB = value; } /// @@ -161,14 +161,8 @@ namespace IOB_WIN_FORM.Iob /// public Int32 contapezziPLC { - get - { - return tcMan.pzCountPLC; - } - set - { - tcMan.pzCountPLC = value; - } + get => tcMan.pzCountPLC; + set => tcMan.pzCountPLC = value; } /// @@ -176,6 +170,7 @@ namespace IOB_WIN_FORM.Iob /// public int counterFLog { get; set; } + /// /// Contatore x invio dati RawTransf /// @@ -291,10 +286,7 @@ namespace IOB_WIN_FORM.Iob /// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per /// 100... 200% --> 200) ///
- public int maxPzDeltaPerc - { - get => IOBConfFull.Counters.MaxIncrPzCountPerc; - } + public int maxPzDeltaPerc => IOBConfFull.Counters.MaxIncrPzCountPerc; /// /// Verifica SE si debba fare log periodico (ogni "verboseLogTOut" sec...) @@ -317,33 +309,17 @@ namespace IOB_WIN_FORM.Iob /// /// 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; - } - } + public double plcAvgTc => tcMan.avgTC > 0 ? tcMan.avgTC : 1; /// /// DataOra dell'ultima lettura variabile contapezzi da CNC /// - public DateTime plcLastPzRead - { - get - { - return tcMan.lastObservedData; - } - } + public DateTime plcLastPzRead => 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; - } + public bool plcPzCountValid => tcMan.lastObservedData > dtAvvioAdp; /// /// Abilitazione coda segnali ingresso @@ -371,18 +347,12 @@ namespace IOB_WIN_FORM.Iob /// /// URL per segnalazione reboot... /// - public string urlReboot - { - get => $@"{urlCommandIobFile("sendReboot")}?mac={NetService.GetMACAddress()}"; - } + public string urlReboot => $@"{urlCommandIobFile("sendReboot")}?mac={NetService.GetMACAddress()}"; /// /// URL per salvataggio dati conf YAML completi IOB... /// - public string urlSaveConfYaml - { - get => $@"{urlCommandIobFile("saveConfYaml")}"; - } + public string urlSaveConfYaml => $@"{urlCommandIobFile("saveConfYaml")}"; /// /// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN) @@ -1010,28 +980,6 @@ namespace IOB_WIN_FORM.Iob { 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); } @@ -1052,7 +1000,7 @@ namespace IOB_WIN_FORM.Iob { // deserializzo... JobTaskData jobTaskReq = JsonDeserialize(rawJob); - + // processo! var reqDict = JobTaskData.TaskDict(jobTaskReq.RawData); if (reqDict.Count > 0) @@ -1063,9 +1011,9 @@ namespace IOB_WIN_FORM.Iob accodaServResp(jobTaskReq.CodTav, serVal); } } - - // svuoto! - QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); + + // svuoto! + QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); } } } @@ -1825,7 +1773,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonSerialize(fullFlObj); + answ = JsonSerialize(fullFlObj); } catch (Exception exc) { @@ -1856,7 +1804,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonSerialize(fullEvObj); + answ = JsonSerialize(fullEvObj); } catch (Exception exc) { @@ -1915,7 +1863,7 @@ namespace IOB_WIN_FORM.Iob // conversione finale try { - answ = JsonSerialize(fullUlObj); + answ = JsonSerialize(fullUlObj); } catch (Exception exc) { @@ -1942,11 +1890,11 @@ namespace IOB_WIN_FORM.Iob string rawVal = redisMan.redGetHashField(lastSendKey, keyReq); if (!string.IsNullOrEmpty(rawVal)) { - lastSend = JsonDeserialize(rawVal); + lastSend = JsonDeserialize(rawVal); } else { - rawVal = JsonSerialize(lastSend); + rawVal = JsonSerialize(lastSend); KeyValuePair[] hashFields = new KeyValuePair[1]; hashFields[0] = new KeyValuePair(keyReq, rawVal); redisMan.redSaveHash(lastSendKey, hashFields); @@ -1962,7 +1910,7 @@ namespace IOB_WIN_FORM.Iob public bool LastSendSet(string keyReq, DateTime dtRif) { string lastSendKey = GetStatusField("LastSend"); - string rawVal = JsonSerialize(dtRif); + string rawVal = JsonSerialize(dtRif); KeyValuePair[] hashFields = new KeyValuePair[1]; hashFields[0] = new KeyValuePair(keyReq, rawVal); bool fatto = redisMan.redSaveHash(lastSendKey, hashFields); @@ -4365,10 +4313,10 @@ namespace IOB_WIN_FORM.Iob List answ = new List(); string redKeyFLog = redisMan.redHash($"IOB:CurrData:{IOBConfFull.General.FilenameIOB}:LogFile:FluxLog"); var rawData = redisMan.getRSV(redKeyFLog); - if (!string.IsNullOrEmpty(rawData)) - { - answ = JsonDeserialize>(rawData); - } + if (!string.IsNullOrEmpty(rawData)) + { + answ = JsonDeserialize>(rawData); + } return answ; } set @@ -5771,7 +5719,7 @@ namespace IOB_WIN_FORM.Iob item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus)); // salvo in redis... string alarmHash = redisMan.redHash($"IOB:ALARM_STATUS:{IOBConfFull.General.FilenameIOB}:{item.memAddr}"); - string rawAlarms = JsonSerialize(item.alarmsState); + string rawAlarms = JsonSerialize(item.alarmsState); redisMan.setRSV(alarmHash, rawAlarms); } else @@ -6123,7 +6071,7 @@ namespace IOB_WIN_FORM.Iob lgInfo("loadMemConf.04"); try { - memMap = JsonDeserialize(jsonData); + memMap = JsonDeserialize(jsonData); } catch (Exception exc) { @@ -6350,7 +6298,7 @@ namespace IOB_WIN_FORM.Iob string rawData = File.ReadAllText(fileItem); if (!string.IsNullOrEmpty(rawData)) { - var convData = JsonDeserialize>(rawData); + var convData = JsonDeserialize>(rawData); if (convData != null) { list2Send = convData; @@ -6409,7 +6357,7 @@ namespace IOB_WIN_FORM.Iob { try { - writeList = JsonDeserialize>(resp); + writeList = JsonDeserialize>(resp); // se ho da fare chiamo esecuzione.. if (writeList.Count > 0) { @@ -6439,7 +6387,7 @@ namespace IOB_WIN_FORM.Iob // richiamo scrittura parametri su PLC plcWriteParams(ref updatedPar); // invio su cloud parametri! - string rawData = JsonSerialize(updatedPar); + string rawData = JsonSerialize(updatedPar); HttpService.CallUrl($"{urlUpdateWriteParams}", rawData); lgInfo($"Notifica a server scrittura {updatedPar.Count} parametri"); } @@ -6721,7 +6669,7 @@ namespace IOB_WIN_FORM.Iob CurrStatus = "Ricevute", ActionList = stdActList }; - string rawWeek = JsonSerialize(cPerInfo); + string rawWeek = JsonSerialize(cPerInfo); redHashWeek.Add(cWeek, rawWeek); } // salvo in redis... @@ -6730,7 +6678,7 @@ namespace IOB_WIN_FORM.Iob // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; - string dictPayload = JsonSerialize(redHashWeek); + string dictPayload = JsonSerialize(redHashWeek); await HttpService.CallUrlAsync(remUrl, dictPayload); } } @@ -7355,8 +7303,8 @@ namespace IOB_WIN_FORM.Iob } // invio e salvo... string remUrl = urlSaveMachIobConf; - string dictPayload = JsonSerialize(currDict); - await HttpService.CallUrlAsync(remUrl, dictPayload); + string dictPayload = JsonSerialize(currDict); + await HttpService.CallUrlAsync(remUrl, dictPayload); lgTrace("Invio MachineConf effettuato"); } } @@ -7505,7 +7453,7 @@ namespace IOB_WIN_FORM.Iob if (!string.IsNullOrEmpty(rawVal)) { // provo a convertire - var lastState = JsonDeserialize(rawVal); + var lastState = JsonDeserialize(rawVal); if (lastState != null && lastState.Length > 0) { item.loadPrev(lastState); @@ -7561,7 +7509,7 @@ namespace IOB_WIN_FORM.Iob lgDebug($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write"); if (utils.CRB("verbose")) { - string rawMemConf = JsonSerialize(memMap, Formatting.Indented); + string rawMemConf = JsonSerialize(memMap, Formatting.Indented); lgDebug($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}"); } // se ho variabili read --> genero dati TSVC... @@ -7601,7 +7549,7 @@ namespace IOB_WIN_FORM.Iob if (memMap != null) { // invio su cloud conf memoria... - string rawData = JsonSerialize(memMap, Formatting.Indented); + string rawData = JsonSerialize(memMap, Formatting.Indented); // controllo ping al server... if (serverOk) { @@ -7677,7 +7625,7 @@ namespace IOB_WIN_FORM.Iob } // invio su cloud parametri SE sono connesso alla macchina... in pratica reset parametri... string tipoCall = urlSaveAllParams; - rawData = JsonSerialize(allParam, Formatting.Indented); + rawData = JsonSerialize(allParam, Formatting.Indented); if (serverOk) { // verifica se sia un IOB "parziale" --> salva solo update ai parametri @@ -9212,7 +9160,7 @@ namespace IOB_WIN_FORM.Iob lgInfo($"Chiamata di plcWriteParams da processMem2Write: {updatedPar.Count} updatedPar"); plcWriteParams(ref updatedPar); // invio su cloud parametri! - string rawData = JsonSerialize(updatedPar); + string rawData = JsonSerialize(updatedPar); HttpService.CallUrlPost($"{urlUpdateWriteParams}", rawData); lgInfo($"Notificato a server scrittura {updatedPar.Count} parametri"); } @@ -9222,7 +9170,7 @@ namespace IOB_WIN_FORM.Iob if (currWritePar.Count > 0 && (lastWriteParamsUpsert.AddMinutes(vetoSendWriteUpsert) < adesso)) { // invio su cloud parametri! - string rawData = JsonSerialize(currWritePar); + string rawData = JsonSerialize(currWritePar); var res = HttpService.CallUrlPost($"{urlUpdateWriteParams}", rawData); lgInfo($"Reinviato a server stato {updatedPar.Count} parametri WRITE"); lastWriteParamsUpsert = adesso; @@ -9348,7 +9296,7 @@ namespace IOB_WIN_FORM.Iob string rawConfFile = File.ReadAllText(confSetupPath); if (!string.IsNullOrEmpty(rawConfFile)) { - currConf = JsonDeserialize(rawConfFile); + currConf = JsonDeserialize(rawConfFile); if (currConf != null) { addHeader = currConf.addHeader; @@ -9510,7 +9458,7 @@ namespace IOB_WIN_FORM.Iob Dictionary currDict = redisMan.redGetHashDict(fullKey); // invio ANCHE in MP-IO l'update delle info... string remUrl = urlSetHashDict; - string dictPayload = JsonSerialize(currDict); + string dictPayload = JsonSerialize(currDict); await HttpService.CallUrlAsync(remUrl, dictPayload); //await callUrlWithPayloadAsync(remUrl, dictPayload, true); //await callUrlWithPayloadAsync(remUrl, dictPayload, false); @@ -9532,7 +9480,7 @@ namespace IOB_WIN_FORM.Iob foreach (var rawJob in listaValori) { // deserializzo... - JobTaskData jobTask = JsonDeserialize(rawJob); + JobTaskData jobTask = JsonDeserialize(rawJob); // ora chiamo la cancellazione dei task eseguiti... var taskDict = JobTaskData.TaskDict(jobTask.RawData); foreach (var item in taskDict) From 788f7bb8fc129ac298c8db01209a0aba9d459bc6 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 17:02:45 +0200 Subject: [PATCH 21/39] Inizia spostamento logiche TempiCiclo --- .../Machine/MachineCommunicationService.cs | 77 +++++++++++-------- IOB-WIN-FORM/Iob/Generic.cs | 47 ++++++----- 2 files changed, 73 insertions(+), 51 deletions(-) diff --git a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs index 364b622b..68a97de7 100644 --- a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs +++ b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs @@ -2,42 +2,31 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using IOB_UT_NEXT.Config; +using IOB_UT_NEXT.Config.Mem; using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Core; -using MapoSDK; using NLog; namespace IOB_UT_NEXT.Services.Machine { /// /// MachineCommunicationService: Orchestratore per il dominio MACCHINA (_machineThread). - /// Gestisce l'interazione a bassa latenza con PLC/CNC e la gestione della memoria condivisa (MemMap). + /// Gestisce l'interazione con tcMan e memMap, incapsulando la logica di "maneggio" dei dati. /// public class MachineCommunicationService { - private readonly IobConfTree _config; - private readonly TCMan _tcMan; - private readonly plcMemMap _memMap; - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + #region Public Constructors - public MachineCommunicationService(IobConfTree config, TCMan tcMan, plcMemMap memMap) + public MachineCommunicationService(IobConfTree config, TCMan tcMan, plcMemMapExt memMap) { _config = config ?? throw new ArgumentNullException(nameof(config)); _tcMan = tcMan ?? throw new ArgumentNullException(nameof(tcMan)); _memMap = memMap ?? throw new ArgumentNullException(nameof(memMap)); } - #region PLC / CNC Operations (Real-Time Domain) + #endregion Public Constructors - /// - /// Legge il conteggio pezzi attuale dal driver della macchina. - /// - public int GetPzCountIOB() => _tcMan.pzCountIOB; - - /// - /// Legge il conteggio pezzi attuale dal PLC. - /// - public int GetPzCountPLC() => _tcMan.pzCountPLC; + #region Public Methods /// /// Ottiene la media dei tempi ciclo (TC) rilevati. @@ -49,9 +38,36 @@ namespace IOB_UT_NEXT.Services.Machine /// public DateTime GetLastObservedData() => _tcMan.lastObservedData; - #endregion + /// + /// Gestione conteggio pezzi attuale dal driver della macchina. + /// + /// + public int ContapezziIOB + { + get => _tcMan.pzCountIOB; + set => _tcMan.pzCountIOB=value; + } - #region Memory Map Operations (Shared Memory Domain) + /// + /// Gestione conteggio pezzi attuale dal PLC. + /// + public int ContapezziPLC + { + get => _tcMan.pzCountPLC; + set => _tcMan.pzCountPLC = value; + } + + /// + /// Legge un valore dalla memoria condivisa (MemMap) ricevuta dal PLC. + /// + public string ReadFromMemMap(string key) + { + if (_memMap != null && _memMap.mMapRead != null && _memMap.mMapRead.ContainsKey(key)) + { + return _memMap.mMapRead[key].value; + } + return null; + } /// /// Scrive un valore nella memoria condivisa (MemMap) per l'invio al PLC. @@ -72,18 +88,15 @@ namespace IOB_UT_NEXT.Services.Machine } } - /// - /// Legge un valore dalla memoria condivisa (MemMap) ricevuta dal PLC. - /// - public string ReadFromMemMap(string key) - { - if (_memMap != null && _memMap.mMapRead != null && _memMap.mMapRead.ContainsKey(key)) - { - return _memMap.mMapRead[key].value; - } - return null; - } + #endregion Public Methods - #endregion + #region Private Fields + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private readonly IobConfTree _config; + private readonly plcMemMapExt _memMap; + private readonly TCMan _tcMan; + + #endregion Private Fields } -} +} \ No newline at end of file diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 279849d4..100fd8a1 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -147,24 +147,6 @@ namespace IOB_WIN_FORM.Iob set => _connOk = value; } - /// - /// Contapezzi attuale - /// - public Int32 contapezziIOB - { - get => tcMan.pzCountIOB; - set => tcMan.pzCountIOB = value; - } - - /// - /// Ultima lettura variabile contapezzi da CNC - /// - public Int32 contapezziPLC - { - get => tcMan.pzCountPLC; - set => tcMan.pzCountPLC = value; - } - /// /// Contatore x invio dati FluxLog /// @@ -306,6 +288,7 @@ namespace IOB_WIN_FORM.Iob } } +#if false /// /// Valore medio del TC rilevato x verifica derive sul delta variazione contapezzi /// @@ -316,10 +299,36 @@ namespace IOB_WIN_FORM.Iob /// public DateTime plcLastPzRead => tcMan.lastObservedData; +#endif + /// + /// Contapezzi attuale + /// + public Int32 contapezziIOB + { + get => machineCommService.ContapezziIOB; + set => machineCommService.ContapezziIOB = value; + } + /// + /// Ultima lettura variabile contapezzi da CNC + /// + public Int32 contapezziPLC + { + get => machineCommService.ContapezziPLC; + set => machineCommService.ContapezziPLC = value; + } + /// + /// Valore medio del TC rilevato x verifica derive sul delta variazione contapezzi + /// + public double plcAvgTc => machineCommService.GetAverageTc(); + /// + /// DataOra dell'ultima lettura variabile contapezzi da CNC + /// + public DateTime plcLastPzRead => machineCommService.GetLastObservedData(); /// /// Determina se il contapezzi plc sia valido (lo è se data avvio adapter è prima di ultimo dato registrato in contapezzi) /// - public bool plcPzCountValid => tcMan.lastObservedData > dtAvvioAdp; + public bool plcPzCountValid => machineCommService.GetLastObservedData() > dtAvvioAdp; + /// /// Abilitazione coda segnali ingresso From 39cdbd33bc49c771228fca453207feed55db19cb Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 17:33:16 +0200 Subject: [PATCH 22/39] update build all --- build_all_par.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_all_par.ps1 b/build_all_par.ps1 index f0b2ffaf..59463dbe 100644 --- a/build_all_par.ps1 +++ b/build_all_par.ps1 @@ -129,6 +129,6 @@ if ($failCount -gt 0) { } } else { - Write-Host "`n🎉 Eccellente! Tutte le soluzioni compilano senza errori (i problemi di lock parallelo sono stati superati)." -ForegroundColor Green + Write-Host "`n🎉 Eccellente! Tutte le soluzioni compilano senza errori." -ForegroundColor Green } Write-Host "==================================================" -ForegroundColor Magenta \ No newline at end of file From 89cdc3d25cc8647db528e22f160c715fbd6a0bde Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 17:54:32 +0200 Subject: [PATCH 23/39] Fix update Generic.cs --- IOB-WIN-FORM/Iob/Generic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 100fd8a1..ac50ab5d 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1117,7 +1117,7 @@ namespace IOB_WIN_FORM.Iob 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; + machineCommService.WriteToMemMap(iKey, item.Value); taskOk = true; } else From 4f581652330ef2f1a24da0d0842b29a0d42756a9 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 18:42:06 +0200 Subject: [PATCH 24/39] Continuo sostituzioni --- .../Machine/MachineCommunicationService.cs | 5 ++- IOB-WIN-FORM/Iob/Generic.cs | 36 +++++++------------ 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs index 68a97de7..5accac77 100644 --- a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs +++ b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs @@ -72,13 +72,15 @@ namespace IOB_UT_NEXT.Services.Machine /// /// Scrive un valore nella memoria condivisa (MemMap) per l'invio al PLC. /// - public void WriteToMemMap(string key, string value) + public bool WriteToMemMap(string key, string value) { + bool fatto = false; if (_memMap != null && _memMap.mMapWrite != null) { if (_memMap.mMapWrite.ContainsKey(key)) { _memMap.mMapWrite[key].value = value; + fatto = true; logger.Debug($"[MachineComm] MemMap Write: {key} = {value}"); } else @@ -86,6 +88,7 @@ namespace IOB_UT_NEXT.Services.Machine logger.Warn($"[MachineComm] Attempted write to non-existent MemMap key: {key}"); } } + return fatto; } #endregion Public Methods diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index ac50ab5d..93488b15 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1170,29 +1170,19 @@ namespace IOB_WIN_FORM.Iob { 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})"; - } + // chiamo server x avere decodifica valore INT + newVal = getNumArt(item.Value); + // procedo come il resto cercando mappatura in memMap: recupero dati + // da memMap... + if (machineCommService.WriteToMemMap(iKey, newVal)) + { + taskVal = $"SET task: {iKey} --> {newVal} | mem: [Updated via MachineComm]"; + } + else + { + taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})"; + } + // salvo in currProd.. upsertKey(iKey, newVal); From 971931ab2ed4b80dd276976f118dd988c52c0d43 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 22 May 2026 19:02:21 +0200 Subject: [PATCH 25/39] Continuo repulisti metodi scrittura var in memoria --- IOB-WIN-FORM/Iob/Generic.cs | 59 ++++++++--------------- build_all_par.ps1 | 95 ++++++++++++++++++++++--------------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 93488b15..a3bd2c16 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1073,24 +1073,13 @@ namespace IOB_WIN_FORM.Iob case taskType.setProg: case taskType.setPzComm: // recupero dati da memMap... - if (memMap != null && memMap.mMapWrite != null) + if (machineCommService.WriteToMemMap(iKey, item.Value)) { - 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}"; - } + taskVal = $"SET task: {iKey} --> {item.Value} | mem: [Updated via MachineComm]"; } else { - taskVal = $"NO BankConf found, SET task: {iKey} --> {item.Value}"; + taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}"; } // salvo in currProd.. upsertKey(iKey, item.Value); @@ -1170,18 +1159,18 @@ namespace IOB_WIN_FORM.Iob { 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 (machineCommService.WriteToMemMap(iKey, newVal)) - { - taskVal = $"SET task: {iKey} --> {newVal} | mem: [Updated via MachineComm]"; - } - else - { - taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})"; - } + // chiamo server x avere decodifica valore INT + newVal = getNumArt(item.Value); + // procedo come il resto cercando mappatura in memMap: recupero dati + // da memMap... + if (machineCommService.WriteToMemMap(iKey, newVal)) + { + taskVal = $"SET task: {iKey} --> {newVal} | mem: [Updated via MachineComm]"; + } + else + { + taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})"; + } // salvo in currProd.. @@ -1193,26 +1182,16 @@ namespace IOB_WIN_FORM.Iob newVal = getNumComm(item.Value); // procedo come il resto cercando mappatura in memMap: recupero dati // da memMap... - if (memMap != null && memMap.mMapWrite != null) + if (machineCommService.WriteToMemMap(iKey, newVal)) { - 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})"; - } + taskVal = $"SET task: {iKey} --> {newVal} | mem: [Updated via MachineComm]"; } else { - taskVal = $"NO BankConf found, SET task: {iKey} --> {newVal} ({item.Value})"; + taskVal = $"NO DATA MEM, SET task: {iKey} --> {newVal} ({item.Value})"; } + // salvo in currProd.. upsertKey(iKey, newVal); break; diff --git a/build_all_par.ps1 b/build_all_par.ps1 index 59463dbe..d9f17b5d 100644 --- a/build_all_par.ps1 +++ b/build_all_par.ps1 @@ -2,17 +2,24 @@ $pattern = "IOB-WIN-*.sln" $sharedProjectPath = ".\IOB-WIN-FORM\IOB-WIN-FORM.csproj" +# Nuova modalità per l'agente +$agentMode = $args[0] -eq "--agent" + # Avvia il cronometro per calcolare il tempo totale $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() # 1. Trova l'MSBuild ufficiale di Visual Studio 2022 (Gestione installazioni multiple corretta) $vsPaths = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17.0,18.0)" -products * -requires Microsoft.Component.MSBuild -property installationPath if (-not $vsPaths) { + if ($agentMode) { exit 1 } Write-Host "❌ Impossibile trovare Visual Studio 2022!" -ForegroundColor Red Exit } $msbuildPath = Join-Path $vsPaths[0] "MSBuild\Current\Bin\MSBuild.exe" -Write-Host "🎯 Usando MSBuild di VS2022: $msbuildPath" -ForegroundColor Gray + +if (-not $agentMode) { + Write-Host "🎯 Usando MSBuild di VS2022: $msbuildPath" -ForegroundColor Gray +} # Cerca tutte le soluzioni escludendo cartelle di build $solutions = Get-ChildItem -Path . -Filter $pattern -Recurse | Where-Object { @@ -20,26 +27,32 @@ $solutions = Get-ChildItem -Path . -Filter $pattern -Recurse | Where-Object { } if ($solutions.Count -eq 0) { + if ($agentMode) { exit 1 } Write-Host "⚠️ Nessuna soluzione trovata che corrisponde al pattern: $pattern" -ForegroundColor Yellow Exit } -Write-Host "🚀 Trovate $($solutions.Count) soluzioni univoche." -ForegroundColor Magenta +if (-not $agentMode) { + Write-Host "🚀 Trovate $($solutions.Count) soluzioni univoche." -ForegroundColor Magenta +} # FASE 1: Compilazione preventiva del progetto comune (Usando MSBuild) if (Test-Path $sharedProjectPath) { - Write-Host "`n📦 Fase 1: Compilazione del progetto comune condiviso..." -ForegroundColor Cyan - & $msbuildPath $sharedProjectPath /p:Configuration=Debug /v:m + if (-not $agentMode) { Write-Host "`n📦 Fase 1: Compilazione del progetto comune condiviso..." -ForegroundColor Cyan } + # Usiamo Release e x86 come da istruzioni AGENTS.md + & $msbuildPath $sharedProjectPath /p:Configuration=Release /v:m if ($LASTEXITCODE -ne 0) { - Write-Host "❌ Errore critico: Impossibile compilare il progetto comune. Interruzione." -ForegroundColor Red - Exit + if (-not $agentMode) { Write-Host "❌ Errore critico: Impossibile compilare il progetto comune. Interruzione." -ForegroundColor Red } + exit 1 } - Write-Host "✅ Progetto comune pronto." -ForegroundColor Green + if (-not $agentMode) { Write-Host "✅ Progetto comune pronto." -ForegroundColor Green } } # FASE 2: Compilazione parallela iniziale -Write-Host "`n🛠️ Fase 2: Avvio compilazione parallela delle soluzioni (Max 4)..." -ForegroundColor Magenta -Write-Host "==================================================" -ForegroundColor Magenta +if (-not $agentMode) { + Write-Host "`n🛠️ Fase 2: Avvio compilazione parallela delle soluzioni (Max 4)..." -ForegroundColor Magenta + Write-Host "==================================================" -ForegroundColor Magenta +} $parallelResults = $solutions | ForEach-Object -Parallel { $solName = $_.Name @@ -47,7 +60,8 @@ $parallelResults = $solutions | ForEach-Object -Parallel { $msb = $using:msbuildPath # Esegue MSBuild nativo catturando i log - $log = & $msb $solPath /p:Configuration=Debug /m:1 /p:BuildInParallel=false /v:m 2>&1 + # Usiamo Release e x86 per coerenza con le specifiche del progetto + $log = & $msb $solPath /p:Configuration=Release /m:1 /p:BuildInParallel=false /v:m 2>&1 [PSCustomObject]@{ Name = $solName @@ -63,36 +77,38 @@ $failedToRetry = @() foreach ($res in $parallelResults) { if ($res.Success) { - Write-Host "✅ $($res.Name) compilata con successo (in parallelo)!" -ForegroundColor Green + if (-not $agentMode) { Write-Host "✅ $($res.Name) compilata con successo (in parallelo)!" -ForegroundColor Green } $successSolutions += $res.Name } else { - Write-Host "⚠️ $($res.Name) fallita in parallelo. Accodata per il recupero sequenziale..." -ForegroundColor Yellow + if (-not $agentMode) { Write-Host "⚠️ $($res.Name) fallita in parallelo. Accodata per il recupero sequenziale..." -ForegroundColor Yellow } $failedToRetry += $res } } # Se ci sono falliti, li rieseguiamo UNO ALLA VOLTA pulendo la cache if ($failedToRetry.Count -gt 0) { - Write-Host "`n🔄 Fase 3: Riesecuzione sequenziale dei task falliti ($($failedToRetry.Count) soluzioni)..." -ForegroundColor Magenta - Write-Host "==================================================" -ForegroundColor Magenta + if (-not $agentMode) { + Write-Host "`n🔄 Fase 3: Riesecuzione sequenziale dei task falliti ($($failedToRetry.Count) soluzioni)..." -ForegroundColor Magenta + Write-Host "==================================================" -ForegroundColor Magenta + } foreach ($failedRes in $failedToRetry) { - Write-Host "⏳ Ripristino e compilazione sequenziale: $($failedRes.Name)..." -ForegroundColor Cyan + if (-not $agentMode) { Write-Host "⏳ Ripristino e compilazione sequenziale: $($failedRes.Name)..." -ForegroundColor Cyan } # 1. Forza un Restore pulito delle dipendenze per questa soluzione & $msbuildPath $failedRes.FullName /t:Restore /v:m > $null # 2. Pulisce eventuali residui corrotti - & $msbuildPath $failedRes.FullName /t:Clean /v:m /p:Configuration=Debug > $null + & $msbuildPath $failedRes.FullName /t:Clean /v:m /p:Configuration=Release > $null # 3. Riprova la Build reale - $retryLog = & $msbuildPath $failedRes.FullName /t:Build /p:Configuration=Debug /v:m 2>&1 + $retryLog = & $msbuildPath $failedRes.FullName /t:Build /p:Configuration=Release /v:m 2>&1 if ($LASTEXITCODE -eq 0) { - Write-Host "✅ FALSO ALLARME: $($failedRes.Name) compilata correttamente in sequenziale!" -ForegroundColor Green + if (-not $agentMode) { Write-Host "✅ FALSO ALLARME: $($failedRes.Name) compilata correttamente in sequenziale!" -ForegroundColor Green } $successSolutions += $failedRes.Name } else { - Write-Host "❌ ERRORE REALE: $($failedRes.Name) è fallita anche in sequenziale." -ForegroundColor Red + if (-not $agentMode) { Write-Host "❌ ERRORE REALE: $($failedRes.Name) è fallita anche in sequenziale." -ForegroundColor Red } # Sovrascriviamo l'oggetto inserendo il log del fallimento sequenziale (più pulito) $failedRes.Log = $retryLog } @@ -110,25 +126,30 @@ $failCount = $totalCount - $successCount $failColor = if ($failCount -gt 0) { "Red" } else { "Gray" } # --- RIEPILOGO FINALE --- -Write-Host "`n==================================================" -ForegroundColor Magenta -Write-Host "🏁 Processo di verifica completato in $elapsedTime!" -ForegroundColor Magenta -Write-Host "==================================================" -ForegroundColor Magenta -Write-Host " Soluzioni Totali: $totalCount" -ForegroundColor White -Write-Host " Successi totali: $successCount" -ForegroundColor Green -Write-Host " Errori reali: $failCount" -ForegroundColor $failColor -Write-Host " Tempo impiegato: $elapsedTime" -ForegroundColor Cyan +if (-not $agentMode) { + Write-Host "`n==================================================" -ForegroundColor Magenta + Write-Host "🏁 Processo di verifica completato in $elapsedTime!" -ForegroundColor Magenta + Write-Host "==================================================" -ForegroundColor Magenta + Write-Host " Soluzioni Totali: $totalCount" -ForegroundColor White + Write-Host " Successi totali: $successCount" -ForegroundColor Green + Write-Host " Errori reali: $failCount" -ForegroundColor $failColor + Write-Host " Tempo impiegato: $elapsedTime" -ForegroundColor Cyan -if ($failCount -gt 0) { - Write-Host "`n❌ Elenco delle soluzioni con ERRORI REALI:" -ForegroundColor Red - foreach ($res in $parallelResults) { - if ($successSolutions -notcontains $res.Name) { - Write-Host " - $($res.Name)" -ForegroundColor Red - Write-Host " 👉 Ultimi dettagli errore:" -ForegroundColor DarkRed - $res.Log | Where-Object { $_ -match "error" } | Select-Object -First 3 | Write-Host -ForegroundColor Gray + if ($failCount -gt 0) { + Write-Host "`n❌ Elenco delle soluzioni con ERRORI REALI:" -ForegroundColor Red + foreach ($res in $parallelResults) { + if ($successSolutions -notcontains $res.Name) { + Write-Host " - $($res.Name)" -ForegroundColor Red + Write-Host " 👉 Ultimi dettagli errore:" -ForegroundColor DarkRed + $res.Log | Where-Object { $_ -match "error" } | Select-Object -First 3 | Write-Host -ForegroundColor Gray + } } } + else { + Write-Host "`n🎉 Eccellente! Tutte le soluzioni compilano senza errori." -ForegroundColor Green + } + Write-Host "==================================================" -ForegroundColor Magenta } -else { - Write-Host "`n🎉 Eccellente! Tutte le soluzioni compilano senza errori." -ForegroundColor Green -} -Write-Host "==================================================" -ForegroundColor Magenta \ No newline at end of file + +# Exit code per l'agente +if ($failCount -gt 0) { exit 1 } else { exit 0 } From 4b9f03c9d7ebb5870b02e69c43e3d38784bef5d1 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 09:17:34 +0200 Subject: [PATCH 26/39] Continuo con le modifiche e i test --- IOB-WIN-FORM/Iob/Generic.cs | 28 +++++++++------------------ build_all_par.ps1 | 38 +++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index a3bd2c16..a5a05a83 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -1129,25 +1129,15 @@ namespace IOB_WIN_FORM.Iob 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}"; - } + if (machineCommService.WriteToMemMap(iKey, item.Value)) + { + taskVal = $"SET task: {iKey} --> {item.Value} | mem: [Updated via MachineComm]"; + } + else + { + taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}"; + } + lgInfo($"Chiamata forceSetPzCount: taskVal: {taskVal}"); break; diff --git a/build_all_par.ps1 b/build_all_par.ps1 index d9f17b5d..f98d89ae 100644 --- a/build_all_par.ps1 +++ b/build_all_par.ps1 @@ -2,13 +2,13 @@ $pattern = "IOB-WIN-*.sln" $sharedProjectPath = ".\IOB-WIN-FORM\IOB-WIN-FORM.csproj" -# Nuova modalità per l'agente -$agentMode = $args[0] -eq "--agent" +# Controllo robusto del parametro --agent (cerca in tutti gli argomenti passati) +$agentMode = $args -contains "--agent" # Avvia il cronometro per calcolare il tempo totale $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() -# 1. Trova l'MSBuild ufficiale di Visual Studio 2022 (Gestione installazioni multiple corretta) +# 1. Trova l'MSBuild ufficiale di Visual Studio 2022 $vsPaths = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17.0,18.0)" -products * -requires Microsoft.Component.MSBuild -property installationPath if (-not $vsPaths) { if ($agentMode) { exit 1 } @@ -36,11 +36,18 @@ if (-not $agentMode) { Write-Host "🚀 Trovate $($solutions.Count) soluzioni univoche." -ForegroundColor Magenta } -# FASE 1: Compilazione preventiva del progetto comune (Usando MSBuild) +# FASE 1: Compilazione preventiva del progetto comune if (Test-Path $sharedProjectPath) { if (-not $agentMode) { Write-Host "`n📦 Fase 1: Compilazione del progetto comune condiviso..." -ForegroundColor Cyan } - # Usiamo Release e x86 come da istruzioni AGENTS.md - & $msbuildPath $sharedProjectPath /p:Configuration=Release /v:m + + # AGGIORNAMENTO: Se siamo in agentMode silenziamo completamente MSBuild (/v:q) e ridirigiamo l'output + if ($agentMode) { + & $msbuildPath $sharedProjectPath /p:Configuration=Release /v:q /nologo > $null 2>&1 + } + else { + & $msbuildPath $sharedProjectPath /p:Configuration=Release /v:m /nologo + } + if ($LASTEXITCODE -ne 0) { if (-not $agentMode) { Write-Host "❌ Errore critico: Impossibile compilare il progetto comune. Interruzione." -ForegroundColor Red } exit 1 @@ -59,9 +66,8 @@ $parallelResults = $solutions | ForEach-Object -Parallel { $solPath = $_.FullName $msb = $using:msbuildPath - # Esegue MSBuild nativo catturando i log - # Usiamo Release e x86 per coerenza con le specifiche del progetto - $log = & $msb $solPath /p:Configuration=Release /m:1 /p:BuildInParallel=false /v:m 2>&1 + # Aggiunto /nologo per evitare intestazioni ripetute nei log interni + $log = & $msb $solPath /p:Configuration=Release /m:1 /p:BuildInParallel=false /v:m /nologo 2>&1 [PSCustomObject]@{ Name = $solName @@ -96,12 +102,9 @@ if ($failedToRetry.Count -gt 0) { foreach ($failedRes in $failedToRetry) { if (-not $agentMode) { Write-Host "⏳ Ripristino e compilazione sequenziale: $($failedRes.Name)..." -ForegroundColor Cyan } - # 1. Forza un Restore pulito delle dipendenze per questa soluzione - & $msbuildPath $failedRes.FullName /t:Restore /v:m > $null - # 2. Pulisce eventuali residui corrotti - & $msbuildPath $failedRes.FullName /t:Clean /v:m /p:Configuration=Release > $null - # 3. Riprova la Build reale - $retryLog = & $msbuildPath $failedRes.FullName /t:Build /p:Configuration=Release /v:m 2>&1 + & $msbuildPath $failedRes.FullName /t:Restore /v:q /nologo > $null 2>&1 + & $msbuildPath $failedRes.FullName /t:Clean /v:q /p:Configuration=Release /nologo > $null 2>&1 + $retryLog = & $msbuildPath $failedRes.FullName /t:Build /p:Configuration=Release /v:m /nologo 2>&1 if ($LASTEXITCODE -eq 0) { if (-not $agentMode) { Write-Host "✅ FALSO ALLARME: $($failedRes.Name) compilata correttamente in sequenziale!" -ForegroundColor Green } @@ -109,7 +112,6 @@ if ($failedToRetry.Count -gt 0) { } else { if (-not $agentMode) { Write-Host "❌ ERRORE REALE: $($failedRes.Name) è fallita anche in sequenziale." -ForegroundColor Red } - # Sovrascriviamo l'oggetto inserendo il log del fallimento sequenziale (più pulito) $failedRes.Log = $retryLog } } @@ -151,5 +153,5 @@ if (-not $agentMode) { Write-Host "==================================================" -ForegroundColor Magenta } -# Exit code per l'agente -if ($failCount -gt 0) { exit 1 } else { exit 0 } +# Exit code standard per ambienti automatizzati (0 = Successo, 1 = Fallimento) +if ($failCount -gt 0) { exit 1 } else { exit 0 } \ No newline at end of file From 62847732dda0dc0229236bda1a2cf9601b8eae73 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 09:59:43 +0200 Subject: [PATCH 27/39] continuo fix --- IOB-UT-NEXT/Iob/BaseObj.cs | 507 ++++++++++++++++-- .../Machine/MachineCommunicationService.cs | 8 + IOB-WIN-FORM/Iob/Generic.cs | 489 +---------------- IOB-WIN-FORM/Iob/Simula.cs | 50 +- 4 files changed, 523 insertions(+), 531 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 131bde02..38bfb956 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -1,16 +1,22 @@ using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Config.Mem; +using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Cache; using IOB_UT_NEXT.Services.Core; using IOB_UT_NEXT.Services.Data; using IOB_UT_NEXT.Services.Files; +using MapoSDK; using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Net.NetworkInformation; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text.Json; +using System.Text.Json.Serialization; using static IOB_UT_NEXT.Config.BaseAlarmConf; namespace IOB_UT_NEXT.Iob @@ -238,13 +244,14 @@ namespace IOB_UT_NEXT.Iob /// Oggetto della coda degli elementi di tipo RawTransf (e non ancora trasmessi) /// NB: sono salvati serializzati come stringhe /// - public DataQueue QueueRawTransf;// = new DataQueue("000", "QueueRawTransf", false); + public DataQueue QueueRawTransf; /// /// Coda delle richieste dal server (Task2Exe) /// public DataQueue QueueSrvReq; + // = new DataQueue("000", "QueueRawTransf", false); /// /// Coda delle risposte al server (Task2Exe) /// @@ -253,13 +260,14 @@ namespace IOB_UT_NEXT.Iob /// /// Coda valori LOG UTENTE (da non sottocampionare come samples)... /// - public DataQueue QueueULog;// = new DataQueue("000", "QueueULog", false); + public DataQueue QueueULog; /// /// alias booleano false = R /// public bool R = false; + // = new DataQueue("000", "QueueULog", false); /// /// 32 byte input base (es strobe, 8 word da 32 bit di flags...) /// @@ -280,11 +288,6 @@ namespace IOB_UT_NEXT.Iob /// public Stopwatch sw = new Stopwatch(); - /// - /// Oggetto gestione TempiCiclo e contapezzi - /// - public TCMan tcMan = new TCMan(0.5, 1.3, 5); - /// /// Imposta veto lettura dati (es per DB a 2 sec) /// @@ -402,49 +405,407 @@ namespace IOB_UT_NEXT.Iob #region Protected Fields - + /// + /// Variabile numero errori vari (in lettura) --> se supera soglia maxErroriCheck --> disconnette + /// + protected static int numErroriCheck = 0; /// /// Valore di attesa (random) dopo ogni invio x evitare congestione send... /// protected static int urlRandWait = 0; + /// + /// Stato connessione con macchina gestita + /// + protected bool _connOk = false; + + /// + /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC + /// + protected bool adpCommAct; + + /// + /// porta x adapter (x restart) + /// + protected int adpPortNum; + + /// + /// DataOra ultimo avvio adapter x watchdog + /// + protected DateTime adpStartRun; + + /// + /// Vettore 32 BIT valori in ingresso al filtro + /// + protected int B_input; + + /// + /// Vettore 32 BIT valori in uscita dal filtro + /// + protected int B_output; + + /// + /// Vettore 32 BIT valori precedenti + /// + protected int B_previous = -1; + + /// + /// Cod grupo IOB x creazione PODL al volo + /// + protected string CodGruppoIob = "ND-00"; + + /// + /// Num errori check alive + /// + protected int currAliveErrors = 0; + + /// + /// Dizionario valori impostati x produzione + /// + protected Dictionary currProdData = new Dictionary(); + + /// + /// num corrente errori read PLC + /// + protected int currReadErrors = 0; + + /// + /// num errori send + /// + protected int currSendErrors = 0; + + /// + /// Tempo di attesa in minuti x lettura contapezzi standard (da .ini / OptPar) + /// + protected double delayMinReadPzCount = 0; + + /// + /// Fattore di demoltiplicazione dei DynData x ridurre campionamento + /// + protected int demFactDynData = 1; + /// /// Disabilitazione gestione ODL (lettura e gestione) /// protected bool disableOdl = false; + /// + /// Boolean x indicare contapezzi disabilitato forzatamente da IOB + /// + protected bool disablePzCountByIob = false; + + /// + /// Veto x esecuzione task AutoDossier + /// + protected DateTime dtVetoAutoDossier = DateTime.Now; + + /// + /// Veto x lettura contapezzi (in caso di autoODL con attesa reset ad esempio...) + /// + protected DateTime dtVetoReadPzCount = DateTime.Now; + + /// + /// DataOra x veto all'invio dataItem + /// + protected DateTime dtVetoSenDataItem = DateTime.Now; + + /// + /// Veto x esecuzione Task2Exe troppo frequenti + /// + protected DateTime dtVetoTask2Exe = DateTime.Now; + + /// + /// Determina se sia prevista gestione PODL (creazione/avvio/chiusura) come Soitaab + /// + protected bool EnabelPodlManFull = false; + + /// + /// Abilita riscrittura memoria se trova differenze lettura/richiesti + /// + protected bool ENABLE_MEM_REWRITE = true; + + /// + /// Abilitazione restart (da opt par...) + /// + protected bool enableCliRestart = false; + + /// + /// Boolean x indicare contapezzi abilitato a livello di conf applicazione + /// + protected bool enablePzCountByApp = true; + + /// + /// Abilitazione invio dataitem + /// + protected bool enableSendDataItem = true; + + /// + /// Abilitazione gestione slow data + /// + protected bool enableSlowData = false; + //protected static Logger lg = LogManager.GetCurrentClassLogger(); /// /// Abilitazione invio conf macchine /// protected bool enabSendMachineConf = true; + /// + /// dizionario (opzionale) xz decodifica file da importare + /// + protected Dictionary FileDecod = new Dictionary(); + + /// + /// DeadBand x riduzione dati FluxLog (se 0 non gestita) + /// + protected double fluxLogRedDeadBand = 0; + + /// + /// Determina se sia gestita riduzione dati FluxLog + /// + protected bool fluxLogReduce = false; + + /// + /// Dizionario dei valori FluxLog ultimi verificati x veto + /// + protected Dictionary fluxLogReduceLast = new Dictionary(); + + /// + /// Dizionario dei valori FluxLog ultimi tipo STRING verificati x veto + /// + protected Dictionary fluxLogReduceLastString = new Dictionary(); + + /// + /// Dizionario dei veto send x ogni variabile quando non variata + /// + protected Dictionary fluxLogReduceVeto = new Dictionary(); + + /// + /// Finestra in minuti x invio dati FluxLog quando invariati + /// + protected int fluxLogResendPeriod = 60; + + /// + /// indica che è richiesto forzatamente reset contapezzi + /// + protected bool forcePzReset = false; + + /// + /// indica che è richiesto forzatamente reset contapezzi + /// + protected DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1); + + /// + /// DEADBAND globale se impostata (es x gestire variazioni minime valori FluxLog da inviare...) + /// + protected float GLOBAL_DBAND = 0; + + /// + /// Determina se siano gestite le ricette + /// + protected bool hasRecipe = false; + + /// + /// Array dei contatori x segnali blinking + /// + protected int[] i_counters; + + /// + /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) + /// + protected bool inSetup = false; + + /// + /// ultimo tentativo connessione... + /// + protected DateTime lastConnectTry; + /// /// Ultimo LOG registrazione avvio (x ridurre log notturni...) /// protected DateTime lastLogStartup = DateTime.Today.AddHours(-1); + /// + /// Dizionario ULTIMI valori impostati x produzione + /// + protected Dictionary lastProdData = new Dictionary(); + + /// + /// Ultimo invio contapezzi (x invio delayed) + /// + protected DateTime lastPzCountSend; + + /// + /// Dizionario ultimi valori (string) delle TSS + /// + protected Dictionary LastTSS = new Dictionary(); + + /// + /// Dizionario ultimi valori (string) delle TSS + /// + protected Dictionary LastTSSSend = new Dictionary(); + + /// + /// Dizionario ultimi valori (double) delle TSVC + /// + protected Dictionary LastTSVC = new Dictionary(); + + /// + /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) + /// + protected DateTime lastWarnODL; + + /// + /// ultima data-ora invio parametri write x ribadire status + /// + protected DateTime lastWriteParamsUpsert = DateTime.Now; + + /// + /// Separatore linea (tipicamente x commenti) + /// + protected string lineSep = "--------------------------"; + + /// + /// Elenco parametri calcolati da inviare alla macchina (es ricetta con traduzione, RAMA/TFT) + /// + protected List list2Write = new List(); + + /// + /// Elenco delle eventuali condizioni di veto conditions attive + /// + protected List ListVetoCond = new List(); + + /// + /// Num massimo di errori in funzionalità check alive + /// + protected int maxAliveErrors = utils.CRI("maxAliveErrors"); + + /// + /// Soglia massima errori prima della disconnessione automatica in check + /// + protected int maxErroriCheck = utils.CRI("maxErroriCheck"); + + /// + /// Quantità massima per singola ricetta (per determinare num ricette da inviare) + /// + protected int maxQtyPerFile = 0; + /// /// Dimensione coda di ping x valutazione /// protected int maxQueuePing = 11; + /// + /// Num massimo di errori di rete read (dal PLC) + /// + protected int maxReadErrors = utils.CRI("maxReadErrors"); + + /// + /// Periodo massimo (in sec) per letture dati in mancanza di eventi di aggiornamento + /// + protected int MaxSecReload = 120; + + /// + /// Num massimodi errori di rete send al server + /// + protected int maxSendErrors = utils.CRI("maxSendErrors"); + + /// + /// Numero massimo di tentativi x test ping preliminare + /// + protected int maxTryPing = 1; + + protected string mem2trace = ""; + /// /// Tempo minimo ammissibile di risposta (es x errori ModBus che risponde troppo in fretta) in millisec /// protected int minRespTimeMs = 5; + protected int minVetoSendDataItem = 60; + + /// + /// indica se serva refresh parametri e quindi PLC... + /// + protected bool needRefresh = true; + + /// + /// Indica se usare la parte numerica di un articolo come codice INT + /// + protected bool numArtCharTrim = false; + + /// + /// Timeout x ping al server + /// + protected int pingServerMsTimeout = utils.CRI("PingMsTimeout"); + + /// + /// DataOra avvio Programma x check avvio + /// + protected DateTime prgStarted = DateTime.Now; + + /// + /// Ritardo minimo x invio contapezzi + /// + protected int pzCountDelay = 2500; + /// /// Indica se resettare allarmi all'avvio e inviare il reset appena parte adapter /// protected bool resetAlarmOnStart = false; + /// + /// Periodo di campionamento x refresh info + /// + protected int samplePeriod = 1000; + + /// + /// MsSample Period base x campionamenti tpo OCP-UA + /// + protected int samplePeriodBase = 600; + + /// + /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) + /// + protected Dictionary TSVC_Data = new Dictionary(); + + /// + /// Indica se usare archivio locale ricette vs scarico http/REST + /// + protected bool useLocalRecipe = true; + + /// + /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a + /// scadenza periodo...) + /// + protected Dictionary VarArray = new Dictionary(); + + /// + /// Dizionario dei divieti di invio x ogni counter gestito + /// + protected Dictionary VetoCounterSend = new Dictionary(); + /// /// Veto per registrazione completa log di startup (minuti) /// protected int vetoLogStartupDuration = 60; + /// + /// Durata in secondi del divieto accodamento segnali IN alla fase di startup + /// + protected int vetoQueueIn = 10; + + /// + /// Periodo di veto prima di invio nuova conferma dei valori write correnti, default ogni 5 min + /// + protected double vetoSendWriteUpsert = 1; + + /// + /// Periodo wathdog di default (2 sec se non specificato) + /// + protected int watchDogPeriod = 2; + #endregion Protected Fields #region Protected Properties @@ -463,6 +824,88 @@ namespace IOB_UT_NEXT.Iob #region Protected Methods + /// + /// Decodifica file MAP (caso .bit) + /// + /// + /// + /// indirizzo Byte: indirizzo di partenza memoria + /// dimensione singolo slot in byte + /// indirizzo bit: numero riga x calcolo indice bit + /// + protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + int resto = 0; + Math.DivRem(BitNum, 8, out resto); + string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + /// + /// Decodifica file MAP generico + /// + /// + /// + /// + /// + /// + /// + protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) + { + if (linea != null) + { + string[] valori = linea.Split(separator); + int shift = 0; + try + { + shift = Convert.ToInt32(valori[0]) - 1; + } + catch + { } + string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); + return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); + } + else + { + return null; + } + } + + protected static long GetObjectSize(object genObj, bool isSerializable) + { + long result = 0; + if (isSerializable) + { + using (var stream = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(stream, genObj); + result = stream.Length; + } + } + else + { + string rawVal = System.Text.Json.JsonSerializer.Serialize(genObj, options); + result = rawVal.Length; + } + return result; + } + /// /// Verifica se ci sia veto log attivo e gestisce casistiche /// @@ -560,6 +1003,21 @@ namespace IOB_UT_NEXT.Iob /// protected string GetWeekStatsKey() => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:WeekStats"); + /// + /// Deserializza una stringa JSON in un oggetto. + /// + protected T JsonDeserialize(string json) => DataSerializer.Deserialize(json); + + /// + /// Serializza un oggetto in formato JSON. + /// + protected string JsonSerialize(T obj) => DataSerializer.Serialize(obj); + + /// + /// Serializza un oggetto in formato JSON con opzioni serializzazione. + /// + protected string JsonSerialize(T obj, Formatting reqFormat) => DataSerializer.Serialize(obj, reqFormat); + /// /// Imposta un valore nel hash dello stato dell'IOB. /// @@ -590,36 +1048,17 @@ namespace IOB_UT_NEXT.Iob QueueULog = new DataQueue(codIob, "QueueULog", false, redisMan); } - #endregion Protected Methods - - #region Protected Serialization Helpers - /// - /// Serializza un oggetto in formato JSON. + /// Deserializza una stringa XML in un oggetto. /// - protected string JsonSerialize(T obj) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj); - - /// - /// Serializza un oggetto in formato JSON con opzioni serializzazione. - /// - protected string JsonSerialize(T obj, Formatting reqFormat) => IOB_UT_NEXT.Services.Data.DataSerializer.Serialize(obj, reqFormat); - - /// - /// Deserializza una stringa JSON in un oggetto. - /// - protected T JsonDeserialize(string json) => IOB_UT_NEXT.Services.Data.DataSerializer.Deserialize(json); + protected T XmlDeserialize(string xml) => XmlDataSerializer.Deserialize(xml); /// /// Serializza un oggetto in formato XML. /// - protected string XmlSerialize(T obj) => IOB_UT_NEXT.Services.Data.XmlDataSerializer.Serialize(obj); + protected string XmlSerialize(T obj) => XmlDataSerializer.Serialize(obj); - /// - /// Deserializza una stringa XML in un oggetto. - /// - protected T XmlDeserialize(string xml) => IOB_UT_NEXT.Services.Data.XmlDataSerializer.Deserialize(xml); - - #endregion Protected Serialization Helpers + #endregion Protected Methods #region Private Fields @@ -628,7 +1067,11 @@ namespace IOB_UT_NEXT.Iob /// private static readonly Logger lg = LogManager.GetCurrentClassLogger(); - private static Random rnd = new Random(); + private static readonly JsonSerializerOptions options = new JsonSerializerOptions + { + ReferenceHandler = ReferenceHandler.IgnoreCycles, + WriteIndented = false // Optional: set to true for pretty-printing + }; #endregion Private Fields } diff --git a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs index 5accac77..e04741d1 100644 --- a/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs +++ b/IOB-UT-NEXT/Iob/Services/Machine/MachineCommunicationService.cs @@ -57,6 +57,14 @@ namespace IOB_UT_NEXT.Services.Machine set => _tcMan.pzCountPLC = value; } + /// + /// Abilitazione allarme in caso di TC superiore a soglia + /// + public bool AlarmDelayTC + { + get => _tcMan.alarmDelayTC; + } + /// /// Legge un valore dalla memoria condivisa (MemMap) ricevuta dal PLC. /// diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index a5a05a83..0a6ff827 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -41,13 +41,6 @@ namespace IOB_WIN_FORM.Iob { public partial class Generic : BaseObj { - #region Private Fields - - private CommunicationService commService; - private MachineCommunicationService machineCommService; - - #endregion Private Fields - #region Public Fields public int numPzReqOdl = 0; @@ -73,6 +66,9 @@ namespace IOB_WIN_FORM.Iob // init oggetto redis... redisMan = new RedisIobCache(IobConfNew.MapoMes.IpAddr, IobConfNew.General.FilenameIOB, $"{IobConfNew.General.IobType}", IobConfNew.General.MinDeltaSec); + // init oggetto TCMan + var tcMan = new TCMan(IobConfNew.TCDataConf.Lambda, IobConfNew.TCDataConf.MaxDelayFactor, IobConfNew.TCDataConf.MaxIncrPz); + // init communication services commService = new CommunicationService(IobConfNew, redisMan); machineCommService = new MachineCommunicationService(IobConfNew, tcMan, memMap); @@ -80,8 +76,6 @@ namespace IOB_WIN_FORM.Iob // init code SetupQueue(); - // initi oggetto TCMan - tcMan = new TCMan(IobConfNew.TCDataConf.Lambda, IobConfNew.TCDataConf.MaxDelayFactor, IobConfNew.TCDataConf.MaxIncrPz); lastConnectTry = DateTime.Now; @@ -288,18 +282,6 @@ namespace IOB_WIN_FORM.Iob } } -#if false - /// - /// Valore medio del TC rilevato x verifica derive sul delta variazione contapezzi - /// - public double plcAvgTc => tcMan.avgTC > 0 ? tcMan.avgTC : 1; - - /// - /// DataOra dell'ultima lettura variabile contapezzi da CNC - /// - public DateTime plcLastPzRead => tcMan.lastObservedData; - -#endif /// /// Contapezzi attuale /// @@ -1129,14 +1111,14 @@ namespace IOB_WIN_FORM.Iob case taskType.forceSetPzCount: // recupero dati da memMap... - if (machineCommService.WriteToMemMap(iKey, item.Value)) - { - taskVal = $"SET task: {iKey} --> {item.Value} | mem: [Updated via MachineComm]"; - } - else - { - taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}"; - } + if (machineCommService.WriteToMemMap(iKey, item.Value)) + { + taskVal = $"SET task: {iKey} --> {item.Value} | mem: [Updated via MachineComm]"; + } + else + { + taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}"; + } lgInfo($"Chiamata forceSetPzCount: taskVal: {taskVal}"); @@ -3834,362 +3816,11 @@ namespace IOB_WIN_FORM.Iob } #endregion Private Methods + #region Protected Fields - /// - /// Variabile numero errori vari (in lettura) --> se supera soglia maxErroriCheck --> disconnette - /// - protected static int numErroriCheck = 0; - - protected bool _connOk = false; - - /// - /// valore booleano di check se sia in fase di COMUNICAZIONE ATTIVA con il PLC/NC - /// - protected bool adpCommAct; - - /// - /// porta x adapter (x restart) - /// - protected int adpPortNum; - - /// - /// DataOra ultimo avvio adapter x watchdog - /// - protected DateTime adpStartRun; - - /// - /// Vettore 32 BIT valori in ingresso al filtro - /// - protected int B_input; - - /// - /// Vettore 32 BIT valori in uscita dal filtro - /// - protected int B_output; - - /// - /// Vettore 32 BIT valori precedenti - /// - protected int B_previous = -1; - - /// - /// Cod grupo IOB x creazione PODL al volo - /// - protected string CodGruppoIob = "ND-00"; - - /// - /// Num errori check alive - /// - protected int currAliveErrors = 0; - - /// - /// Dizionario valori impostati x produzione - /// - protected Dictionary currProdData = new Dictionary(); - - /// - /// num corrente errori read PLC - /// - protected int currReadErrors = 0; - - /// - /// num errori send - /// - protected int currSendErrors = 0; - - /// - /// Tempo di attesa in minuti x lettura contapezzi standard (da .ini / OptPar) - /// - protected double delayMinReadPzCount = 0; - - /// - /// Fattore di demoltiplicazione dei DynData x ridurre campionamento - /// - protected int demFactDynData = 1; - - /// - /// Boolean x indicare contapezzi disabilitato forzatamente da IOB - /// - protected bool disablePzCountByIob = false; - - /// - /// Veto x esecuzione task AutoDossier - /// - protected DateTime dtVetoAutoDossier = DateTime.Now; - - /// - /// Veto x esecuzione Task2Exe troppo frequenti - /// - protected DateTime dtVetoTask2Exe = DateTime.Now; - - /// - /// Veto x lettura contapezzi (in caso di autoODL con attesa reset ad esempio...) - /// - protected DateTime dtVetoReadPzCount = DateTime.Now; - - /// - /// Determina se sia prevista gestione PODL (creazione/avvio/chiusura) come Soitaab - /// - protected bool EnabelPodlManFull = false; - - /// - /// Abilita riscrittura memoria se trova differenze lettura/richiesti - /// - protected bool ENABLE_MEM_REWRITE = true; - - /// - /// Abilitazione restart (da opt par...) - /// - protected bool enableCliRestart = false; - - /// - /// Abilitazione invio dataitem - /// - protected bool enableSendDataItem = true; - protected int minVetoSendDataItem = 60; - /// - /// DataOra x veto all'invio dataItem - /// - protected DateTime dtVetoSenDataItem = DateTime.Now; - - /// - /// Boolean x indicare contapezzi abilitato a livello di conf applicazione - /// - protected bool enablePzCountByApp = true; - - /// - /// Abilitazione gestione slow data - /// - protected bool enableSlowData = false; - - /// - /// dizionario (opzionale) xz decodifica file da importare - /// - protected Dictionary FileDecod = new Dictionary(); - - /// - /// DeadBand x riduzione dati FluxLog (se 0 non gestita) - /// - protected double fluxLogRedDeadBand = 0; - - /// - /// Determina se sia gestita riduzione dati FluxLog - /// - protected bool fluxLogReduce = false; - - /// - /// Dizionario dei valori FluxLog ultimi verificati x veto - /// - protected Dictionary fluxLogReduceLast = new Dictionary(); - - /// - /// Dizionario dei valori FluxLog ultimi tipo STRING verificati x veto - /// - protected Dictionary fluxLogReduceLastString = new Dictionary(); - - /// - /// Dizionario dei veto send x ogni variabile quando non variata - /// - protected Dictionary fluxLogReduceVeto = new Dictionary(); - - /// - /// Finestra in minuti x invio dati FluxLog quando invariati - /// - protected int fluxLogResendPeriod = 60; - - /// - /// indica che è richiesto forzatamente reset contapezzi - /// - protected bool forcePzReset = false; - - /// - /// indica che è richiesto forzatamente reset contapezzi - /// - protected DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1); - - /// - /// DEADBAND globale se impostata (es x gestire variazioni minime valori FluxLog da inviare...) - /// - protected float GLOBAL_DBAND = 0; - - /// - /// Determina se siano gestite le ricette - /// - protected bool hasRecipe = false; - - /// - /// Array dei contatori x segnali blinking - /// - protected int[] i_counters; - - /// - /// Indica impianto IN SETUP (fino a quando SMETTE di esserlo...) - /// - protected bool inSetup = false; - - /// - /// ultimo tentativo connessione... - /// - protected DateTime lastConnectTry; - - /// - /// Dizionario ULTIMI valori impostati x produzione - /// - protected Dictionary lastProdData = new Dictionary(); - - /// - /// Ultimo invio contapezzi (x invio delayed) - /// - protected DateTime lastPzCountSend; - - /// - /// Dizionario ultimi valori (string) delle TSS - /// - protected Dictionary LastTSS = new Dictionary(); - - /// - /// Dizionario ultimi valori (string) delle TSS - /// - protected Dictionary LastTSSSend = new Dictionary(); - - /// - /// Dizionario ultimi valori (double) delle TSVC - /// - protected Dictionary LastTSVC = new Dictionary(); - - /// - /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) - /// - protected DateTime lastWarnODL; - - /// - /// ultima data-ora invio parametri write x ribadire status - /// - protected DateTime lastWriteParamsUpsert = DateTime.Now; - - /// - /// Separatore linea (tipicamente x commenti) - /// - protected string lineSep = "--------------------------"; - - /// - /// Elenco parametri calcolati da inviare alla macchina (es ricetta con traduzione, RAMA/TFT) - /// - protected List list2Write = new List(); - - /// - /// Elenco delle eventuali condizioni di veto conditions attive - /// - protected List ListVetoCond = new List(); - - /// - /// Num massimo di errori in funzionalità check alive - /// - protected int maxAliveErrors = utils.CRI("maxAliveErrors"); - - /// - /// Soglia massima errori prima della disconnessione automatica in check - /// - protected int maxErroriCheck = utils.CRI("maxErroriCheck"); - - /// - /// Quantità massima per singola ricetta (per determinare num ricette da inviare) - /// - protected int maxQtyPerFile = 0; - - /// - /// Num massimo di errori di rete read (dal PLC) - /// - protected int maxReadErrors = utils.CRI("maxReadErrors"); - - /// - /// Periodo massimo (in sec) per letture dati in mancanza di eventi di aggiornamento - /// - protected int MaxSecReload = 120; - - /// - /// Num massimodi errori di rete send al server - /// - protected int maxSendErrors = utils.CRI("maxSendErrors"); - - /// - /// Numero massimo di tentativi x test ping preliminare - /// - protected int maxTryPing = 1; - - protected string mem2trace = ""; - - /// - /// indica se serva refresh parametri e quindi PLC... - /// - protected bool needRefresh = true; - - /// - /// Indica se usare la parte numerica di un articolo come codice INT - /// - protected bool numArtCharTrim = false; - - /// - /// Timeout x ping al server - /// - protected int pingServerMsTimeout = utils.CRI("PingMsTimeout"); - - /// - /// DataOra avvio Programma x check avvio - /// - protected DateTime prgStarted = DateTime.Now; - - /// - /// Ritardo minimo x invio contapezzi - /// - protected int pzCountDelay = 2500; - - /// - /// Periodo di campionamento x refresh info - /// - protected int samplePeriod = 1000; - - /// - /// MsSample Period base x campionamenti tpo OCP-UA - /// - protected int samplePeriodBase = 600; - - /// - /// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...) - /// - protected Dictionary TSVC_Data = new Dictionary(); - - /// - /// Indica se usare archivio locale ricette vs scarico http/REST - /// - protected bool useLocalRecipe = true; - - /// - /// Dizionario di VARIABILI da trattare come eventi (da inviare quando cambiano oppure a - /// scadenza periodo...) - /// - protected Dictionary VarArray = new Dictionary(); - - /// - /// Dizionario dei divieti di invio x ogni counter gestito - /// - protected Dictionary VetoCounterSend = new Dictionary(); - - /// - /// Durata in secondi del divieto accodamento segnali IN alla fase di startup - /// - protected int vetoQueueIn = 10; - - /// - /// Periodo di veto prima di invio nuova conferma dei valori write correnti, default ogni 5 min - /// - protected double vetoSendWriteUpsert = 1; - - /// - /// Periodo wathdog di default (2 sec se non specificato) - /// - protected int watchDogPeriod = 2; + protected CommunicationService commService; + protected MachineCommunicationService machineCommService; #endregion Protected Fields @@ -4938,87 +4569,7 @@ namespace IOB_WIN_FORM.Iob #region Protected Methods - /// - /// Decodifica file MAP (caso .bit) - /// - /// - /// - /// indirizzo Byte: indirizzo di partenza memoria - /// dimensione singolo slot in byte - /// indirizzo bit: numero riga x calcolo indice bit - /// - protected static otherData decodeBitData(string linea, char separator, int ByteNum, int memSize, int BitNum) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - int resto = 0; - Math.DivRem(BitNum, 8, out resto); - string memAddr = string.Format("{0}.{1}", ByteNum + shift * memSize, resto); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - /// - /// Decodifica file MAP generico - /// - /// - /// - /// - /// - /// - /// - protected static otherData decodeOtherData(string linea, char separator, string memPre, int baseAddr, int memSize) - { - if (linea != null) - { - string[] valori = linea.Split(separator); - int shift = 0; - try - { - shift = Convert.ToInt32(valori[0]) - 1; - } - catch - { } - string memAddr = string.Format("{0}{1}", memPre, baseAddr + shift * memSize); - return new otherData(valori[0], memAddr, valori[1].Trim(), valori[2].Trim()); - } - else - { - return null; - } - } - - protected static long GetObjectSize(object genObj, bool isSerializable) - { - long result = 0; - if (isSerializable) - { - using (var stream = new MemoryStream()) - { - var formatter = new BinaryFormatter(); - formatter.Serialize(stream, genObj); - result = stream.Length; - } - } - else - { - string rawVal = System.Text.Json.JsonSerializer.Serialize(genObj, options); - result = rawVal.Length; - } - return result; - } /// /// Decodifica valore della coda IN nel formato sendEnab[0]=dtEve sendEnab[1]=valore sendEnab[2]=counter @@ -6331,10 +5882,9 @@ namespace IOB_WIN_FORM.Iob { foreach (var item in writeList) { - // scrivo in memoria - if (memMap.mMapWrite.ContainsKey(item.uid)) + // scrivo in memoria tramite servizio + if (machineCommService.WriteToMemMap(item.uid, item.reqValue)) { - memMap.mMapWrite[item.uid].value = item.reqValue; currOut = $" | Parameter {item.uid} | {item.value} --> {item.reqValue}"; // sistemo valori item.value = item.reqValue; @@ -6344,6 +5894,7 @@ namespace IOB_WIN_FORM.Iob // salvo in lista da ritrasmettere updatedPar.Add(item); } + else { currOut = $" | Error: parameter {item.uid} not found"; @@ -8473,11 +8024,7 @@ namespace IOB_WIN_FORM.Iob #region Private Fields - private static readonly JsonSerializerOptions options = new JsonSerializerOptions - { - ReferenceHandler = ReferenceHandler.IgnoreCycles, - WriteIndented = false // Optional: set to true for pretty-printing - }; + /// /// Oggetto logger della classe diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 0f70f550..f075c8c2 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -60,18 +60,13 @@ namespace IOB_WIN_FORM.Iob #endregion Public Fields - #region Private Fields for Dirty Check - private int lastSentPzCount = -1; - private DateTime lastBitmapDecodeTime = DateTime.MinValue; - #endregion - #region Public Constructors public Simula(AdapterForm caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { // jitter di avvio per disallineare le istanze nella VM Random startRnd = new Random(); - int startDelay = startRnd.Next(0, 5000); + int startDelay = startRnd.Next(0, 5000); Thread.Sleep(startDelay); // gestione invio ritardato contapezzi @@ -95,13 +90,13 @@ namespace IOB_WIN_FORM.Iob { int basePeriod = 0; int.TryParse(IOBConfFull.OptParGet("PER_BASE"), out basePeriod); - + // Applicazione Jitter sui periodi per evitare sincronizzazione tra VM Random rnd = new Random(); // Aggiungiamo un rumore del +/- 15% e un jitter extra - double noiseFactor = 0.85 + (rnd.NextDouble() * 0.30); + double noiseFactor = 0.85 + (rnd.NextDouble() * 0.30); periodoMSec = (int)(basePeriod * noiseFactor); - + // Assicuriamo un minimo di jitter per evitare che periodi simili si allineino periodoMSec += rnd.Next(1, 500); } @@ -157,12 +152,12 @@ namespace IOB_WIN_FORM.Iob // carica conf try { - // Invece di bloccare il costruttore con .GetAwaiter().GetResult(), + // Invece di bloccare il costruttore con .GetAwaiter().GetResult(), // avviamo il carico in modo "fire-and-forget" controllato. // Questo evita il context switch pesante durante l'inizializzazione. _ = Task.Run(async () => { - try + try { await Simula_Load(adesso); } @@ -973,10 +968,10 @@ namespace IOB_WIN_FORM.Iob item.reqValue = ""; MemBlock = new byte[byteSize]; - serObj = JsonSerialize(item, Formatting.Indented); - lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); - serObj = JsonSerialize(currMem, Formatting.Indented); - lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + serObj = JsonSerialize(item, Formatting.Indented); + lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + serObj = JsonSerialize(currMem, Formatting.Indented); + lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); lgInfo($"---------------MemBlock data---------------{Environment.NewLine}{BitConverter.ToString(MemBlock)}{Environment.NewLine}--------------- END data ---------------"); // salvo @@ -1002,12 +997,12 @@ namespace IOB_WIN_FORM.Iob { try { - // Eseguiamo il lavoro in un Task per non bloccare il thread chiamante, + // Eseguiamo il lavoro in un Task per non bloccare il thread chiamante, // ma evitiamo il .GetAwaiter().GetResult() che causerebbe un blocco sincrono pesante. // In un ambiente WinForms, questo permette al thread di polling di non restare in attesa attiva. _ = Task.Run(async () => { - try + try { // Esecuzione dei task sincroni esistenti iobWriteLocalCSV(); @@ -1102,9 +1097,8 @@ namespace IOB_WIN_FORM.Iob #region Private Fields private List alarmMessages = new List(); - private int currSimAlarmCode = 0; - + private DateTime lastBitmapDecodeTime = DateTime.MinValue; private Random rnd = new Random(); #endregion Private Fields @@ -1475,7 +1469,7 @@ namespace IOB_WIN_FORM.Iob } } } - else if (bit5 != null && bit5.wait <= 0 || tcMan.alarmDelayTC) + else if (bit5 != null && bit5.wait <= 0 || machineCommService.AlarmDelayTC) { // segnalo BIT B_input += (1 << 5); @@ -1760,8 +1754,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaArt = JsonDeserialize>(rawListArt); - okArt = listaArt.Count > 0; + listaArt = JsonDeserialize>(rawListArt); + okArt = listaArt.Count > 0; } catch (Exception exc) { @@ -1776,8 +1770,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaDoss = JsonDeserialize>(rawListDOSS); - okDoss = listaDoss.Count > 0; + listaDoss = JsonDeserialize>(rawListDOSS); + okDoss = listaDoss.Count > 0; } catch (Exception exc) { @@ -1792,8 +1786,8 @@ namespace IOB_WIN_FORM.Iob { try { - listaPODL = JsonDeserialize>(rawListPODL); - okPodl = listaPODL.Count > 0; + listaPODL = JsonDeserialize>(rawListPODL); + okPodl = listaPODL.Count > 0; } catch (Exception exc) { @@ -1828,7 +1822,7 @@ namespace IOB_WIN_FORM.Iob if (selArt != null && selDoss != null) { // recupero il resultset dei valori FluxLog da caricare - DossierFluxLogDTO resultSet = JsonDeserialize(selDoss.Valore); + DossierFluxLogDTO resultSet = JsonDeserialize(selDoss.Valore); List listFluxLog = resultSet.ODL; // preparo elenco parametri da inviare... @@ -2167,7 +2161,7 @@ namespace IOB_WIN_FORM.Iob /// private void sendDataItemsList(List dataItems) { - string rawData = JsonSerialize(dataItems); + string rawData = JsonSerialize(dataItems); HttpService.CallUrlPost($"{urlSaveDataItems}", rawData); } From f21754cf5a42a11cbddd92fb02152cb7d16b9234 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 12:47:52 +0200 Subject: [PATCH 28/39] Continuo modifiche --- AGENTS.md | 6 +++--- AGENTS.pdf | Bin 0 -> 97423 bytes IOB-WIN-FORM/Iob/BaseObj.cs | 1 + IOB-WIN-FORM/Iob/Generic.cs | 10 ++++++---- 4 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 AGENTS.pdf diff --git a/AGENTS.md b/AGENTS.md index cee8e2b8..a7b7a80b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,11 +4,11 @@ A collection of .NET (C#/VB) projects for industrial communication with NC controls (Fanuc, Siemens, Mitsubishi, etc.) via the Mapo Protocol. ## Build & Release -- **Toolchain**: MSBuild (`x86` target). -- **Config**: Always use `Release` configuration and `x86` platform. +- **Toolchain**: MSBuild. +- **Config**: Always use `Release` configuration. - **Command Example**: ```powershell - & "$env:MSBUILD_PATH" "ProjectName\ProjectName.csproj" -target:Build /p:Configuration=Release /p:Platform="x86" /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m + & "$env:MSBUILD_PATH" "ProjectName\ProjectName.csproj" -target:Build /p:Configuration=Release /p:OutputPath=bin/ /nodeReuse:false /verbosity:minimal /m ``` - **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. diff --git a/AGENTS.pdf b/AGENTS.pdf new file mode 100644 index 0000000000000000000000000000000000000000..07040a28bc111457e160fb61958af2c6027a3cfc GIT binary patch literal 97423 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

mPDgyuy3Nbo)`v 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 - // processo! var reqDict = JobTaskData.TaskDict(jobTaskReq.RawData); if (reqDict.Count > 0) @@ -3820,7 +3819,7 @@ namespace IOB_WIN_FORM.Iob #region Protected Fields protected CommunicationService commService; - protected MachineCommunicationService machineCommService; + protected MachineCommunicationService machineCommService; #endregion Protected Fields @@ -5068,7 +5067,8 @@ namespace IOB_WIN_FORM.Iob } var rawData = HttpService.CallUrlGet(url2call); // deserializzo e recupero KVP... - var dictArtSrv = JsonConvert.DeserializeObject>(rawData); + var dictArtSrv = JsonDeserialize>(rawData); + // se fosse una chiamata con valore vuoto --> salvo intera tabella... if (string.IsNullOrEmpty(value)) { @@ -6110,6 +6110,7 @@ namespace IOB_WIN_FORM.Iob string rawData = File.ReadAllText(recipeFile); // deserializza file XML x recuperare righe consumo var currRecipe = XmlDeserialize(rawData); + if (currRecipe != null) { // recupero data-ora ricetta da campo string @@ -8024,7 +8025,7 @@ namespace IOB_WIN_FORM.Iob #region Private Fields - + ///

/// Oggetto logger della classe @@ -8996,6 +8997,7 @@ namespace IOB_WIN_FORM.Iob { // deserializzo... JobTaskData jobTask = JsonDeserialize(rawJob); + // ora chiamo la cancellazione dei task eseguiti... var taskDict = JobTaskData.TaskDict(jobTask.RawData); foreach (var item in taskDict) From 38d1c8f5654091817c554a0027cd3f42311f0c26 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 13:01:42 +0200 Subject: [PATCH 29/39] Aggiunta refactor log precedente --- refactor_log.md | 39 +++++++++++++++++++++++++++++++++++++++ refactor_log.pdf | Bin 0 -> 100334 bytes 2 files changed, 39 insertions(+) create mode 100644 refactor_log.md create mode 100644 refactor_log.pdf diff --git a/refactor_log.md b/refactor_log.md new file mode 100644 index 00000000..77863c74 --- /dev/null +++ b/refactor_log.md @@ -0,0 +1,39 @@ +# Log 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 (COMPLETED/REFACTORED) +- [x] Identify redundant `callUrl` and `callUrlWithPayloadAsync` wrappers in `BaseObj.cs`. +- [x] Instead of creating a new service, cleaned up `BaseObj.cs` by removing pass-through methods that merely delegated to `utils`. +- [x] Updated `Generic.cs` to call `HttpService.CallUrl` directly, eliminating the unnecessary indirection layer. + +### 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. +- [x] Eliminated `RedisService.cs` as it was absorbed by the `BaseObj` identity-aware implementation. + +### 3. Data Serializer Extraction (IN_PROGRESS) +- [x] Analyzed current `DataSerializer.cs` (JSON) and `XmlDataSerializer.cs` (XML) in `IOB-WIN-FORM`. +- [x] Decided to move these to `IOB_UT_NEXT` to make them available to the entire IOB hierarchy. +- [ ] Move files to `IOB_UT_NEXT\Iob\Services\`. +- [ ] Update namespaces and references. +- [ ] Integrate into `BaseObj.cs` via `protected` helper methods (e.g., `JsonSerialize`, `XmlSerialize`). +- [ ] Update `Generic.cs` to use the new `BaseObj` helpers. + +### 4. BaseObj Simplification (COMPLETED) +- [x] Analyze `BaseObj` responsibilities (State, Config, Messaging, Diagnostics). +- [x] Remove pass-through methods for `utils` (Network, MAC, WebClients) to reduce "noise" in the base class. +- [x] Cleaned up commented-out code blocks (`#if false`) to improve readability. + +## Progress Log +- [x] Created WIP document. +- [x] Completed Redis Semantic Refactoring. +- [x] Completed BaseObj Cleanup. +- [x] Identified Data Serializer components for extraction. +- [ ] Started migration of Serializers to `IOB_UT_NEXT`. diff --git a/refactor_log.pdf b/refactor_log.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6c334dd8e466ba4ce3c32f5fd10f40b4d7548de1 GIT binary patch literal 100334 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 Date: Sat, 23 May 2026 17:24:46 +0200 Subject: [PATCH 30/39] update pianificazione steps --- refactoring_wip.md | 49 ++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/refactoring_wip.md b/refactoring_wip.md index 8675acf4..5e09b4e1 100644 --- a/refactoring_wip.md +++ b/refactoring_wip.md @@ -1,39 +1,16 @@ -# WIP: Refactoring Phase 1 - Infrastructure Extraction +# Refactoring Progress: IOB-WIN-FORM (Generic.cs) -## Status: IN_PROGRESS +## Obiettivo +Riduzione Context Switching e CPU Load tramite eliminazione Sync-over-Async e ottimizzazione I/O. -## Objective -Extract infrastructure and helper components from `Generic.cs` to improve modularity and reduce the monolithic footprint. +## Stato Attuale +- [x] Analisi iniziale del codice `Generic.cs` +- [ ] Mappatura dipendenze (Analisi firme metodi per evitare breaking changes) +- [ ] Implementazione Fase A (Dirty Checks & Throttling) +- [ ] Implementazione Fase B (Internal Async Refactoring) +- [ ] Implementazione Fase C (Signature Migration) -## Tasks - -### 1. Communication Service Extraction (COMPLETED/REFACTORED) -- [x] Identify redundant `callUrl` and `callUrlWithPayloadAsync` wrappers in `BaseObj.cs`. -- [x] Instead of creating a new service, cleaned up `BaseObj.cs` by removing pass-through methods that merely delegated to `utils`. -- [x] Updated `Generic.cs` to call `HttpService.CallUrl` directly, eliminating the unnecessary indirection layer. - -### 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. -- [x] Eliminated `RedisService.cs` as it was absorbed by the `BaseObj` identity-aware implementation. - -### 3. Data Serializer Extraction (IN_PROGRESS) -- [x] Analyzed current `DataSerializer.cs` (JSON) and `XmlDataSerializer.cs` (XML) in `IOB-WIN-FORM`. -- [x] Decided to move these to `IOB_UT_NEXT` to make them available to the entire IOB hierarchy. -- [ ] Move files to `IOB_UT_NEXT\Iob\Services\`. -- [ ] Update namespaces and references. -- [ ] Integrate into `BaseObj.cs` via `protected` helper methods (e.g., `JsonSerialize`, `XmlSerialize`). -- [ ] Update `Generic.cs` to use the new `BaseObj` helpers. - -### 4. BaseObj Simplification (COMPLETED) -- [x] Analyze `BaseObj` responsibilities (State, Config, Messaging, Diagnostics). -- [x] Remove pass-through methods for `utils` (Network, MAC, WebClients) to reduce "noise" in the base class. -- [x] Cleaned up commented-out code blocks (`#if false`) to improve readability. - -## Progress Log -- [x] Created WIP document. -- [x] Completed Redis Semantic Refactoring. -- [x] Completed BaseObj Cleanup. -- [x] Identified Data Serializer components for extraction. -- [ ] Started migration of Serializers to `IOB_UT_NEXT`. +## Log Modifiche e Test di Build +| Data | Metodo/Area | Tipo Modifica | Esito Build (`--agent`) | Note/Errori | +| :--- | :--- | :--- | :--- | :--- | +| 2026-05-23 | Inizio | Analisi | N/A | Identificati colli di bottiglia (Sync-over-Async, Log eccessivo, lack of Dirty Checks) | From cbee42e466463c32277aa57bb6c0ba3cf45f2ed9 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 17:46:02 +0200 Subject: [PATCH 31/39] Aggiunnto altro piano di refactor da verificare --- refactor_progress_tracker.md | 83 ++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 refactor_progress_tracker.md diff --git a/refactor_progress_tracker.md b/refactor_progress_tracker.md new file mode 100644 index 00000000..f1820d9c --- /dev/null +++ b/refactor_progress_tracker.md @@ -0,0 +1,83 @@ +# 🚀 Piano di Refactoring e Monitoraggio: Ottimizzazione Simula.cs + +Questo documento funge da **Master Tracker** per il processo di refactoring della classe `Simula.cs`. Serve a monitorare lo stato di avanzamento di ogni singolo intervento, garantendo che ogni modifica sia mappata e verificata. + +--- + +## 📊 Stato di Avanzamento Generale +**Progresso Totale:** `[░░░░░░░░░░░░░░░░░░░░] 0%` + +| Fase | Obiettivo | Stato | +| :--- | :--- | :---: | +| **Fase 1** | **Async Optimization** (Eliminazione Sync-over-Async) | ⚪ NOT STARTED | +| **Fase 2** | **Throttling I/O** (Dirty Check & Rate Limiting) | ⚪ NOT STARTED | +| **Fase 3** | **Jittering & Staggering** (Disallineamento istanze) | ⚪ NOT STARTED | +| **Fase 4** | **Verbosity Control** (Logging Optimization) | ⚪ NOT STARTED | +| **Fase 5** | **Verifica & Test** (Stress Test & Monitoraggio CPU) | ⚪ NOT STARTED | + +--- + +## 🛠️ Dettaglio Interventi + +### 1. ⚡ Fase 1: Async Optimization (Priorità Massima) +*Obiettivo: Eliminare il context switching e il Thread Pool Starvation.* + +- [ ] **[TASK-1.1] Refactoring `IobGetDataFromServerToFtp`** + - *Problema:* Uso di `.GetAwaiter().GetResult()` (Line 1670). + - *Azione:* Trasformare il metodo in `async Task` e usare `await` per le chiamate `HttpService`. +- [ ] **[TASK-1.2] Refactoring `IobGetSendDossierKepware`** + - *Problema:* Uso di `.GetAwaiter().GetResult()` (Line 1802). + - *Azione:* Trasformare in `async Task`. +- [ ] **[TASK-1.3] Audit `Task.Run` (Fire-and-Forget)** + - *Problema:* Potenziali fughe di thread o eccezioni silenziate (Line 158, 1003, 1597, 1748). + - *Azione:* Implementare un wrapper `SafeTaskRun` con gestione log errori centralizzata. + +### 2. 🛡️ Fase 2: Throttling I/O & Dirty Check +*Obiettivo: Ridurre il traffico verso Redis e i servizi esterni.* + +- [ ] **[TASK-2.1] Implementazione Dirty Check `upsertKey`** + - *Problema:* Invio continuo di dati anche se invariati (Line 232, 298, 327, 978). + - *Azione:* Creare un `Dictionary _lastSentValues` per validare i dati prima di `upsertKey`. +- [ ] **[TASK-2.2] Rate Limiting `decodeToBaseBitmap`** + - *Problema:* Operazione pesante invocata troppo frequentemente (Line 612). + - *Azione:* Implementare un controllo temporale (es. max 1 esecuzione ogni 500ms). +- [ ] **[TASK-2.3] Throttling `trySendPzCountBlock`** + - *Problema:* Possibile saturazione chiamate HTTP per i contapezzi (Line 1246). + - *Azione:* Limitare la frequenza di invio tramite timer o flag di stato. +- [ ] **[TASK-2.4] Ottimizzazione chiamate `HttpService`** + - *Problema:* Chiamate REST ripetitive e non ottimizzate. + - *Azione:* Implementare un meccanismo di caching temporaneo per i dati che non cambiano rapidamente. + +### 3. 🎲 Fase 3: Jittering & Disallineamento +*Obiettivo: Distribuire il carico computazionale nel tempo tra le diverse VM.* + +- [ ] **[TASK-3.1] Jitter di Inizializzazione** + - *Problema:* Ritardo fisso o troppo breve all'avvio (Line 70). + - *Azione:* Estendere il range del `Random startDelay` e assicurarsi che non blocchi il thread principale. +- [ ] **[TASK-3.2] Jittering dei Periodi di Polling** + - *Problema:* Sincronizzazione dei task tra istanze diverse. + - *Azione:* Applicare un rumore statistico (jitter) a ogni iterazione dei cicli di polling/timer. + +### 4. 📝 Fase 4: Verbosity Control +*Obiettivo: Ridurre l'overhead di I/O disco e CPU per il logging.* + +- [ ] **[TASK-4.1] Pulizia Log ad alta frequenza** + - *Problema:* Log massivi di stringhe e memoria nei cicli critici (Line 972-975). + - *Azione:* Spostare i log di debug/trace in modalità non invasiva o eliminarli se ridondanti. + +--- + +## 🧪 Fase 5: Verifica e Validazione + +| Test Case | Risultato Atteso | Stato | Note | +| :--- | :--- | :---: | :--- | +| **Stress Test CPU** | Riduzione % CPU rispetto al baseline | ⚪ | | +| **Monitoraggio Context Switch** | Riduzione numero switch/sec | ⚪ | | +| **Test Correttezza Dati** | I dati inviati a Redis/MES sono identici | ⚪ | | +| **Test Error Handling** | Le eccezioni nei Task asincroni vengono loggate | ⚪ | | + +--- + +## 📓 Note e Osservazioni +*Aggiungere qui osservazioni durante lo sviluppo.* +- From ee31322dda8316f7538d56e9dbeca33ed326afc4 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:11:39 +0200 Subject: [PATCH 32/39] Spostamento aree protected da Generic a BaseObj --- IOB-WIN-FORM/Iob/BaseObj.cs | 4 +--- IOB-WIN-FORM/Iob/Simula.cs | 29 ++++------------------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/IOB-WIN-FORM/Iob/BaseObj.cs b/IOB-WIN-FORM/Iob/BaseObj.cs index b91449d2..1a3bdc6e 100644 --- a/IOB-WIN-FORM/Iob/BaseObj.cs +++ b/IOB-WIN-FORM/Iob/BaseObj.cs @@ -1,6 +1,4 @@ -using IOB_UT_NEXT; -using IOB_UT_NEXT.Objects; -using IOB_UT_NEXT.Services.Data; +using IOB_UT_NEXT.Objects; using NLog; using System; diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index f075c8c2..e8414a21 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -66,7 +66,7 @@ namespace IOB_WIN_FORM.Iob { // jitter di avvio per disallineare le istanze nella VM Random startRnd = new Random(); - int startDelay = startRnd.Next(0, 5000); + int startDelay = startRnd.Next(0, 200); Thread.Sleep(startDelay); // gestione invio ritardato contapezzi @@ -149,30 +149,9 @@ namespace IOB_WIN_FORM.Iob // simulazione processo FTP (Cimolai) - FIXME TODO DeleteME ProcessDataSync(); } - // carica conf - try - { - // Invece di bloccare il costruttore con .GetAwaiter().GetResult(), - // avviamo il carico in modo "fire-and-forget" controllato. - // Questo evita il context switch pesante durante l'inizializzazione. - _ = Task.Run(async () => - { - try - { - await Simula_Load(adesso); - } - catch (Exception ex) - { - lgError($"Errore in Simula_Load (background): {ex.Message}"); - } - }); - } - catch (Exception ex) - { - lgError($"Errore nel lancio del task Simula_Load: {ex.Message}"); - } } + #endregion Public Constructors #region Public Methods @@ -516,8 +495,8 @@ namespace IOB_WIN_FORM.Iob public override async Task InitializeAsync() { await Simula_Load(DateTime.Now); - // attendo altri 50ms - await Task.Delay(50); + // attendo altri 10ms + await Task.Delay(10); } /// From eaed03bb1f7fa0789a148e016d9fb546f8a6ac58 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:17:07 +0200 Subject: [PATCH 33/39] Inizio spostamento variabili DateTime in helper DateTimeHelper --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 1 + IOB-UT-NEXT/Iob/BaseObj.cs | 19 ++------------ IOB-UT-NEXT/Iob/DateTimeHelper.cs | 37 ++++++++++++++++++++++++++++ IOB-WIN-FANUC/Iob/Fanuc.cs | 2 +- IOB-WIN-FORM/AdapterForm.cs | 2 +- IOB-WIN-FORM/Iob/Generic.cs | 22 ++++++++--------- IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs | 2 +- IOB-WIN-OSAI/Iob/OSAI.cs | 2 +- IOB-WIN/IobGeneric.cs | 30 +++++++++++----------- IOB-WIN/IobOSAI.cs | 2 +- 10 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 IOB-UT-NEXT/Iob/DateTimeHelper.cs diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index 9b160f6d..c05344f2 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -156,6 +156,7 @@ + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 38bfb956..46d5e478 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -100,24 +100,9 @@ namespace IOB_UT_NEXT.Iob public bool doStartMemDump; /// - /// Data/ora ultimo avvio adapter + /// Collettore di tutte le variabili scadenza DateTime /// - public DateTime dtAvvioAdp = DateTime.Now; - - /// - /// Data/ora ultimo spegnimento adapter - /// - public DateTime dtStopAdp = DateTime.Now; - - /// - /// Indicazione VETO check status IOB x evitare loop troppo stretti... - /// - public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1); - - /// - /// Indicazione VETO check sync ricette x evitare loop troppo stretti... - /// - public DateTime dtVetoCheckSyncRecipe = DateTime.Now.AddHours(-1); + public DateTimeHelper DtHelp; /// /// Abilitazione lettura PrgName diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs new file mode 100644 index 00000000..482b7555 --- /dev/null +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IOB_UT_NEXT.Iob +{ + /// + /// Classe helper di tutti gli oggetti scadenze DateTime impiegate + /// + public class DateTimeHelper + { + + /// + /// Data/ora ultimo avvio adapter + /// + public DateTime AvvioAdp = DateTime.Now; + + /// + /// Data/ora ultimo spegnimento adapter + /// + public DateTime StopAdp = DateTime.Now; + + /// + /// Indicazione VETO check status IOB x evitare loop troppo stretti... + /// + public DateTime VetoCheckIOB = DateTime.Now.AddDays(-1); + + /// + /// Indicazione VETO check sync ricette x evitare loop troppo stretti... + /// + public DateTime VetoCheckSyncRecipe = DateTime.Now.AddHours(-1); + + + } +} diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index 14716a33..47d50602 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -1581,7 +1581,7 @@ namespace IOB_WIN_FANUC.Iob if (connectionOk) { checkVetoQueueIn(); - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; if (adpRunning) { lgInfo("Connessione OK"); diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 7e67bab7..2a2292ba 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -2153,7 +2153,7 @@ namespace IOB_WIN_FORM Dictionary setPar = new Dictionary(); setPar.Add("IP", iobObj.IOBConfFull.Device.Connect.IpAddr); setPar.Add("PORT", iobObj.IOBConfFull.Device.Connect.Port); - string note = $"{iobObj.IOBConfFull.General.FilenameIOB} | DT ultimo avvio: {iobObj.dtAvvioAdp}"; + string note = $"{iobObj.IOBConfFull.General.FilenameIOB} | DT ultimo avvio: {iobObj.DtHelp.AvvioAdp}"; // verifico IOB status IobWinStatus currIobStatus = new IobWinStatus() { diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 5eaf25dc..3acbcff3 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -309,7 +309,7 @@ namespace IOB_WIN_FORM.Iob /// /// Determina se il contapezzi plc sia valido (lo è se data avvio adapter è prima di ultimo dato registrato in contapezzi) /// - public bool plcPzCountValid => machineCommService.GetLastObservedData() > dtAvvioAdp; + public bool plcPzCountValid => machineCommService.GetLastObservedData() > DtHelp.AvvioAdp; /// @@ -609,7 +609,7 @@ namespace IOB_WIN_FORM.Iob public async Task CheckIobEnabled() { // 1. Controllo Veto (Sincrono) - if (dtVetoCheckIOB >= DateTime.Now) + if (DtHelp.VetoCheckIOB >= DateTime.Now) return IobOnline; bool currentAnsw = false; @@ -1899,16 +1899,16 @@ namespace IOB_WIN_FORM.Iob // 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); + 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(DtHelp.AvvioAdp).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("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(DtHelp.AvvioAdp).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)); + parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalSeconds)); } } @@ -3360,7 +3360,7 @@ namespace IOB_WIN_FORM.Iob maxJsonDataEv = utils.CRI("maxJsonDataEv"); parentForm.commPlcActive = false; adpRunning = true; - dtAvvioAdp = adesso; + DtHelp.AvvioAdp = adesso; lastWatchDog = scaduto; lastPING = scaduto; lastReadPLC = scaduto; @@ -3480,7 +3480,7 @@ namespace IOB_WIN_FORM.Iob // chiudo la connessione all'adapter... tryDisconnect(); - dtStopAdp = DateTime.Now; + DtHelp.StopAdp = DateTime.Now; adpTryRestart = tryRestart; adpRunning = false; // chiudo! @@ -3570,7 +3570,7 @@ namespace IOB_WIN_FORM.Iob /// public virtual void tryConnect() { - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; queueInEnabCurr = true; } @@ -6057,12 +6057,12 @@ namespace IOB_WIN_FORM.Iob { DateTime adesso = DateTime.Now; // verifico veto sync ricette (x non ripetere troppo spesso) - if (adesso > dtVetoCheckSyncRecipe) + if (adesso > DtHelp.VetoCheckSyncRecipe) { // effettua sync eventuali NUOVI file ricette bool okSync = RecipeDoSyncRecipe(); // imposto veto a 1h... - dtVetoCheckSyncRecipe = adesso.AddHours(1); + DtHelp.VetoCheckSyncRecipe = adesso.AddHours(1); } } return answ; @@ -9451,7 +9451,7 @@ namespace IOB_WIN_FORM.Iob // Imposto il veto per il prossimo controllo (se ConnOk + rapido sennò meno rapido) var msWait = baseUtils.nextPauseSendMSec * (connectionOk ? 12 : 150); - dtVetoCheckIOB = adesso.AddMilliseconds(msWait); + DtHelp.VetoCheckIOB = adesso.AddMilliseconds(msWait); } #endregion Private Methods diff --git a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs index 950de20b..f9b041a0 100644 --- a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs +++ b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs @@ -936,7 +936,7 @@ namespace IOB_WIN_MITSUBISHI.Iob if (connectionOk) { checkVetoQueueIn(); - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; if (adpRunning) { lgInfo("Connessione OK"); diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 3ca99a89..4d5e9dac 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -491,7 +491,7 @@ namespace IOB_WIN_OSAI.Iob // refresh stato allarmi!!! if (connectionOk) { - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; queueInEnabCurr = true; if (adpRunning) { diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 8b1c1a87..5910479e 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -192,17 +192,17 @@ namespace IOB_WIN /// /// Data/ora ultimo avvio adapter /// - public DateTime dtAvvioAdp = DateTime.Now; + public DateTime DtHelp.AvvioAdp = DateTime.Now; /// /// Data/ora ultimo spegnimento adapter /// - public DateTime dtStopAdp = DateTime.Now; + public DateTime DtHelp.StopAdp = DateTime.Now; /// /// Indicazione VETO check status IOB x evitare loop troppo stretti... /// - public DateTime dtVetoCheckIOB = DateTime.Now.AddDays(-1); + public DateTime DtHelp.VetoCheckIOB = DateTime.Now.AddDays(-1); /// /// Abilitazione lettura PrgName @@ -418,7 +418,7 @@ namespace IOB_WIN { bool answ = false; // controllo se ho veto al check... - if (dtVetoCheckIOB < DateTime.Now) + if (DtHelp.VetoCheckIOB < DateTime.Now) { if (DemoOut) { @@ -466,7 +466,7 @@ namespace IOB_WIN { lastIobOnline = DateTime.Now; } - dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); + DtHelp.VetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); } catch { } @@ -3554,16 +3554,16 @@ namespace IOB_WIN // loggo SOLO se del mio IOB corrente... if (item.classCall == cIobConf.codIOB) { - lgInfo("{4}|Chiamate {0}: effettuate {1}, tempo medio {2:N2} msec | impegno canale {3:P3}", item.codCall, item.numCall, item.avgMsec, item.totMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + 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(DtHelp.AvvioAdp).TotalSeconds, cIobConf.codIOB); globNumCall += item.numCall; globAvgMsec += item.totMsec; } } // riporto conteggio medio al secondo... - lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds, cIobConf.codIOB); + lgInfo("{4}|Chiamate GLOBALI: {0}, periodo: {1:N2} minuti.cent, tempo medio {2:N2} msec | impegno canale {3:P3}", globNumCall, DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalMinutes, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalSeconds, cIobConf.codIOB); lgInfo("{0}--------------- STOP TIMING DATA ---------------{0}", Environment.NewLine); // mostro in form statistiche globali! - parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(dtAvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(dtAvvioAdp).TotalSeconds)); + parentForm.updateComStats(string.Format("Periodo: {0:N2}min | {1} x {2:N2}ms | canale {3:P3}", DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalMinutes, globNumCall, globAvgMsec.TotalMilliseconds / globNumCall, globAvgMsec.TotalSeconds / DateTime.Now.Subtract(DtHelp.AvvioAdp).TotalSeconds)); } } @@ -4436,11 +4436,11 @@ namespace IOB_WIN maxJsonDataEv = utils.CRI("maxJsonDataEv"); parentForm.commPlcActive = false; adpRunning = true; - dtAvvioAdp = DateTime.Now; - lastWatchDog = dtAvvioAdp; - lastPING = dtAvvioAdp; - lastReadPLC = dtAvvioAdp.AddMinutes(-1); - lastDisconnCheck = dtAvvioAdp; + DtHelp.AvvioAdp = DateTime.Now; + lastWatchDog = DtHelp.AvvioAdp; + lastPING = DtHelp.AvvioAdp; + lastReadPLC = DtHelp.AvvioAdp.AddMinutes(-1); + lastDisconnCheck = DtHelp.AvvioAdp; TimingData.resetData(); // aggiungo altri defaults setDefaults(resetQueue); @@ -4515,7 +4515,7 @@ namespace IOB_WIN // chiudo la connessione all'adapter... tryDisconnect(); - dtStopAdp = DateTime.Now; + DtHelp.StopAdp = DateTime.Now; adpTryRestart = tryRestart; adpRunning = false; // chiudo! @@ -4602,7 +4602,7 @@ namespace IOB_WIN /// public virtual void tryConnect() { - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; } /// diff --git a/IOB-WIN/IobOSAI.cs b/IOB-WIN/IobOSAI.cs index fb7cd067..83dd2ced 100644 --- a/IOB-WIN/IobOSAI.cs +++ b/IOB-WIN/IobOSAI.cs @@ -674,7 +674,7 @@ namespace IOB_WIN // refresh stato allarmi!!! if (connectionOk) { - dtAvvioAdp = DateTime.Now; + DtHelp.AvvioAdp = DateTime.Now; if (adpRunning) { lgInfo("Connessione OK"); From 0e1575dd9a115e75c4a03661ec20850b038e1c39 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:24:55 +0200 Subject: [PATCH 34/39] Continuo spostamento variabili DateTime in helper --- IOB-UT-NEXT/Iob/BaseObj.cs | 30 ------------- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 34 ++++++++++++++ IOB-WIN-FANUC/Iob/Fanuc.cs | 4 +- IOB-WIN-FILE/IobFile/IobFileSoitaab.cs | 12 ++--- IOB-WIN-FORM/AdapterForm.cs | 4 +- IOB-WIN-FORM/Iob/Generic.cs | 40 ++++++++--------- IOB-WIN-FORM/Iob/PingWatchDog.cs | 26 +++++------ IOB-WIN-FORM/Iob/Simula.cs | 6 +-- IOB-WIN-FTP/Iob/Ftp.cs | 22 +++++----- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 6 +-- IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 26 +++++------ IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs | 4 +- IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs | 4 +- IOB-WIN-MTC/Iob/MTConn.cs | 6 +-- IOB-WIN-OMRON/Iob/Omron.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 4 +- IOB-WIN-OSAI/Iob/OSAI.cs | 4 +- IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 22 +++++----- IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 24 +++++----- .../IobSiemens/IobSiemensTorri_legacy.cs | 4 +- IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 13 ++---- IOB-WIN-SQL/Iob/IobFileSoitaab.cs | 12 ++--- IOB-WIN-SQL/IobSql/IcoelDb.cs | 16 +++---- IOB-WIN-SQL/IobSql/SqlServLantek.cs | 18 ++++---- IOB-WIN-SQL/IobSql/SqlServPama.cs | 18 ++++---- IOB-WIN-WS/IobWs/Citizen.cs | 8 ++-- IOB-WIN-WS/IobWs/EmmegiFPW.cs | 8 ++-- IOB-WIN-WS/IobWs/Gomba.cs | 8 ++-- IOB-WIN-WS/IobWs/IcoelSoap.cs | 12 ++--- IOB-WIN-WS/IobWs/RestBase.cs | 10 ++--- IOB-WIN/AdapterForm.cs | 2 +- IOB-WIN/IobGeneric.cs | 44 +++++++++---------- IOB-WIN/IobOSAI.cs | 4 +- 33 files changed, 227 insertions(+), 232 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 46d5e478..56cecc0a 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -124,36 +124,6 @@ namespace IOB_UT_NEXT.Iob /// public IobConfTree IOBConfFull; - /// - /// dataOra ultima verifica CNC disconnesso... - /// - public DateTime lastDisconnCheck; - - /// - /// Data/ora ultima volta che IOB è stato dichiarato online - /// - 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... - /// - public DateTime lastPeriodicLog; - - /// - /// dataOra ultimo PING inviato verso il PLC... - /// - public DateTime lastPING = DateTime.Now.AddHours(-1); - - /// - /// DataOra ultima lettura da PLC - /// - public DateTime lastReadPLC; - /// /// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...) /// diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index 482b7555..55cfebe5 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -32,6 +32,40 @@ namespace IOB_UT_NEXT.Iob /// public DateTime VetoCheckSyncRecipe = DateTime.Now.AddHours(-1); + /// + /// dataOra ultima verifica CNC disconnesso... + /// + public DateTime lastDisconnCheck; + /// + /// Data/ora ultima volta che IOB è stato dichiarato online + /// + 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... + /// + public DateTime lastPeriodicLog; + + /// + /// dataOra ultimo PING inviato verso il PLC... + /// + public DateTime lastPING = DateTime.Now.AddHours(-1); + + + /// + /// Ultimo controllo ping x evitare ping flood... + /// + public DateTime lastPingConn = DateTime.Now.AddMinutes(-10); + + /// + /// DataOra ultima lettura da PLC + /// + public DateTime lastReadPLC; } } diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index 47d50602..a6f2b155 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -1527,14 +1527,14 @@ namespace IOB_WIN_FANUC.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("FANUC: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // ora PING!!! Ping pingSender = new Ping(); IPAddress address = IPAddress.Loopback; diff --git a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs index 49e8a916..3e804d87 100644 --- a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs +++ b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs @@ -72,7 +72,7 @@ namespace IOB_WIN_FILE.IobFile lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}"); } - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -89,7 +89,7 @@ namespace IOB_WIN_FILE.IobFile DateTime adesso = DateTime.Now; // NON fa nulla... anche se non dovrebbe richiamarlo Dictionary taskDone = new Dictionary(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return taskDone; } @@ -103,7 +103,7 @@ namespace IOB_WIN_FILE.IobFile Dictionary outVal = new Dictionary(); // processo ed accodo! processFluxLogTable(adesso); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -145,14 +145,14 @@ namespace IOB_WIN_FILE.IobFile if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("FileSoitaab: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -175,7 +175,7 @@ namespace IOB_WIN_FILE.IobFile if (adpRunning) { lgInfo($"Connessione OK alla folder {logDirPath}"); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index 2a2292ba..e3f11db7 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -2169,8 +2169,8 @@ namespace IOB_WIN_FORM counterMAC = iobObj.contapezziPLC, lastUpdate = lastIobStatus.lastUpdate > iobObj.lastWatchDog ? lastIobStatus.lastUpdate : iobObj.lastWatchDog, online = utils.IOB_Online, - lastDataIn = iobObj.lastReadPLC, - lastDataOut = iobObj.lastIobOnline, + lastDataIn = iobObj.DtHelp.lastReadPLC, + lastDataOut = iobObj.DtHelp.lastIobOnline, setupParams = setPar, freeNotes = note }; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 3acbcff3..b4780ffc 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -272,10 +272,10 @@ namespace IOB_WIN_FORM.Iob get { bool answ = false; - answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); + answ = (DateTime.Now.Subtract(DtHelp.lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); if (answ) { - lastPeriodicLog = DateTime.Now; + DtHelp.lastPeriodicLog = DateTime.Now; } return answ; @@ -2468,7 +2468,7 @@ namespace IOB_WIN_FORM.Iob newDisplayData currDispData = new newDisplayData(); // controllo contatore invio "keepalive"... invio solo a scadenza int disconnMaxSec = utils.CRI("disconMaxSec"); - if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > disconnMaxSec) + if (DateTime.Now.Subtract(DtHelp.lastDisconnCheck).TotalSeconds > disconnMaxSec) { // resetto tutti i valori BYTE IN/PREV/OUT... così invio macchina spenta... B_input = 0; @@ -2476,7 +2476,7 @@ namespace IOB_WIN_FORM.Iob B_previous = -1; accodaSigIN(ref currDispData); // update controllo - lastDisconnCheck = DateTime.Now; + DtHelp.lastDisconnCheck = DateTime.Now; lgInfo($"Send 00 | disconMaxSec: {disconnMaxSec}"); } raiseRefresh(currDispData); @@ -2839,7 +2839,7 @@ namespace IOB_WIN_FORM.Iob /// public virtual void readSemafori(ref newDisplayData currDispData) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -3170,12 +3170,12 @@ namespace IOB_WIN_FORM.Iob fatto = true; currDispData.semOut = Semaforo.SV; // se oltre 1 min NON era online --> check pezzi! - if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + if (DateTime.Now.Subtract(DtHelp.lastIobOnline).TotalMinutes > 1 && !isMulti) { - lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); + lgInfo($"sendDataBlock --> offline timeout ({DtHelp.lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } - lastIobOnline = DateTime.Now; + DtHelp.lastIobOnline = DateTime.Now; } else { @@ -3252,9 +3252,9 @@ namespace IOB_WIN_FORM.Iob // 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) + if (DateTime.Now.Subtract(DtHelp.lastIobOnline).TotalMinutes > 1 && !isMulti) { - lgInfo($"sendToMoonPro --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); + lgInfo($"sendToMoonPro --> offline timeout ({DtHelp.lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } // se richiesto effettuo refresh contapezzi @@ -3263,7 +3263,7 @@ namespace IOB_WIN_FORM.Iob pzCntReload(true); needRefreshPzCount = false; } - lastIobOnline = DateTime.Now; + DtHelp.lastIobOnline = DateTime.Now; // se "OK" verde, altrimenti errore --> ROSSO if (answ == "OK") { @@ -3362,9 +3362,9 @@ namespace IOB_WIN_FORM.Iob adpRunning = true; DtHelp.AvvioAdp = adesso; lastWatchDog = scaduto; - lastPING = scaduto; - lastReadPLC = scaduto; - lastDisconnCheck = scaduto; + DtHelp.lastPING = scaduto; + DtHelp.lastReadPLC = scaduto; + DtHelp.lastDisconnCheck = scaduto; TimingData.resetData(); // aggiungo altri defaults setDefaults(resetQueue); @@ -3486,8 +3486,8 @@ namespace IOB_WIN_FORM.Iob // chiudo! parentForm.displayTaskAndLog("Adapter Stopped.", true); DateTime adesso = DateTime.Now; - lastReadPLC = adesso; - lastPING = adesso; + DtHelp.lastReadPLC = adesso; + DtHelp.lastPING = adesso; parentForm.commPlcActive = false; } @@ -9138,7 +9138,7 @@ namespace IOB_WIN_FORM.Iob } // imposto contatori blink a zero... i_counters = new int[32]; - lastPeriodicLog = DateTime.Now; + DtHelp.lastPeriodicLog = DateTime.Now; // fix parametri generali... pzCountDelay = IOBConfFull.Device.PzCountDelay; @@ -9433,14 +9433,14 @@ namespace IOB_WIN_FORM.Iob { // Log se lo stato è cambiato DateTime adesso = DateTime.Now; - if (IobOnline != newStatus || adesso.Subtract(lastIobStatusDisplUpdate).TotalMilliseconds > (baseUtils.nextPauseSendMSec * 10)) + if (IobOnline != newStatus || adesso.Subtract(DtHelp.lastIobStatusDisplUpdate).TotalMilliseconds > (baseUtils.nextPauseSendMSec * 10)) { lgInfo(newStatus ? "IOB ONLINE for server MP/IO" : "IOB OFFLINE for server MP/IO"); IobOnline = newStatus; - lastIobStatusDisplUpdate = adesso; + DtHelp.lastIobStatusDisplUpdate = adesso; if (newStatus) { - lastIobOnline = adesso; + DtHelp.lastIobOnline = adesso; parentForm.commSrvActive = 2; // Stato Online } else diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index c1cc77e8..45d4b268 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -35,8 +35,8 @@ namespace IOB_WIN_FORM.Iob lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... - lastPING = adesso; - lastDisconnCheck = adesso; + DtHelp.lastPING = adesso; + DtHelp.lastDisconnCheck = adesso; PoweroffTimeoutSec = IOBConfFull.Device.PoweroffTimeOutSec; // imposto i valori da inviare al controllo stato... bInOff = IOBConfFull.Special.PingConf.B_PowerOff; @@ -58,7 +58,7 @@ namespace IOB_WIN_FORM.Iob { // effettuo check ping! DateTime adesso = DateTime.Now; - var lastCheck = lastPING < lastReadPLC ? lastReadPLC : lastReadPLC; + var lastCheck = DtHelp.lastPING < DtHelp.lastReadPLC ? DtHelp.lastReadPLC : DtHelp.lastReadPLC; if (adesso.Subtract(lastCheck).TotalSeconds >= PoweroffTimeoutSec) { // gestione invio anche in caso di disconnessione @@ -78,8 +78,8 @@ namespace IOB_WIN_FORM.Iob { accodaSigIN(ref currDispData); // reset last ping... - lastPING = DateTime.Now; - lastReadPLC = DateTime.Now; + DtHelp.lastPING = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } } @@ -99,19 +99,19 @@ namespace IOB_WIN_FORM.Iob if (QueuePing.Count < maxQueuePing) { // in primis salvo data ping comunque... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // salvo esito ping bool pingOK = testPingMachine == IPStatus.Success; addTest(pingOK); } - if (adesso.Subtract(lastPING).TotalSeconds >= vetoCheckSec) + if (adesso.Subtract(DtHelp.lastPING).TotalSeconds >= vetoCheckSec) { try { currDispData.semIn = Semaforo.SV; // in primis salvo data ping comunque... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // salvo esito ping bool pingOK = testPingMachine == IPStatus.Success; addTest(pingOK); @@ -120,7 +120,7 @@ namespace IOB_WIN_FORM.Iob if (pingStatusOk()) { connectionOk = true; - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lastWatchDog = DateTime.Now; } else @@ -158,8 +158,8 @@ namespace IOB_WIN_FORM.Iob // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; lastWatchDog = adesso; - lastReadPLC = adesso; - lastDisconnCheck = adesso; + DtHelp.lastReadPLC = adesso; + DtHelp.lastDisconnCheck = adesso; // faccio un primo check POST ritardo tryConnect(); } @@ -174,7 +174,7 @@ namespace IOB_WIN_FORM.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { @@ -195,7 +195,7 @@ namespace IOB_WIN_FORM.Iob if (pingStatusOk()) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; connectionOk = true; queueInEnabCurr = true; lgInfo("PING OK"); diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index e8414a21..6f54a321 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -522,7 +522,7 @@ namespace IOB_WIN_FORM.Iob sendDataItemsList(demoItems); // salvo lettura - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lgInfo($"Completato processCustomTaskLF"); } @@ -540,7 +540,7 @@ namespace IOB_WIN_FORM.Iob var currOdl = CurrOdl(); // salvo lettura - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lgInfo($"Completato processCustomTaskMF"); } @@ -1025,7 +1025,7 @@ namespace IOB_WIN_FORM.Iob string autoOdlRes = HttpService.CallUrl(urlFixDailyOdl); // salvo lettura - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lgInfo($"Completato processSlowDataRead"); return answ; } diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index 4c5a2a6b..e3fce130 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -43,8 +43,8 @@ namespace IOB_WIN_FTP.Iob lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... - lastPING = adesso; - lastDisconnCheck = adesso; + DtHelp.lastPING = adesso; + DtHelp.lastDisconnCheck = adesso; var VETO_PING_SEC = getOptPar("VETO_PING_SEC"); if (!string.IsNullOrEmpty(VETO_PING_SEC)) { @@ -329,7 +329,7 @@ namespace IOB_WIN_FTP.Iob } } } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -343,7 +343,7 @@ namespace IOB_WIN_FTP.Iob if (ftpClientMan.IsConfigured) { // salto se fosse attivo il veto ping... - if (lastPING.AddSeconds(vetoPingSec) < adesso) + if (DtHelp.lastPING.AddSeconds(vetoPingSec) < adesso) { // lo stato è come ping machine, x ora puntato a IP unico (WiFi?) byte[] MemBlock = new byte[2]; @@ -351,7 +351,7 @@ namespace IOB_WIN_FTP.Iob { currDispData.semIn = Semaforo.SV; // in primis salvo data ping comunque... - lastPING = adesso; + DtHelp.lastPING = adesso; // salvo esito ping bool pingOK = testPingMachine == IPStatus.Success; @@ -412,7 +412,7 @@ namespace IOB_WIN_FTP.Iob connectionOk = true; } - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lastWatchDog = adesso; } else @@ -445,9 +445,9 @@ namespace IOB_WIN_FTP.Iob // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; lastWatchDog = adesso; - //lastPING = adesso; - lastReadPLC = adesso; - lastDisconnCheck = adesso; + //DtHelp.lastPING = adesso; + DtHelp.lastReadPLC = adesso; + DtHelp.lastDisconnCheck = adesso; // faccio un primo check POST ritardo tryConnect(); } @@ -464,7 +464,7 @@ namespace IOB_WIN_FTP.Iob //// resetto coda... //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > vetoPingSec) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { if (doLog) { @@ -481,7 +481,7 @@ namespace IOB_WIN_FTP.Iob if (pingStatusOk()) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; connectionOk = true; queueInEnabCurr = true; lgInfo("FTP - ping OK"); diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index b45b9186..17f13141 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -309,7 +309,7 @@ namespace IOB_WIN_KAWASAKI.Iob // se trova ok thread o almeno kawasaki connesso --> aggiorno ultima lettura if (threadOk || KAWASAKI_ref.IsConnected) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } catch @@ -366,14 +366,14 @@ namespace IOB_WIN_KAWASAKI.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("KAWASAKI: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index a24aac2a..96a1dfd5 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -296,7 +296,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP } else { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else @@ -512,7 +512,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgInfo("ModBus TCP: tryConnect step 03"); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { @@ -521,7 +521,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgInfo("ModBus TCP: tryConnect step 04"); // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -600,7 +600,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP connectionOk = false; queueInEnabCurr = false; // resetto last ping... - lastPING = DateTime.Now.AddMinutes(-1); + DtHelp.lastPING = DateTime.Now.AddMinutes(-1); } /// @@ -678,10 +678,6 @@ namespace IOB_WIN_MBUS.IobModbusTCP /// protected Dictionary InputRegisterLUT = new Dictionary(); - /// - /// Ultimo controllo ping x evitare ping flood... - /// - protected DateTime lastPingConn = DateTime.Now.AddMinutes(-10); /// /// Esito ultimo ping @@ -1182,7 +1178,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP if (!connectionOk) { // ora tento avvio PLC... SE PING OK... - lastPING = adesso; + DtHelp.lastPING = adesso; IPStatus esitoPing = testPingMachine; if (esitoPing == IPStatus.Success) { @@ -1379,7 +1375,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP { // riduco i controlli ping.. li faccio solo ogni 5 ping period se precedente positivo... DateTime adesso = DateTime.Now; - if (lastPingOk && adesso.Subtract(lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout) + if (lastPingOk && adesso.Subtract(DtHelp.lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout) { answ = lastPingOk; } @@ -1425,7 +1421,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP } } // salvo stato ping - lastPingConn = adesso; + DtHelp.lastPingConn = adesso; } lastPingOk = answ; } @@ -2011,7 +2007,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgTrace($"Lettura in blocco Colis | startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {sw.ElapsedMilliseconds}ms"); // salvo statistica... trackReadData(rawData.Length / 8, sw.Elapsed.TotalMilliseconds / 1000); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // salvo in LUT i dati... if (rawData.Length > 0) { @@ -2082,7 +2078,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgTrace($"Lettura in blocco DiscreteInput | startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {sw.ElapsedMilliseconds}ms"); // salvo statistica... trackReadData(rawData.Length / 8, sw.Elapsed.TotalMilliseconds / 1000); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // salvo in LUT i dati... if (rawData.Length > 0) { @@ -2154,7 +2150,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgTrace($"Lettura in blocco HoldingRegisters| startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {sw.Elapsed.TotalMilliseconds}ms"); // salvo statistica... trackReadData(rawData.Length * 2, sw.Elapsed.TotalMilliseconds / 1000); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // se risposta troppo rapida (< minRespTimeMs, tipicamente 5 msse non impostato in OptPar) indico NON ok e errori... if (sw.Elapsed.TotalMilliseconds < minRespTimeMs) { @@ -2245,7 +2241,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP lgTrace($"Lettura in blocco InputRegisters| startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {sw.ElapsedMilliseconds}ms"); // salvo statistica... trackReadData(rawData.Length * 2, sw.Elapsed.TotalMilliseconds / 1000); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // salvo in LUT la versione ESPLOSA 2 byte alla volta... if (rawData.Length > 0) { diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs index 26fa5647..287f0554 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs @@ -76,7 +76,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP ----------------------------------------------------- */ DateTime adesso = DateTime.Now; // se ha risposto ad ultima chiamata --> ok - if (connectionOk && adesso.Subtract(lastReadPLC).TotalMinutes < 1) + if (connectionOk && adesso.Subtract(DtHelp.lastReadPLC).TotalMinutes < 1) { B_input = 3; // aggiungo NON emergenza... @@ -86,7 +86,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP { B_input = 0; } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lastWatchDog = adesso; } diff --git a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs index f9b041a0..a6d5a7fc 100644 --- a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs +++ b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs @@ -885,14 +885,14 @@ namespace IOB_WIN_MITSUBISHI.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("MITSUBISHI: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // ora PING!!! Ping pingSender = new Ping(); IPAddress address = IPAddress.Loopback; diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index f7fb6bf7..ce86dcdc 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -175,7 +175,7 @@ namespace IOB_WIN_MTC.Iob public override void readSemafori(ref newDisplayData currDispData) { DateTime adesso = DateTime.Now; - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; // verifico non sia in veto invio iniziale... if (queueInEnabCurr) { @@ -248,14 +248,14 @@ namespace IOB_WIN_MTC.Iob // disattivo eventuali sottoscrizioni DeactEvents(); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("MTC: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index 4ce1f9e2..e0098393 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -147,14 +147,14 @@ namespace IOB_WIN_OMRON.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("OMRON: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 3c7913a8..60ab4abc 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -588,14 +588,14 @@ namespace IOB_WIN_OPC_UA.IobOpc if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("OpcUa: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success || opcUaParams.forcePingOk) { diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 4d5e9dac..1072095e 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -434,7 +434,7 @@ namespace IOB_WIN_OSAI.Iob if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { @@ -449,7 +449,7 @@ namespace IOB_WIN_OSAI.Iob if (needPing) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // ora PING!!! IPAddress address = IPAddress.Loopback; IPAddress.TryParse(IOBConfFull.Device.Connect.PingIpAddr, out address); diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 092bddad..4776ea7f 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -47,8 +47,8 @@ namespace IOB_WIN_SHELLY.Iob lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... - lastPING = adesso; - lastDisconnCheck = adesso; + DtHelp.lastPING = adesso; + DtHelp.lastDisconnCheck = adesso; var VETO_PING_SEC = getOptPar("VETO_PING_SEC"); if (!string.IsNullOrEmpty(VETO_PING_SEC)) { @@ -86,14 +86,14 @@ namespace IOB_WIN_SHELLY.Iob { Dictionary outData = new Dictionary(); // verifico che non sia una chiamata troppo frequente, ogni 10 sec minimo... - if (DateTime.Now > lastReadPLC.AddSeconds(shellyCallDelay)) + if (DateTime.Now > DtHelp.lastReadPLC.AddSeconds(shellyCallDelay)) { // chiama lettura status completo e recupera info in memRead e le passa come DynData... lastShellyResp = Task.Run(() => ((Shelly1PmClient)shellyCli).GetSwitchStatus(CancellationToken.None, 0)).Result; // NB: da verificare tipo di chiamata in base al tipo di shelly... TDB ora è cablato x Shelly1PM if (lastShellyResp.IsSuccess) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // recupero da conf memMap e ciclo foreach (var item in IOBConfFull.Memory.mMapRead) { @@ -149,7 +149,7 @@ namespace IOB_WIN_SHELLY.Iob { DateTime adesso = DateTime.Now; // se ha risposto ad ultima chiamata --> ok - if (connectionOk && adesso.Subtract(lastReadPLC).TotalMinutes < 1) + if (connectionOk && adesso.Subtract(DtHelp.lastReadPLC).TotalMinutes < 1) { B_input = 3; // aggiungo NON emergenza... @@ -159,7 +159,7 @@ namespace IOB_WIN_SHELLY.Iob { B_input = 0; } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lastWatchDog = adesso; } @@ -169,9 +169,9 @@ namespace IOB_WIN_SHELLY.Iob // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; lastWatchDog = adesso; - //lastPING = adesso; - lastReadPLC = adesso; - lastDisconnCheck = adesso; + //DtHelp.lastPING = adesso; + DtHelp.lastReadPLC = adesso; + DtHelp.lastDisconnCheck = adesso; // faccio un primo check POST ritardo tryConnect(); } @@ -188,7 +188,7 @@ namespace IOB_WIN_SHELLY.Iob //// resetto coda... //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > vetoPingSec) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { if (doLog) { @@ -205,7 +205,7 @@ namespace IOB_WIN_SHELLY.Iob if (pingStatusOk() || pingOK) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; connectionOk = true; queueInEnabCurr = true; lgInfo("Shelly - ping OK"); diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index 56777586..a475ba4b 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -48,8 +48,8 @@ namespace IOB_WIN_SHELLY.Iob lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... - lastPING = adesso; - lastDisconnCheck = adesso; + DtHelp.lastPING = adesso; + DtHelp.lastDisconnCheck = adesso; var VETO_PING_SEC = getOptPar("VETO_PING_SEC"); if (!string.IsNullOrEmpty(VETO_PING_SEC)) { @@ -87,7 +87,7 @@ namespace IOB_WIN_SHELLY.Iob { Dictionary outData = new Dictionary(); // verifico che non sia una chiamata troppo frequente, ogni 10 sec minimo... - if (DateTime.Now > lastReadPLC.AddSeconds(shellyCallDelay)) + if (DateTime.Now > DtHelp.lastReadPLC.AddSeconds(shellyCallDelay)) { // chiama lettura status valor RT (Potenza, Corrente...) lastShellyRespRT = Task.Run(() => ((ShellyPro3EmClient)shellyCli).GetEmStatus(CancellationToken.None, 0)).Result; @@ -96,7 +96,7 @@ namespace IOB_WIN_SHELLY.Iob // Salvo negli oggetti DynData x EnergyTotal if (lastShellyRespEnergy.IsSuccess) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // recupero da conf memMap e ciclo foreach (var item in IOBConfFull.Memory.mMapRead) { @@ -137,7 +137,7 @@ namespace IOB_WIN_SHELLY.Iob // Salvo negli oggetti DynData x RT if (lastShellyRespRT.IsSuccess) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; // recupero da conf memMap e ciclo foreach (var item in IOBConfFull.Memory.mMapRead) { @@ -224,7 +224,7 @@ namespace IOB_WIN_SHELLY.Iob { DateTime adesso = DateTime.Now; // se ha risposto ad ultima chiamata --> ok - if (connectionOk && adesso.Subtract(lastReadPLC).TotalMinutes < 1) + if (connectionOk && adesso.Subtract(DtHelp.lastReadPLC).TotalMinutes < 1) { B_input = 3; // aggiungo NON emergenza... @@ -234,7 +234,7 @@ namespace IOB_WIN_SHELLY.Iob { B_input = 0; } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lastWatchDog = adesso; } @@ -244,9 +244,9 @@ namespace IOB_WIN_SHELLY.Iob // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; lastWatchDog = adesso; - //lastPING = adesso; - lastReadPLC = adesso; - lastDisconnCheck = adesso; + //DtHelp.lastPING = adesso; + DtHelp.lastReadPLC = adesso; + DtHelp.lastDisconnCheck = adesso; // faccio un primo check POST ritardo tryConnect(); } @@ -263,7 +263,7 @@ namespace IOB_WIN_SHELLY.Iob //// resetto coda... //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > vetoPingSec) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { if (doLog) { @@ -280,7 +280,7 @@ namespace IOB_WIN_SHELLY.Iob if (pingStatusOk() || pingOK) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; connectionOk = true; queueInEnabCurr = true; lgInfo("Shelly - ping OK"); diff --git a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs index e83c32f3..fb5a6638 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs @@ -372,14 +372,14 @@ namespace IOB_WIN // reimporto parametri PLC se necessario... setParamPlc(); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { lgInfo("SIEMENS-TORRI: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPing() == IPStatus.Success) { diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index 379eaadd..f4dc94be 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -1021,7 +1021,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens lgInfoStartup("SIEMENS: tryConnect step 03"); // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (doLog) { @@ -1030,7 +1030,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens lgInfoStartup("SIEMENS: tryConnect step 04"); // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -1142,11 +1142,6 @@ namespace IOB_WIN_SIEMENS.IobSiemens /// protected Plc currPLC; - /// - /// Ultimo controllo ping x evitare ping flood... - /// - protected DateTime lastPingConn = DateTime.Now.AddMinutes(-10); - /// /// Esito ultimo ping /// @@ -1596,7 +1591,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens bool answ = false; // riduco i controlli ping.. li faccio solo ogni 5 ping period se precedente positivo... DateTime adesso = DateTime.Now; - if (lastPingOk && adesso.Subtract(lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout) + if (lastPingOk && adesso.Subtract(DtHelp.lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout) { answ = lastPingOk; } @@ -1646,7 +1641,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens } // salvo stato ping lastPingOk = answ; - lastPingConn = adesso; + DtHelp.lastPingConn = adesso; } return answ; diff --git a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs index a9bc1b2f..01d7bf2e 100644 --- a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs +++ b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs @@ -72,7 +72,7 @@ namespace IOB_WIN_SQL.IobFile lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}"); } - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -89,7 +89,7 @@ namespace IOB_WIN_SQL.IobFile DateTime adesso = DateTime.Now; // NON fa nulla... anche se non dovrebbe richiamarlo Dictionary taskDone = new Dictionary(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return taskDone; } @@ -103,7 +103,7 @@ namespace IOB_WIN_SQL.IobFile Dictionary outVal = new Dictionary(); // processo ed accodo! processFluxLogTable(adesso); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -145,14 +145,14 @@ namespace IOB_WIN_SQL.IobFile if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("FileSoitaab: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -175,7 +175,7 @@ namespace IOB_WIN_SQL.IobFile if (adpRunning) { lgInfo($"Connessione OK alla folder {logDirPath}"); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else diff --git a/IOB-WIN-SQL/IobSql/IcoelDb.cs b/IOB-WIN-SQL/IobSql/IcoelDb.cs index 4630a9f3..db914ed8 100644 --- a/IOB-WIN-SQL/IobSql/IcoelDb.cs +++ b/IOB-WIN-SQL/IobSql/IcoelDb.cs @@ -54,7 +54,7 @@ namespace IOB_WIN_SQL.IobSql // eccezione: NON TROVA EntityFramework 6.0.0 o successivo... why?!? dbProxy = new DbProxy(connSyncState); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -103,7 +103,7 @@ namespace IOB_WIN_SQL.IobSql } } } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return taskDone; } @@ -131,7 +131,7 @@ namespace IOB_WIN_SQL.IobSql saveValue(ref outVal, item.Topic, (double)item.CurrVal); } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -145,7 +145,7 @@ namespace IOB_WIN_SQL.IobSql lgInfo($"Richiesto processCustomTaskLF"); // effettua sync refreshElencoStati(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -182,14 +182,14 @@ namespace IOB_WIN_SQL.IobSql if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("IcoelSoap: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -215,7 +215,7 @@ namespace IOB_WIN_SQL.IobSql if (adpRunning) { lgInfo("Connessione OK"); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else @@ -275,7 +275,7 @@ namespace IOB_WIN_SQL.IobSql sw.Start(); elencoSyncState = dbProxy.DataController.SyncStateDoImportAll(); sw.Stop(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lgInfo($"DB: esecuzione task dbProxy.DataController.SyncStateGetAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) diff --git a/IOB-WIN-SQL/IobSql/SqlServLantek.cs b/IOB-WIN-SQL/IobSql/SqlServLantek.cs index 3dced9b0..c5e1ac7f 100644 --- a/IOB-WIN-SQL/IobSql/SqlServLantek.cs +++ b/IOB-WIN-SQL/IobSql/SqlServLantek.cs @@ -70,7 +70,7 @@ namespace IOB_WIN_SQL.IobSql // avvio DB controller dbProxy = new DbController(connSyncState); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -212,7 +212,7 @@ namespace IOB_WIN_SQL.IobSql } } } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return taskDone; } @@ -226,7 +226,7 @@ namespace IOB_WIN_SQL.IobSql // dizionario vuoto / gestito da altro adapter file-based Dictionary outVal = new Dictionary(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -240,7 +240,7 @@ namespace IOB_WIN_SQL.IobSql // effettua sync execExportAll(); execImportAll(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -287,14 +287,14 @@ namespace IOB_WIN_SQL.IobSql if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("SqlDb LANTEK: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -320,7 +320,7 @@ namespace IOB_WIN_SQL.IobSql if (adpRunning) { lgInfo("Connessione OK"); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else @@ -393,7 +393,7 @@ namespace IOB_WIN_SQL.IobSql elencoSyncState = dbProxy.SyncStateDoExportAll(); sw.Stop(); DateTime adesso = DateTime.Now; - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoExportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) @@ -440,7 +440,7 @@ namespace IOB_WIN_SQL.IobSql sw.Start(); elencoSyncState = dbProxy.SyncStateDoImportAll(); sw.Stop(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoImportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index 9ef490bd..2e83d0b9 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -68,7 +68,7 @@ namespace IOB_WIN_SQL.IobSql // eccezione: NON TROVA EntityFramework 6.0.0 o successivo... why?!? dbProxy = new DbController(connSyncState); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -210,7 +210,7 @@ namespace IOB_WIN_SQL.IobSql } } } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return taskDone; } @@ -227,7 +227,7 @@ namespace IOB_WIN_SQL.IobSql // processo ed accodo! processFluxLogTable(adesso); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -240,7 +240,7 @@ namespace IOB_WIN_SQL.IobSql lgInfo($"Richiesto processCustomTaskLF"); // effettua sync execImportAll(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -289,14 +289,14 @@ namespace IOB_WIN_SQL.IobSql if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("SqlDb PAMA: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -322,7 +322,7 @@ namespace IOB_WIN_SQL.IobSql if (adpRunning) { lgInfo("Connessione OK"); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } } else @@ -395,7 +395,7 @@ namespace IOB_WIN_SQL.IobSql elencoSyncState = dbProxy.SyncStateDoExportAll(); sw.Stop(); DateTime adesso = DateTime.Now; - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoExportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) @@ -442,7 +442,7 @@ namespace IOB_WIN_SQL.IobSql sw.Start(); elencoSyncState = dbProxy.SyncStateDoImportAll(); sw.Stop(); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoImportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) diff --git a/IOB-WIN-WS/IobWs/Citizen.cs b/IOB-WIN-WS/IobWs/Citizen.cs index da764e01..f67f2341 100644 --- a/IOB-WIN-WS/IobWs/Citizen.cs +++ b/IOB-WIN-WS/IobWs/Citizen.cs @@ -30,9 +30,9 @@ namespace IOB_WIN_WS.IobWs public Citizen(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { lgInfo($"Richiesto Adapter IobRest.RestCitizen con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); redKeyAlarm = redisMan.redHash($"IOB:Status:{IOBConfFull.General.CodIOB}:Alarm:LastRead"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); // predispongo configurazione specifica Rest... lgInfo("Rest - 01"); if (!string.IsNullOrEmpty(getOptPar("REST_CONF"))) @@ -278,14 +278,14 @@ namespace IOB_WIN_WS.IobWs if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("Rest: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; bool checkMachine = false; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) diff --git a/IOB-WIN-WS/IobWs/EmmegiFPW.cs b/IOB-WIN-WS/IobWs/EmmegiFPW.cs index b88e895a..c2a72ecc 100644 --- a/IOB-WIN-WS/IobWs/EmmegiFPW.cs +++ b/IOB-WIN-WS/IobWs/EmmegiFPW.cs @@ -33,9 +33,9 @@ namespace IOB_WIN_WS.IobWs public EmmegiFPW(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { lgInfo($"Richiesto Adapter IobRest.EmmegiFPW con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); redKeyAlarm = redisMan.redHash($"IOB:Status:{IOBConfFull.General.CodIOB}:Alarm:LastRead"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); // predispongo configurazione specifica Rest... lgInfo("01 - Rest"); if (!string.IsNullOrEmpty(getOptPar("REST_CONF"))) @@ -221,14 +221,14 @@ namespace IOB_WIN_WS.IobWs if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("Rest: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN-WS/IobWs/Gomba.cs b/IOB-WIN-WS/IobWs/Gomba.cs index d21984c5..4efc4448 100644 --- a/IOB-WIN-WS/IobWs/Gomba.cs +++ b/IOB-WIN-WS/IobWs/Gomba.cs @@ -31,7 +31,7 @@ namespace IOB_WIN_WS.IobWs { lgInfo($"Richiesto Adapter IobSoap.Gomba con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}"); redKeyPesate = redisMan.redHash($"IOB:Status:{IOBConfFull.General.CodIOB}:ListPesate"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); // verifico se sia attiva gestione riduzione FluxLog... if (!string.IsNullOrEmpty(getOptJsonKVP("numLastWeight"))) { @@ -190,7 +190,7 @@ namespace IOB_WIN_WS.IobWs lgInfo($"getDynData | SOAP: effettuata chiamata reqWeightList in {sw.Elapsed.TotalMilliseconds}ms | {dataFrom} --> {dataTo} | {listPesateCurr.Count} rec"); } // indico esecuzione e proseguo - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -234,14 +234,14 @@ namespace IOB_WIN_WS.IobWs if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("Gomba: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN-WS/IobWs/IcoelSoap.cs b/IOB-WIN-WS/IobWs/IcoelSoap.cs index d79c69ba..6030121d 100644 --- a/IOB-WIN-WS/IobWs/IcoelSoap.cs +++ b/IOB-WIN-WS/IobWs/IcoelSoap.cs @@ -43,7 +43,7 @@ namespace IOB_WIN_WS.IobWs */ IcoelSizer = new Connector(IOBConfFull.Device.Connect.IpAddr, IOBConfFull.Device.Connect.Port); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors @@ -154,7 +154,7 @@ namespace IOB_WIN_WS.IobWs saveValueString(ref outVal, item.Key, item.Value); } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -188,7 +188,7 @@ namespace IOB_WIN_WS.IobWs } } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -258,14 +258,14 @@ namespace IOB_WIN_WS.IobWs if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("IcoelSoap: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { @@ -395,7 +395,7 @@ namespace IOB_WIN_WS.IobWs lgTrace("SOAP: effettuata chiamata IcoelSizer.GetLayoutForVarietyList()"); } - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } #endregion Protected Methods diff --git a/IOB-WIN-WS/IobWs/RestBase.cs b/IOB-WIN-WS/IobWs/RestBase.cs index aff50844..ea646980 100644 --- a/IOB-WIN-WS/IobWs/RestBase.cs +++ b/IOB-WIN-WS/IobWs/RestBase.cs @@ -30,7 +30,7 @@ namespace IOB_WIN_WS.IobWs public RestBase(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { lgInfo($"Richiesto Adapter IobRest.Base con i parametri seguenti | ADDR: {IOBConfFull.Device.Connect.IpAddr} | PORT: {IOBConfFull.Device.Connect.Port}"); - lastPING = DateTime.Now.AddHours(-1); + DtHelp.lastPING = DateTime.Now.AddHours(-1); // predispongo configurazione specifica Rest... if (!string.IsNullOrEmpty(getOptPar("REST_CONF"))) { @@ -125,7 +125,7 @@ namespace IOB_WIN_WS.IobWs // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // indico esecuzione e proseguo - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; return outVal; } @@ -136,7 +136,7 @@ namespace IOB_WIN_WS.IobWs public override void readSemafori(ref newDisplayData currDispData) { DateTime adesso = DateTime.Now; - lastReadPLC = adesso; + DtHelp.lastReadPLC = adesso; // verifico non sia in veto invio iniziale... if (queueInEnabCurr) { @@ -171,14 +171,14 @@ namespace IOB_WIN_WS.IobWs if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("Rest: ConnKO - tryConnect"); } // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // se passa il ping faccio il resto... if (testPingMachine == IPStatus.Success) { diff --git a/IOB-WIN/AdapterForm.cs b/IOB-WIN/AdapterForm.cs index 70377d58..6786ab59 100644 --- a/IOB-WIN/AdapterForm.cs +++ b/IOB-WIN/AdapterForm.cs @@ -1235,7 +1235,7 @@ namespace IOB_WIN counterMAC = iobObj.contapezziPLC, lastUpdate = lastIobStatus.lastUpdate, online = utils.IOB_Online, - lastDataIn = iobObj.lastReadPLC + lastDataIn = iobObj.DtHelp.lastReadPLC }; // se diverso SALVO! if (lastIobStatus.online != currIobStatus.online || lastIobStatus.lastDataIn != currIobStatus.lastDataIn || lastIobStatus.counterIOB != currIobStatus.counterIOB || lastIobStatus.counterMAC != currIobStatus.counterMAC || lastIobStatus.queueEvLen != currIobStatus.queueEvLen || lastIobStatus.queueFlLen != currIobStatus.queueFlLen || lastIobStatus.queueAlLen != currIobStatus.queueAlLen || lastIobStatus.queueMsLen != currIobStatus.queueMsLen) diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 5910479e..3c307a5a 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -222,27 +222,27 @@ namespace IOB_WIN /// /// dataOra ultima verifica CNC disconnesso... /// - public DateTime lastDisconnCheck; + public DateTime DtHelp.lastDisconnCheck; /// /// Data/ora ultima volta che IOB è stato dichairato online /// - public DateTime lastIobOnline = DateTime.Now.AddHours(-1); + public DateTime DtHelp.lastIobOnline = DateTime.Now.AddHours(-1); /// /// dataOra ultimo log periodico... /// - public DateTime lastPeriodicLog; + public DateTime DtHelp.lastPeriodicLog; /// /// dataOra ultimo PING inviato verso il PLC... /// - public DateTime lastPING; + public DateTime DtHelp.lastPING; /// /// DataOra ultima lettura da PLC /// - public DateTime lastReadPLC; + public DateTime DtHelp.lastReadPLC; /// /// ULtimo valore inviato (in caso di disconnessione lo reinvia x garantire watchdog...) @@ -464,7 +464,7 @@ namespace IOB_WIN // se online imposto veto check a 5 x tempo reinvio... if (answ) { - lastIobOnline = DateTime.Now; + DtHelp.lastIobOnline = DateTime.Now; } DtHelp.VetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5); } @@ -1044,10 +1044,10 @@ namespace IOB_WIN get { bool answ = false; - answ = (DateTime.Now.Subtract(lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); + answ = (DateTime.Now.Subtract(DtHelp.lastPeriodicLog).TotalSeconds > utils.CRI("verboseLogTOut")); if (answ) { - lastPeriodicLog = DateTime.Now; + DtHelp.lastPeriodicLog = DateTime.Now; } return answ; @@ -1786,7 +1786,7 @@ namespace IOB_WIN } // imposto contatori blink a zero... i_counters = new int[32]; - lastPeriodicLog = DateTime.Now; + DtHelp.lastPeriodicLog = DateTime.Now; // fix parametri generali... enablePrgName = true; } @@ -3852,7 +3852,7 @@ namespace IOB_WIN // init obj display newDisplayData currDispData = new newDisplayData(); // controllo contatore invio "keepalive"... invio solo a scadenza - if (DateTime.Now.Subtract(lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec")) + if (DateTime.Now.Subtract(DtHelp.lastDisconnCheck).TotalSeconds > utils.CRI("disconMaxSec")) { // resetto tutti i vlaori BYTE IN/PREV/OUT... così invio macchina spenta... B_input = 0; @@ -3860,7 +3860,7 @@ namespace IOB_WIN B_previous = 0; accodaSigIN(ref currDispData); // update controllo - lastDisconnCheck = DateTime.Now; + DtHelp.lastDisconnCheck = DateTime.Now; } raiseRefresh(currDispData); } @@ -4118,7 +4118,7 @@ namespace IOB_WIN if (connectionOk) { readSemafori(ref currDispData); - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } else { @@ -4142,7 +4142,7 @@ namespace IOB_WIN /// public virtual void readSemafori(ref newDisplayData currDispData) { - lastReadPLC = DateTime.Now; + DtHelp.lastReadPLC = DateTime.Now; } /// @@ -4268,12 +4268,12 @@ namespace IOB_WIN { currDispData.semOut = Semaforo.SV; // se oltre 1 min NON era online --> check pezzi! - if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + if (DateTime.Now.Subtract(DtHelp.lastIobOnline).TotalMinutes > 1 && !isMulti) { - lgInfo($"sendDataBlock --> offline timeout ({lastIobOnline}) --> pzCntReload(true)"); + lgInfo($"sendDataBlock --> offline timeout ({DtHelp.lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } - lastIobOnline = DateTime.Now; + DtHelp.lastIobOnline = DateTime.Now; } else { @@ -4344,9 +4344,9 @@ namespace IOB_WIN // loggo! lgInfo(string.Format("[SEND] {0} -> {1}", queueVal, answ)); // se oltre 1 min NON era online --> check pezzi! - if (DateTime.Now.Subtract(lastIobOnline).TotalMinutes > 1 && !isMulti) + if (DateTime.Now.Subtract(DtHelp.lastIobOnline).TotalMinutes > 1 && !isMulti) { - lgInfo($"sendToMoonPro --> offline timeaout ({lastIobOnline}) --> pzCntReload(true)"); + lgInfo($"sendToMoonPro --> offline timeaout ({DtHelp.lastIobOnline}) --> pzCntReload(true)"); pzCntReload(true); } // se richiesto effettuo refresh contapezzi @@ -4355,7 +4355,7 @@ namespace IOB_WIN pzCntReload(true); needRefreshPzCount = false; } - lastIobOnline = DateTime.Now; + DtHelp.lastIobOnline = DateTime.Now; // se "OK" verde, altrimenti errore --> ROSSO if (answ == "OK") { @@ -4438,9 +4438,9 @@ namespace IOB_WIN adpRunning = true; DtHelp.AvvioAdp = DateTime.Now; lastWatchDog = DtHelp.AvvioAdp; - lastPING = DtHelp.AvvioAdp; - lastReadPLC = DtHelp.AvvioAdp.AddMinutes(-1); - lastDisconnCheck = DtHelp.AvvioAdp; + DtHelp.lastPING = DtHelp.AvvioAdp; + DtHelp.lastReadPLC = DtHelp.AvvioAdp.AddMinutes(-1); + DtHelp.lastDisconnCheck = DtHelp.AvvioAdp; TimingData.resetData(); // aggiungo altri defaults setDefaults(resetQueue); diff --git a/IOB-WIN/IobOSAI.cs b/IOB-WIN/IobOSAI.cs index 83dd2ced..71dec67f 100644 --- a/IOB-WIN/IobOSAI.cs +++ b/IOB-WIN/IobOSAI.cs @@ -632,7 +632,7 @@ namespace IOB_WIN if (!connectionOk) { // controllo che il ping sia stato tentato almeno pingTestSec fa... - if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec")) + if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { @@ -647,7 +647,7 @@ namespace IOB_WIN if (needPing) { // in primis salvo data ping... - lastPING = DateTime.Now; + DtHelp.lastPING = DateTime.Now; // ora PING!!! IPAddress address = IPAddress.Loopback; IPAddress.TryParse(cIobConf.cncIpAddr, out address); From bd1d95112be2f52e76126bfe0b8fbc2ab4d2f27c Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:31:59 +0200 Subject: [PATCH 35/39] Ancora spostamento variabili dateTime in helper --- IOB-UT-NEXT/Iob/BaseObj.cs | 15 ----- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 66 +++++++++++++++------ IOB-WIN-FORM/AdapterForm.cs | 2 +- IOB-WIN-FORM/Iob/Generic.cs | 16 ++--- IOB-WIN-FORM/Iob/PingWatchDog.cs | 4 +- IOB-WIN-FORM/Iob/Simula.cs | 34 +++-------- IOB-WIN-FTP/Iob/Ftp.cs | 4 +- IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaEwon.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaImas.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaKpw.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaMBH.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaOmron.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensSW.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaUlma.cs | 4 +- IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 4 +- IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 4 +- IOB-WIN/IobGeneric.cs | 20 ++----- IOB-WIN/IobSimula.cs | 19 +++--- 21 files changed, 106 insertions(+), 120 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 56cecc0a..961a815a 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -129,21 +129,6 @@ namespace IOB_UT_NEXT.Iob /// public string lastSignInVal = ""; - /// - /// DateTime Ultimo valore simulazione generato - /// - public DateTime lastSim; - - /// - /// dataOra ultimo segnale inviato al SERVER... - /// - public DateTime lastWatchDog; - - /// - /// dataOra ultimo segnale inviato a macchina/PLC... - /// - public DateTime lastWatchDogPLC = DateTime.Now; - /// /// Massimo numero di px da inviare in blocco /// diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index 55cfebe5..4b3198af 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -11,32 +11,23 @@ namespace IOB_UT_NEXT.Iob /// public class DateTimeHelper { + #region Public Fields /// /// Data/ora ultimo avvio adapter /// public DateTime AvvioAdp = DateTime.Now; - /// - /// Data/ora ultimo spegnimento adapter - /// - public DateTime StopAdp = DateTime.Now; - - /// - /// Indicazione VETO check status IOB x evitare loop troppo stretti... - /// - public DateTime VetoCheckIOB = DateTime.Now.AddDays(-1); - - /// - /// Indicazione VETO check sync ricette x evitare loop troppo stretti... - /// - public DateTime VetoCheckSyncRecipe = DateTime.Now.AddHours(-1); - /// /// dataOra ultima verifica CNC disconnesso... /// public DateTime lastDisconnCheck; + /// + /// ultimo controllo decremento eventi + /// + public DateTime lastEvCheck; + /// /// Data/ora ultima volta che IOB è stato dichiarato online /// @@ -57,7 +48,6 @@ namespace IOB_UT_NEXT.Iob /// public DateTime lastPING = DateTime.Now.AddHours(-1); - /// /// Ultimo controllo ping x evitare ping flood... /// @@ -67,5 +57,47 @@ namespace IOB_UT_NEXT.Iob /// DataOra ultima lettura da PLC /// public DateTime lastReadPLC; + + /// + /// Ultimo istante in cui sono stati ridotti dati simulazione duration + /// + public DateTime lastRedDuration; + + /// + /// DateTime Ultimo valore simulazione generato + /// + public DateTime lastSim; + + /// + /// Ultimo istante in cui sono stati generati dati di simulazione + /// + public DateTime lastSimData; + + /// + /// dataOra ultimo segnale inviato al SERVER... + /// + public DateTime lastWatchDog; + + /// + /// dataOra ultimo segnale inviato a macchina/PLC... + /// + public DateTime lastWatchDogPLC = DateTime.Now; + + /// + /// Data/ora ultimo spegnimento adapter + /// + public DateTime StopAdp = DateTime.Now; + + /// + /// Indicazione VETO check status IOB x evitare loop troppo stretti... + /// + public DateTime VetoCheckIOB = DateTime.Now.AddDays(-1); + + /// + /// Indicazione VETO check sync ricette x evitare loop troppo stretti... + /// + public DateTime VetoCheckSyncRecipe = DateTime.Now.AddHours(-1); + + #endregion Public Fields } -} +} \ No newline at end of file diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index e3f11db7..ed23e1f1 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -2167,7 +2167,7 @@ namespace IOB_WIN_FORM queueUlLen = ulQueueLen, counterIOB = iobObj.contapezziIOB, counterMAC = iobObj.contapezziPLC, - lastUpdate = lastIobStatus.lastUpdate > iobObj.lastWatchDog ? lastIobStatus.lastUpdate : iobObj.lastWatchDog, + lastUpdate = lastIobStatus.lastUpdate > iobObj.DtHelp.lastWatchDog ? lastIobStatus.lastUpdate : iobObj.DtHelp.lastWatchDog, online = utils.IOB_Online, lastDataIn = iobObj.DtHelp.lastReadPLC, lastDataOut = iobObj.DtHelp.lastIobOnline, diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index b4780ffc..ccfc53be 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -3197,7 +3197,7 @@ namespace IOB_WIN_FORM.Iob // riporto cosa inviato currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } catch { @@ -3288,7 +3288,7 @@ namespace IOB_WIN_FORM.Iob nSendOut++; currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } catch { @@ -3361,7 +3361,7 @@ namespace IOB_WIN_FORM.Iob parentForm.commPlcActive = false; adpRunning = true; DtHelp.AvvioAdp = adesso; - lastWatchDog = scaduto; + DtHelp.lastWatchDog = scaduto; DtHelp.lastPING = scaduto; DtHelp.lastReadPLC = scaduto; DtHelp.lastDisconnCheck = scaduto; @@ -9220,7 +9220,7 @@ namespace IOB_WIN_FORM.Iob { bool sendOnMachineOff = false; // controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! - if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > IOBConfFull.General.WatchDogSec) + if (DateTime.Now.Subtract(DtHelp.lastWatchDog).TotalSeconds > IOBConfFull.General.WatchDogSec) { string wdStatus = "elapsed"; string sVal = string.Format("[WDST]{0}", wdStatus); @@ -9234,7 +9234,7 @@ namespace IOB_WIN_FORM.Iob trackDynData("WDST", wdStatus); } } - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; sendOnMachineOff = true; lgInfo("Impostato sendOnMachineOff a true"); } @@ -9266,7 +9266,7 @@ namespace IOB_WIN_FORM.Iob listaValori.Add(currVal); } await sendDataBlock(urlType.FLog, listaValori); - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } else { @@ -9277,7 +9277,7 @@ namespace IOB_WIN_FORM.Iob // svuoto! NO redis QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", false, redisMan); //QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueFLog", IOBConfFull.General.EnabRedisQue, redisMan); - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } } else @@ -9285,7 +9285,7 @@ namespace IOB_WIN_FORM.Iob // INVIO SINGOLO...!!! QueueFLog.TryDequeue(out currVal); await sendToMoonPro(urlType.FLog, currVal); - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } } else diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index 45d4b268..1595b91a 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -121,7 +121,7 @@ namespace IOB_WIN_FORM.Iob { connectionOk = true; DtHelp.lastReadPLC = DateTime.Now; - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } else { @@ -157,7 +157,7 @@ namespace IOB_WIN_FORM.Iob base.startAdapter(resetQueue); // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; DtHelp.lastReadPLC = adesso; DtHelp.lastDisconnCheck = adesso; // faccio un primo check POST ritardo diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 6f54a321..6b086426 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -73,9 +73,9 @@ namespace IOB_WIN_FORM.Iob DateTime adesso = DateTime.Now; lastPzCountSend = adesso; lastWarnODL = adesso; - lastEvCheck = adesso; - lastSimData = adesso; - lastRedDuration = adesso; + DtHelp.lastEvCheck = adesso; + DtHelp.lastSimData = adesso; + DtHelp.lastRedDuration = adesso; // abilito subito scrittura coda IN queueInEnabCurr = true; // setup allarmi simulati @@ -151,7 +151,6 @@ namespace IOB_WIN_FORM.Iob } } - #endregion Public Constructors #region Public Methods @@ -378,7 +377,7 @@ namespace IOB_WIN_FORM.Iob // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // verificare periodo SIM parametri... se passato li invio altrimenti NO... FIX a 20 sec - if (lastSimData.AddSeconds(waitSimPar) < DateTime.Now && memMap != null) + if (DtHelp.lastSimData.AddSeconds(waitSimPar) < DateTime.Now && memMap != null) { Random rnd = new Random(); // controllo conf memorie json (se ci sono...) @@ -460,7 +459,7 @@ namespace IOB_WIN_FORM.Iob } catch { } - lastSimData = DateTime.Now; + DtHelp.lastSimData = DateTime.Now; } return outVal; } @@ -549,7 +548,7 @@ namespace IOB_WIN_FORM.Iob /// public override void processVHF() { - if (lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now) + if (DtHelp.lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now) { // decremento contatore ultimo evento if (bit2 != null) @@ -572,7 +571,7 @@ namespace IOB_WIN_FORM.Iob if (simRS != null) simRS.wait--; - lastEvCheck = DateTime.Now; + DtHelp.lastEvCheck = DateTime.Now; } } @@ -768,21 +767,6 @@ namespace IOB_WIN_FORM.Iob protected bool disableSimStatus = false; - /// - /// ultimo controllo decremento eventi - /// - protected DateTime lastEvCheck; - - /// - /// Ultimo istante in cui sono stati ridotti dati simulazione duration - /// - protected DateTime lastRedDuration; - - /// - /// Ultimo istante in cui sono stati generati dati di simulazione - /// - protected DateTime lastSimData; - /// /// Cartella logfile x test import (tipo Soitaab) /// @@ -1036,7 +1020,7 @@ namespace IOB_WIN_FORM.Iob /// protected void tryRedDuration(int bit2proc) { - if (lastRedDuration.AddMilliseconds(periodoMSec / 20) < DateTime.Now) + if (DtHelp.lastRedDuration.AddMilliseconds(periodoMSec / 20) < DateTime.Now) { switch (bit2proc) { @@ -1067,7 +1051,7 @@ namespace IOB_WIN_FORM.Iob default: break; } - lastRedDuration = DateTime.Now; + DtHelp.lastRedDuration = DateTime.Now; } } diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index e3fce130..d3b96925 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -413,7 +413,7 @@ namespace IOB_WIN_FTP.Iob } DtHelp.lastReadPLC = adesso; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; } else { @@ -444,7 +444,7 @@ namespace IOB_WIN_FTP.Iob base.startAdapter(resetQueue); // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; //DtHelp.lastPING = adesso; DtHelp.lastReadPLC = adesso; DtHelp.lastDisconnCheck = adesso; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs index 287f0554..6805f1a0 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCPFrer.cs @@ -87,7 +87,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP B_input = 0; } DtHelp.lastReadPLC = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; } diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 60ab4abc..54a04200 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -1636,9 +1636,9 @@ namespace IOB_WIN_OPC_UA.IobOpc if (opcUaParams.WatchDog.IsEnabled) { lgTrace("WatchDog 01"); - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaEwon.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaEwon.cs index d66471a4..3b0e50ea 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaEwon.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaEwon.cs @@ -143,9 +143,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaImas.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaImas.cs index 0e202dae..0687e89f 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaImas.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaImas.cs @@ -147,9 +147,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpw.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpw.cs index 37739e83..0a5c1868 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaKpw.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaKpw.cs @@ -137,9 +137,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBH.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBH.cs index 2349a0c1..29e09d15 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaMBH.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaMBH.cs @@ -147,9 +147,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaOmron.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaOmron.cs index 47bed9d1..a1025ffe 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaOmron.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaOmron.cs @@ -144,9 +144,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs index 1203447e..565c89a5 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs @@ -360,9 +360,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensSW.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensSW.cs index 99a1fd2e..3408b26b 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensSW.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensSW.cs @@ -160,9 +160,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaUlma.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaUlma.cs index 5feb1303..df0d7a0a 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaUlma.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaUlma.cs @@ -146,9 +146,9 @@ namespace IOB_WIN_OPC_UA.IobOpc // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { - if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) + if (adesso.Subtract(DtHelp.lastWatchDogPLC).TotalSeconds > 2) { - lastWatchDogPLC = adesso; + DtHelp.lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 4776ea7f..ed52a548 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -160,7 +160,7 @@ namespace IOB_WIN_SHELLY.Iob B_input = 0; } DtHelp.lastReadPLC = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; } public override void startAdapter(bool resetQueue) @@ -168,7 +168,7 @@ namespace IOB_WIN_SHELLY.Iob base.startAdapter(resetQueue); // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; //DtHelp.lastPING = adesso; DtHelp.lastReadPLC = adesso; DtHelp.lastDisconnCheck = adesso; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index a475ba4b..4be5341a 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -235,7 +235,7 @@ namespace IOB_WIN_SHELLY.Iob B_input = 0; } DtHelp.lastReadPLC = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; } public override void startAdapter(bool resetQueue) @@ -243,7 +243,7 @@ namespace IOB_WIN_SHELLY.Iob base.startAdapter(resetQueue); // 2023.09.05 imposto anche primo ping e check disconnected... DateTime adesso = DateTime.Now; - lastWatchDog = adesso; + DtHelp.lastWatchDog = adesso; //DtHelp.lastPING = adesso; DtHelp.lastReadPLC = adesso; DtHelp.lastDisconnCheck = adesso; diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 3c307a5a..f11f9415 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -249,16 +249,6 @@ namespace IOB_WIN /// public string lastSignInVal = ""; - /// - /// DateTime Ultimo valore simulazione generato - /// - public DateTime lastSim; - - /// - /// dataOra ultimo segnale inviato... - /// - public DateTime lastWatchDog; - /// /// Massimo numero di px da inviare in blocco /// @@ -1832,13 +1822,13 @@ namespace IOB_WIN private void svuotaCodaFLog() { //controllo se è passato oltre watchdog e non ho inviato nulla --> RE-INVIO (ultimo inviato)!!!! - if (DateTime.Now.Subtract(lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) + if (DateTime.Now.Subtract(DtHelp.lastWatchDog).TotalSeconds > utils.CRI("watchdogMaxSec")) { string wdStatus = "elapsed"; string sVal = string.Format("[WDST]{0}", wdStatus); // chiamo accodamento... accodaFLog(sVal, qEncodeFLog("WDST", wdStatus)); - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } // verifico SE la coda abbia dei valori... if (QueueFLog.Count > 0) @@ -4295,7 +4285,7 @@ namespace IOB_WIN // riporto cosa inviato currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } catch { @@ -4384,7 +4374,7 @@ namespace IOB_WIN #endif currDispData.newUrlCallData = lastUrl; // aggiorno data ultimo watchdog... - lastWatchDog = DateTime.Now; + DtHelp.lastWatchDog = DateTime.Now; } catch { @@ -4437,7 +4427,7 @@ namespace IOB_WIN parentForm.commPlcActive = false; adpRunning = true; DtHelp.AvvioAdp = DateTime.Now; - lastWatchDog = DtHelp.AvvioAdp; + DtHelp.lastWatchDog = DtHelp.AvvioAdp; DtHelp.lastPING = DtHelp.AvvioAdp; DtHelp.lastReadPLC = DtHelp.AvvioAdp.AddMinutes(-1); DtHelp.lastDisconnCheck = DtHelp.AvvioAdp; diff --git a/IOB-WIN/IobSimula.cs b/IOB-WIN/IobSimula.cs index dfd82792..f0ff7617 100644 --- a/IOB-WIN/IobSimula.cs +++ b/IOB-WIN/IobSimula.cs @@ -47,12 +47,7 @@ namespace IOB_WIN /// /// ultimo controllo decremento eventi /// - protected DateTime lastEvCheck; - - /// - /// Ultimo istante in cui sono stati generati dati di simulazione - /// - protected DateTime lastSimData; + protected DateTime DtHelp.lastEvCheck; /// /// Durata minima ODL x reset quando pezzi iob > pezzi macchina... @@ -114,8 +109,8 @@ namespace IOB_WIN DateTime adesso = DateTime.Now; lastPzCountSend = adesso; lastWarnODL = adesso; - lastEvCheck = adesso; - lastSimData = adesso; + DtHelp.lastEvCheck = adesso; + DtHelp.lastSimData = adesso; // sistemo parametri x simulazione... if (cIobConf.optPar.Count > 0) { @@ -642,7 +637,7 @@ namespace IOB_WIN // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // verificare periodo SIM parametri... se passato li invio altrimenti NO... FIX a 20 sec - if (lastSimData.AddSeconds(waitSimPar) < DateTime.Now) + if (DtHelp.lastSimData.AddSeconds(waitSimPar) < DateTime.Now) { Random rnd = new Random(); // controllo conf memorie json (se ci sono...) @@ -667,7 +662,7 @@ namespace IOB_WIN } catch { } - lastSimData = DateTime.Now; + DtHelp.lastSimData = DateTime.Now; } return outVal; } @@ -706,14 +701,14 @@ namespace IOB_WIN /// public override void processVHF() { - if (lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now) + if (DtHelp.lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now) { // decremento contatore ultimo evento bit2.wait--; bit3.wait--; bit4.wait--; bit5.wait--; - lastEvCheck = DateTime.Now; + DtHelp.lastEvCheck = DateTime.Now; } } From 2a19e3c42507b5a9681a3e246b8a9f3c65b6680a Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:41:04 +0200 Subject: [PATCH 36/39] Continuo fix helper datetime --- IOB-UT-NEXT/Iob/BaseObj.cs | 46 -------------------------- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 24 +++++++++++++- IOB-UT/baseUtils.cs | 6 ++-- IOB-WIN-FILE/IobFile/IobFileSoitaab.cs | 4 +-- IOB-WIN-FORM/Iob/Generic.cs | 36 ++++++++++---------- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs | 2 +- IOB-WIN-SQL/Iob/IobFileSoitaab.cs | 4 +-- IOB-WIN-SQL/IobSql/SqlServLantek.cs | 8 ++--- IOB-WIN-SQL/IobSql/SqlServPama.cs | 8 ++--- IOB-WIN/IobGeneric.cs | 22 ++++++------ IOB-WIN/MainForm.cs | 10 +++--- 11 files changed, 73 insertions(+), 97 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 961a815a..dd7d7ca8 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -228,31 +228,11 @@ namespace IOB_UT_NEXT.Iob /// public Stopwatch sw = new Stopwatch(); - /// - /// Imposta veto lettura dati (es per DB a 2 sec) - /// - public DateTime vetoDataRead = DateTime.Now; - - /// - /// Imposta veto SYNC dati (es per DB 2 DB a 10 sec) - /// - public DateTime vetoDataSync = DateTime.Now; - /// /// Veto (in sec) a nuovi ping (x IOB PING, FTP...) /// public int vetoPingSec = 1; - /// - /// Veto specifico per la gestione (svuotamento) coda contapezzi (es in fase di attrezzaggio e reset lenti) - /// Tipicamente impostato a DelayReadPzCountSetup dopo attrezzaggio... - /// - public DateTime vetoQueuePzCount = DateTime.Now; - - /// - /// Imposta veto chiamata split (durante chiamata, per 60 sec) - /// - public DateTime vetoSplit = DateTime.Now.AddMinutes(1); /// /// alias booleano true = W @@ -273,32 +253,6 @@ namespace IOB_UT_NEXT.Iob /// public static bool DemoOut => utils.CRB("DemoOut"); - /// - /// Indicazione VETO PING a server sino alla data-ora indicata - /// - public static DateTime dtVetoPing - { - get => utils.dtVetoPing; - set => utils.dtVetoPing = value; - } - - /// - /// Indicazione VETO accodamento valori INGRESSI/EVENTI sino alla data-ora indicata - /// - public static DateTime dtVetoQueueIN - { - get => utils.dtVetoQueueIN; - set => utils.dtVetoQueueIN = value; - } - - /// - /// Indicazione VETO invio a server sino alla data-ora indicata - /// - public static DateTime dtVetoSend - { - get => utils.dtVetoSend; - set => utils.dtVetoSend = value; - } /// /// Verifica se sia abilitato test lettura blocchi memoria all'avvio diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index 4b3198af..4c015655 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -1,4 +1,5 @@ -using System; +using IOB_UT_NEXT.Services.Files; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -98,6 +99,27 @@ namespace IOB_UT_NEXT.Iob /// public DateTime VetoCheckSyncRecipe = DateTime.Now.AddHours(-1); + /// + /// Imposta veto lettura dati (es per DB a 2 sec) + /// + public DateTime vetoDataRead = DateTime.Now; + + /// + /// Imposta veto SYNC dati (es per DB 2 DB a 10 sec) + /// + public DateTime vetoDataSync = DateTime.Now; + + /// + /// Veto specifico per la gestione (svuotamento) coda contapezzi (es in fase di attrezzaggio e reset lenti) + /// Tipicamente impostato a DelayReadPzCountSetup dopo attrezzaggio... + /// + public DateTime vetoQueuePzCount = DateTime.Now; + + /// + /// Imposta veto chiamata split (durante chiamata, per 60 sec) + /// + public DateTime vetoSplit = DateTime.Now.AddMinutes(1); + #endregion Public Fields } } \ No newline at end of file diff --git a/IOB-UT/baseUtils.cs b/IOB-UT/baseUtils.cs index de3206f3..3a973eef 100644 --- a/IOB-UT/baseUtils.cs +++ b/IOB-UT/baseUtils.cs @@ -53,17 +53,17 @@ namespace IOB_UT /// /// Indicazione VETO PING a server sino alla data-ora indicata /// - public static DateTime dtVetoPing = DateTime.Now; + public static DateTime DtHelp.dtVetoPing = DateTime.Now; #if false { get { - return redisMan.servStatus.dtVetoPing; + return redisMan.servStatus.DtHelp.dtVetoPing; } set { var currData = redisMan.servStatus; - currData.dtVetoPing = value; + currData.DtHelp.dtVetoPing = value; redisMan.servStatus = currData; } } diff --git a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs index 3e804d87..05aac825 100644 --- a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs +++ b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs @@ -117,10 +117,10 @@ namespace IOB_WIN_FILE.IobFile if (connectionOk) { // controllo veto checkDB - if (adesso > vetoDataRead) + if (adesso > DtHelp.vetoDataRead) { // predispongo prox veto... - vetoDataRead = adesso.AddSeconds(vetoReadFileSec); + DtHelp.vetoDataRead = adesso.AddSeconds(vetoReadFileSec); // semaforo currDispData.semIn = Semaforo.SV; // verifico SignLog e processo diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index ccfc53be..9a29a23d 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -96,8 +96,8 @@ namespace IOB_WIN_FORM.Iob 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}"; + utils.dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn); + string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {utils.dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"; lgInfoStartup(msgVeto); lgInfo(msgVeto); @@ -648,7 +648,7 @@ namespace IOB_WIN_FORM.Iob public bool CheckServerAlive() // Rimosso async, restituisce bool { // 1. Controllo Veto (Sincrono) - if (dtVetoPing >= DateTime.Now) return MPOnline; + if (utils.dtVetoPing >= DateTime.Now) return MPOnline; if (DemoOut) return true; // 2. Eseguo la parte asincrona forzando l'attesa @@ -670,7 +670,7 @@ namespace IOB_WIN_FORM.Iob public async Task CheckServerAliveAsync() { // 1. Controllo Veto (Sincrono) - if (dtVetoPing >= DateTime.Now) return MPOnline; + if (utils.dtVetoPing >= DateTime.Now) return MPOnline; if (DemoOut) return true; bool isAlive = await ExecuteApiCheckWithRetryAsync(maxRetries: 7); @@ -686,7 +686,7 @@ namespace IOB_WIN_FORM.Iob /// public void checkVetoQueueIn() { - queueInEnabCurr = dtVetoQueueIN < DateTime.Now; + queueInEnabCurr = utils.dtVetoQueueIN < DateTime.Now; } /// @@ -1095,7 +1095,7 @@ namespace IOB_WIN_FORM.Iob taskVal = $"NO DATA MEM, SET task: {iKey} --> {item.Value}"; } // imposto reset gestione pzcount da parametri - vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); + DtHelp.vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } else @@ -1173,7 +1173,7 @@ namespace IOB_WIN_FORM.Iob { lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC"); taskOk = resetContapezziPLC(codTav); - vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); + DtHelp.vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } taskVal = taskOk ? "startSetup | RESET: SETUP START" : "startSetup | PZ RESET DISABLED | NO EXEC"; @@ -1289,11 +1289,11 @@ namespace IOB_WIN_FORM.Iob public async Task forceSplitOdl() { bool fatto = false; - if (vetoSplit < DateTime.Now) + if (DtHelp.vetoSplit < DateTime.Now) { lgInfo("Richiesto forceSplitOdl"); // imposto veto x 1 minuto ad altre chiamate... - vetoSplit = DateTime.Now.AddMinutes(1); + DtHelp.vetoSplit = DateTime.Now.AddMinutes(1); // eseguo SOLO SE sono online... if (MPOnline && IobOnline) { @@ -3370,11 +3370,11 @@ namespace IOB_WIN_FORM.Iob setDefaults(resetQueue); adpTryRestart = true; // sistemo altri check avvio - dtVetoQueueIN = DateTime.Now.AddSeconds(vetoQueueIn); + utils.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}"; + string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {utils.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}"); + lgDebug($"startAdapter completed | veto queueIn impostato per {vetoQueueIn}s fino alle {utils.dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"); } /// @@ -3797,20 +3797,20 @@ namespace IOB_WIN_FORM.Iob if (currentAlive) { lgInfo("SERVER ONLINE"); - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10); + utils.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; + utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 20); + utils.dtVetoSend = utils.dtVetoPing; } } else { // Se lo stato è invariato (es. era online e resta online), allunghiamo il prossimo controllo - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 30); + utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 30); } } @@ -9162,9 +9162,9 @@ namespace IOB_WIN_FORM.Iob private async Task SvuotaCodaContapezziAsync() { // verifico non sia impedita la gestione contapezzi (per evitare invio su macchine lente nel reset all'attrezzaggio) - if (DateTime.Now <= vetoQueuePzCount) + if (DateTime.Now <= DtHelp.vetoQueuePzCount) { - lgInfo($"Ciclo SvuotaCodaContapezziAsync DISABILITATO fino a {vetoQueuePzCount}"); + lgInfo($"Ciclo SvuotaCodaContapezziAsync DISABILITATO fino a {DtHelp.vetoQueuePzCount}"); } else { diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs index 565c89a5..6537c022 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs @@ -114,7 +114,7 @@ namespace IOB_WIN_OPC_UA.IobOpc if (addResetPzCount) { // imposto reset gestione pzcount da parametri - vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); + DtHelp.vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); // se è una doppia tavola aggiungo anche tag "processOtherInfo"... if (isMulti) { diff --git a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs index 01d7bf2e..34a76840 100644 --- a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs +++ b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs @@ -117,10 +117,10 @@ namespace IOB_WIN_SQL.IobFile if (connectionOk) { // controllo veto checkDB - if (adesso > vetoDataRead) + if (adesso > DtHelp.vetoDataRead) { // predispongo prox veto... - vetoDataRead = adesso.AddSeconds(vetoReadFileSec); + DtHelp.vetoDataRead = adesso.AddSeconds(vetoReadFileSec); // semaforo currDispData.semIn = Semaforo.SV; // verifico SignLog e processo diff --git a/IOB-WIN-SQL/IobSql/SqlServLantek.cs b/IOB-WIN-SQL/IobSql/SqlServLantek.cs index c5e1ac7f..821347d7 100644 --- a/IOB-WIN-SQL/IobSql/SqlServLantek.cs +++ b/IOB-WIN-SQL/IobSql/SqlServLantek.cs @@ -253,15 +253,15 @@ namespace IOB_WIN_SQL.IobSql if (connectionOk) { // controllo veto checkDB - if (adesso > vetoDataRead) + if (adesso > DtHelp.vetoDataRead) { // predispongo prox veto... - vetoDataRead = adesso.AddSeconds(vetoReadDbSec); - if (adesso > vetoDataSync) + DtHelp.vetoDataRead = adesso.AddSeconds(vetoReadDbSec); + if (adesso > DtHelp.vetoDataSync) { // effettua sync execImportAll(); - vetoDataSync = adesso.AddSeconds(VetoSyncDbSec); + DtHelp.vetoDataSync = adesso.AddSeconds(VetoSyncDbSec); } // semaforo diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index 2e83d0b9..bcada3ba 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -253,15 +253,15 @@ namespace IOB_WIN_SQL.IobSql if (connectionOk) { // controllo veto checkDB - if (adesso > vetoDataRead) + if (adesso > DtHelp.vetoDataRead) { // predispongo prox veto... - vetoDataRead = adesso.AddSeconds(vetoReadDbSec); - if (adesso > vetoDataSync) + DtHelp.vetoDataRead = adesso.AddSeconds(vetoReadDbSec); + if (adesso > DtHelp.vetoDataSync) { // effettua sync execImportAll(); - vetoDataSync = adesso.AddSeconds(VetoSyncDbSec); + DtHelp.vetoDataSync = adesso.AddSeconds(VetoSyncDbSec); } // semaforo diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index f11f9415..38bd60f8 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -332,7 +332,7 @@ namespace IOB_WIN /// /// Imposta veto chiamata split (durante chiamata, per 60 sec) /// - public DateTime vetoSplit = DateTime.Now.AddMinutes(1); + public DateTime DtHelp.vetoSplit = DateTime.Now.AddMinutes(1); /// /// alias booleano true = W @@ -690,15 +690,15 @@ namespace IOB_WIN /// /// Indicazione VETO PING a server sino alla data-ora indicata /// - public static DateTime dtVetoPing + public static DateTime DtHelp.dtVetoPing { get { - return utils.dtVetoPing; + return utils.DtHelp.dtVetoPing; } set { - utils.dtVetoPing = value; + utils.DtHelp.dtVetoPing = value; } } @@ -741,7 +741,7 @@ namespace IOB_WIN { bool answ = false; // controllo se ho un VETO all'invio... - if (dtVetoPing < DateTime.Now) + if (DtHelp.dtVetoPing < DateTime.Now) { if (DemoOut) { @@ -810,7 +810,7 @@ namespace IOB_WIN { lgInfo("SERVER ONLINE in checkServerAlive"); parentForm.commSrvActive = 1; - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec); + DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec); } else { @@ -823,7 +823,7 @@ namespace IOB_WIN else { // allungo periodo controllo... - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); + DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); } } else @@ -831,8 +831,8 @@ namespace IOB_WIN lgInfo($"SERVER NOT RESPONDING (PING at {cIobConf.serverData.MPIP})"); MPOnline = false; // imposto veto a 10 volte reinvio dati standard... - dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); - utils.dtVetoSend = dtVetoPing; + DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); + utils.dtVetoSend = DtHelp.dtVetoPing; } } } @@ -3070,10 +3070,10 @@ namespace IOB_WIN public bool forceSplitOdl() { bool fatto = false; - if (vetoSplit < DateTime.Now) + if (DtHelp.vetoSplit < DateTime.Now) { // imposto veto x 1 minuto ad altre chiamate... - vetoSplit = DateTime.Now.AddMinutes(1); + DtHelp.vetoSplit = DateTime.Now.AddMinutes(1); // eseguo SOLO SE sono online... if (MPOnline && IobOnline) { diff --git a/IOB-WIN/MainForm.cs b/IOB-WIN/MainForm.cs index b827dccf..a01f0fc4 100644 --- a/IOB-WIN/MainForm.cs +++ b/IOB-WIN/MainForm.cs @@ -321,7 +321,7 @@ namespace IOB_WIN bool answ = false; if (!string.IsNullOrEmpty(MPIP)) { - if (utils.dtVetoPing < DateTime.Now) + if (utils.DtHelp.dtVetoPing < DateTime.Now) { IPStatus pingStatus = testPingServer; // se passa il ping faccio il resto... @@ -344,7 +344,7 @@ namespace IOB_WIN if (answ) { lgInfo("SERVER ONLINE"); - utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec); + utils.DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec); } else { @@ -356,7 +356,7 @@ namespace IOB_WIN else { // allungo periodo controllo... - utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); + utils.DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 3); } } else @@ -365,8 +365,8 @@ namespace IOB_WIN utils.MPIO_Online = false; updateComStats(0, 0, false); // imposto veto a 10 volte reinvio dati standard... - utils.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10); - utils.dtVetoSend = utils.dtVetoPing; + utils.DtHelp.dtVetoPing = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 10); + utils.dtVetoSend = utils.DtHelp.dtVetoPing; } } else From c073e76b89c8c5968dc9d5a984731971ffe8821b Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:52:25 +0200 Subject: [PATCH 37/39] Ancora modifiche concentrazione variabili dateTime --- IOB-UT-NEXT/Iob/BaseObj.cs | 42 ---------------- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 46 ++++++++++++++++++ IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs | 2 +- IOB-WIN-FANUC/Iob/Fanuc.cs | 13 +++-- IOB-WIN-FORM/Iob/BaseObj.cs | 12 ++--- IOB-WIN-FORM/Iob/Generic.cs | 32 ++++++------ IOB-WIN-FORM/Iob/PingWatchDog.cs | 2 +- IOB-WIN-FORM/Iob/Simula.cs | 4 +- IOB-WIN-FTP/Iob/Ftp.cs | 2 +- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 10 ++-- IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 2 +- IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs | 10 ++-- IOB-WIN-MTC/Iob/MTConn.cs | 23 +++------ IOB-WIN-OMRON/Iob/Omron.cs | 2 +- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 4 +- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs | 8 +-- IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs | 2 +- IOB-WIN-OSAI/Iob/OSAI.cs | 10 ++-- IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 2 +- IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 2 +- .../IobSiemens/IobSiemensTorri_legacy.cs | 10 ++-- IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 4 +- IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs | 10 ++-- IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs | 10 ++-- IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs | 8 +-- IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs | 12 ++--- IOB-WIN/IobGeneric.cs | 20 ++++---- IOB-WIN/IobOSAI.cs | 10 ++-- IOB-WIN/IobSimula.cs | 4 +- refactor_progress_tracker.pdf | Bin 0 -> 545846 bytes 30 files changed, 155 insertions(+), 163 deletions(-) create mode 100644 refactor_progress_tracker.pdf diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index dd7d7ca8..5ad36184 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -324,11 +324,6 @@ namespace IOB_UT_NEXT.Iob /// protected int adpPortNum; - /// - /// DataOra ultimo avvio adapter x watchdog - /// - protected DateTime adpStartRun; - /// /// Vettore 32 BIT valori in ingresso al filtro /// @@ -389,26 +384,6 @@ namespace IOB_UT_NEXT.Iob /// protected bool disablePzCountByIob = false; - /// - /// Veto x esecuzione task AutoDossier - /// - protected DateTime dtVetoAutoDossier = DateTime.Now; - - /// - /// Veto x lettura contapezzi (in caso di autoODL con attesa reset ad esempio...) - /// - protected DateTime dtVetoReadPzCount = DateTime.Now; - - /// - /// DataOra x veto all'invio dataItem - /// - protected DateTime dtVetoSenDataItem = DateTime.Now; - - /// - /// Veto x esecuzione Task2Exe troppo frequenti - /// - protected DateTime dtVetoTask2Exe = DateTime.Now; - /// /// Determina se sia prevista gestione PODL (creazione/avvio/chiusura) come Soitaab /// @@ -485,10 +460,6 @@ namespace IOB_UT_NEXT.Iob /// protected bool forcePzReset = false; - /// - /// indica che è richiesto forzatamente reset contapezzi - /// - protected DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1); /// /// DEADBAND globale se impostata (es x gestire variazioni minime valori FluxLog da inviare...) @@ -510,25 +481,12 @@ namespace IOB_UT_NEXT.Iob /// protected bool inSetup = false; - /// - /// ultimo tentativo connessione... - /// - protected DateTime lastConnectTry; - - /// - /// Ultimo LOG registrazione avvio (x ridurre log notturni...) - /// - protected DateTime lastLogStartup = DateTime.Today.AddHours(-1); /// /// Dizionario ULTIMI valori impostati x produzione /// protected Dictionary lastProdData = new Dictionary(); - /// - /// Ultimo invio contapezzi (x invio delayed) - /// - protected DateTime lastPzCountSend; /// /// Dizionario ultimi valori (string) delle TSS diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index 4c015655..eb14ff72 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -14,11 +14,46 @@ namespace IOB_UT_NEXT.Iob { #region Public Fields + /// + /// DataOra ultimo avvio adapter x watchdog + /// + public DateTime adpStartRun; + /// /// Data/ora ultimo avvio adapter /// public DateTime AvvioAdp = DateTime.Now; + /// + /// Veto x esecuzione task AutoDossier + /// + public DateTime dtVetoAutoDossier = DateTime.Now; + + /// + /// Veto x lettura contapezzi (in caso di autoODL con attesa reset ad esempio...) + /// + public DateTime dtVetoReadPzCount = DateTime.Now; + + /// + /// DataOra x veto all'invio dataItem + /// + public DateTime dtVetoSenDataItem = DateTime.Now; + + /// + /// Veto x esecuzione Task2Exe troppo frequenti + /// + public DateTime dtVetoTask2Exe = DateTime.Now; + + /// + /// indica che è richiesto forzatamente reset contapezzi + /// + public DateTime forcePzResetUntil = DateTime.Now.AddMinutes(-1); + + /// + /// ultimo tentativo connessione... + /// + public DateTime lastConnectTry; + /// /// dataOra ultima verifica CNC disconnesso... /// @@ -39,6 +74,11 @@ namespace IOB_UT_NEXT.Iob /// public DateTime lastIobStatusDisplUpdate = DateTime.Now; + /// + /// Ultimo LOG registrazione avvio (x ridurre log notturni...) + /// + public DateTime lastLogStartup = DateTime.Today.AddHours(-1); + /// /// dataOra ultimo log periodico... /// @@ -121,5 +161,11 @@ namespace IOB_UT_NEXT.Iob public DateTime vetoSplit = DateTime.Now.AddMinutes(1); #endregion Public Fields + + + /// + /// Ultimo invio contapezzi (x invio delayed) + /// + public DateTime lastPzCountSend; } } \ No newline at end of file diff --git a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs index 9fdae387..b1ba9b30 100644 --- a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs +++ b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs @@ -28,7 +28,7 @@ namespace IOB_WIN_BECKHOFF.IobBeckhoff // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; vetoCheckStatus = adesso; diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index a6f2b155..eb4e2545 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -35,7 +35,7 @@ namespace IOB_WIN_FANUC.Iob // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; int gSize = 0; int rSize = 8; @@ -2605,9 +2605,8 @@ namespace IOB_WIN_FANUC.Iob } // verifico NON sia vietato gestione contapezzi if (!plcPzCountValid) - //if (!plcPzCountValid || !rawInputRead) { - lgInfo($"Veto check contapezzi e ODL fino alle {dtVetoReadPzCount}"); + lgInfo($"Veto check contapezzi e ODL fino alle {DtHelp.dtVetoReadPzCount}"); } else { @@ -2616,7 +2615,7 @@ namespace IOB_WIN_FANUC.Iob { // controllo se è passato intervallo minimo tra 2 // controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -2650,17 +2649,17 @@ namespace IOB_WIN_FANUC.Iob pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC FANUC: {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } diff --git a/IOB-WIN-FORM/Iob/BaseObj.cs b/IOB-WIN-FORM/Iob/BaseObj.cs index 1a3bdc6e..a0fb40f8 100644 --- a/IOB-WIN-FORM/Iob/BaseObj.cs +++ b/IOB-WIN-FORM/Iob/BaseObj.cs @@ -239,13 +239,13 @@ namespace IOB_WIN_FORM.Iob { lg.Factory.Configuration.Variables["codIOB"] = IOBConfFull.General.FilenameIOB; DateTime adesso = DateTime.Now; - if (adesso.Subtract(lastLogStartup).TotalMinutes > vetoLogStartupDuration) + if (adesso.Subtract(DtHelp.lastLogStartup).TotalMinutes > vetoLogStartupDuration) { lg.Info(message); // se supera di 5 minutis cadenza -_> reimposto veto... - if (adesso.Subtract(lastLogStartup).TotalMinutes > vetoLogStartupDuration + 5) + if (adesso.Subtract(DtHelp.lastLogStartup).TotalMinutes > vetoLogStartupDuration + 5) { - lastLogStartup = adesso; + DtHelp.lastLogStartup = adesso; } } if (sendToForm) @@ -268,13 +268,13 @@ namespace IOB_WIN_FORM.Iob { lg.Factory.Configuration.Variables["codIOB"] = IOBConfFull.General.FilenameIOB; DateTime adesso = DateTime.Now; - if (adesso.Subtract(lastLogStartup).TotalMinutes > vetoLogStartupDuration) + if (adesso.Subtract(DtHelp.lastLogStartup).TotalMinutes > vetoLogStartupDuration) { lg.Info(message, args); // se supera di 5 minutis cadenza -_> reimposto veto... - if (adesso.Subtract(lastLogStartup).TotalMinutes > vetoLogStartupDuration + 5) + if (adesso.Subtract(DtHelp.lastLogStartup).TotalMinutes > vetoLogStartupDuration + 5) { - lastLogStartup = adesso; + DtHelp.lastLogStartup = adesso; } } sendToLogWatch("INFO", message, args); diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 9a29a23d..8206b888 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -77,10 +77,10 @@ namespace IOB_WIN_FORM.Iob SetupQueue(); - lastConnectTry = DateTime.Now; + DtHelp.lastConnectTry = DateTime.Now; lgInfo("Avvio preliminare AdapterGeneric"); - lastLogStartup = DateTime.Now; + DtHelp.lastLogStartup = DateTime.Now; // setup currProdData & last prod data currProdData = redisMan.redGetHashDict(rKeyCurrProdData); @@ -734,13 +734,13 @@ namespace IOB_WIN_FORM.Iob { // imposto flag adapter running.. adpCommAct = true; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; } catch (Exception exc) { string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}"; adpCommAct = false; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; currDispData.newLiveLogData = errore; } if (adpCommAct) @@ -820,11 +820,11 @@ namespace IOB_WIN_FORM.Iob // 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")) + if (DateTime.Now.Subtract(DtHelp.adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) { // tolgo flag running adpCommAct = false; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; } } } @@ -840,11 +840,11 @@ namespace IOB_WIN_FORM.Iob { waitRecMSec = IOBConfFull.General.WaitRecMsec; } - DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); + DateTime dtVeto = DtHelp.lastConnectTry.AddMilliseconds(waitRecMSec); if (DateTime.Now > dtVeto) { - lgInfo($"Veto Time Elapsed | lastConnectTry: {lastConnectTry} | waitRecMSec {waitRecMSec} ms) | NOW tryConnect"); - lastConnectTry = DateTime.Now; + lgInfo($"Veto Time Elapsed | DtHelp.lastConnectTry: {DtHelp.lastConnectTry} | waitRecMSec {waitRecMSec} ms) | NOW tryConnect"); + DtHelp.lastConnectTry = DateTime.Now; tryConnect(); } } @@ -1096,7 +1096,7 @@ namespace IOB_WIN_FORM.Iob } // imposto reset gestione pzcount da parametri DtHelp.vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } else { @@ -1174,7 +1174,7 @@ namespace IOB_WIN_FORM.Iob lgDebug($"Generic.executeTasks {tName} | resetContapezziPLC"); taskOk = resetContapezziPLC(codTav); DtHelp.vetoQueuePzCount = DateTime.Now.AddMinutes(IOBConfFull.General.DelayReadPzCountSetup); - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } taskVal = taskOk ? "startSetup | RESET: SETUP START" : "startSetup | PZ RESET DISABLED | NO EXEC"; lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}"); @@ -1958,9 +1958,9 @@ namespace IOB_WIN_FORM.Iob if (IOBConfFull.FluxLog.AutoSnapshotDossier) { DateTime adesso = DateTime.Now; - if (adesso > dtVetoAutoDossier) + if (adesso > DtHelp.dtVetoAutoDossier) { - dtVetoAutoDossier = adesso.AddMinutes(30); + DtHelp.dtVetoAutoDossier = adesso.AddMinutes(30); lgTrace("Richiesta ProcessAutoDossier"); lgTrace("AutoSnapshotDossier abilitato"); @@ -2008,7 +2008,7 @@ namespace IOB_WIN_FORM.Iob { lgTrace("ProcessAutoOdlAsync | AutoChangeOdl abilitato"); // imposto il veto lettura contapezzi a 1 minuto x iniziare... - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); string fullUrl = ""; DateTime adesso = DateTime.Now; DateTime inizioOdl = adesso; @@ -9017,10 +9017,10 @@ namespace IOB_WIN_FORM.Iob { // se non ho veto task2exe DateTime adesso = DateTime.Now; - if (adesso > dtVetoTask2Exe) + if (adesso > DtHelp.dtVetoTask2Exe) { // blocco fisso a 5 sec x ora - dtVetoTask2Exe = adesso.AddSeconds(5); + DtHelp.dtVetoTask2Exe = adesso.AddSeconds(5); // recupero elenco delle cose da fare string resp = await getTask2exe(""); // se ho qualcosa --> creo obj e lo accodo... diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index 1595b91a..ba73a6b8 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -31,7 +31,7 @@ namespace IOB_WIN_FORM.Iob lgInfo("NEW IobPing (WatchDog)"); // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 6b086426..ce6f7adf 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -71,7 +71,7 @@ namespace IOB_WIN_FORM.Iob // gestione invio ritardato contapezzi DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; DtHelp.lastEvCheck = adesso; DtHelp.lastSimData = adesso; @@ -1416,7 +1416,7 @@ namespace IOB_WIN_FORM.Iob } } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } // provo a fare split ODL... diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index d3b96925..498c1968 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -39,7 +39,7 @@ namespace IOB_WIN_FTP.Iob B_input = 0; // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index 17f13141..c26045da 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -23,7 +23,7 @@ namespace IOB_WIN_KAWASAKI.Iob { // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // init connessione setConnection(); @@ -889,7 +889,7 @@ namespace IOB_WIN_KAWASAKI.Iob { // ora processo il contapezzi... controllo se è passato intervallo minimo tra 2 // controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -924,7 +924,7 @@ namespace IOB_WIN_KAWASAKI.Iob pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } else @@ -934,11 +934,11 @@ namespace IOB_WIN_KAWASAKI.Iob } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgError($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC KAWASAKI {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index 96a1dfd5..73161e69 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -46,7 +46,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP { // gestione invio ritardato contapezzi - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; // inizializzo parametri... parametri = new ModBusTcpParamConf(); diff --git a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs index a6d5a7fc..da35c448 100644 --- a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs +++ b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs @@ -26,7 +26,7 @@ namespace IOB_WIN_MITSUBISHI.Iob lgInfo("Start init Mitsubishi"); // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // inizializzo correttamente aree memoria secondo CONF - da IobConfFull @@ -1663,7 +1663,7 @@ namespace IOB_WIN_MITSUBISHI.Iob { // controllo se è passato intervallo minimo tra 2 // controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -1697,17 +1697,17 @@ namespace IOB_WIN_MITSUBISHI.Iob pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC MITSUBISHI: {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } #endif diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index ce86dcdc..35e01dd7 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -60,7 +60,7 @@ namespace IOB_WIN_MTC.Iob // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; lastCurrent = adesso; // ora leggo il file di conf specifico.... @@ -1349,7 +1349,7 @@ namespace IOB_WIN_MTC.Iob // fix tempi! DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; lastCurrent = adesso; } @@ -2032,12 +2032,12 @@ namespace IOB_WIN_MTC.Iob { // verifico veto ad invio (ogni 60 min...) DateTime adesso = DateTime.Now; - if (adesso > dtVetoSenDataItem) + if (adesso > DtHelp.dtVetoSenDataItem) { string rawData = JsonConvert.SerializeObject(dataItems); var resp = HttpService.CallUrlPost($"{urlSaveDataItems}", rawData); // imposto nuovo veto - dtVetoSenDataItem = adesso.AddMinutes(minVetoSendDataItem); + DtHelp.dtVetoSenDataItem = adesso.AddMinutes(minVetoSendDataItem); } } } @@ -2048,21 +2048,10 @@ namespace IOB_WIN_MTC.Iob /// private void traceObservation(IObservation observation) { +#if DEBUG // SOLO in debug... -#if false - string sMsg = $"VETO: - {observation.DataItemId} | {observation.Category}"; - if (observation.Values != null && observation.Values.Count() > 0) - { - foreach (var item in observation.Values) - { - sMsg += $" | {item.Key}: {item.Value}"; - } - } - lgTrace(sMsg); - trackExchDataRaw(sMsg.Length, 1024); -#endif - trackExchData(64, 4096); +#endif } #endregion Private Methods diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index e0098393..59ad6623 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -25,7 +25,7 @@ namespace IOB_WIN_OMRON.Iob { // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // imposto i parametri PLC diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index 54a04200..e097cdc7 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -52,7 +52,7 @@ namespace IOB_WIN_OPC_UA.IobOpc // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; lastCurrent = adesso; lgTrace($"Aggiornato IobOpcUa.lastCurrent: {lastCurrent}"); @@ -2658,7 +2658,7 @@ namespace IOB_WIN_OPC_UA.IobOpc // fix tempi! DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; lastCurrent = adesso; lgTrace($"Aggiornato doConnect.lastCurrent: {lastCurrent}"); diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs index 6537c022..4c3b7db5 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemens.cs @@ -161,7 +161,7 @@ namespace IOB_WIN_OPC_UA.IobOpc { lgInfo("OpcUaSiemens.resetContapezziPLC 02"); // imposto un veto in lettura cmq x evitare problemi - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); if (opcUaParams.actResetCounter != null && opcUaParams.actResetCounter.Count > 0) { lgInfo("OpcUaSiemens.resetContapezziPLC 03"); @@ -318,7 +318,7 @@ namespace IOB_WIN_OPC_UA.IobOpc if (!isWorking && delayMinReadPzCount > 0) { // metto un veto al contapezzi... - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } // se ho emergenza premuta --> emergenza! @@ -335,10 +335,10 @@ namespace IOB_WIN_OPC_UA.IobOpc { // RUN = LAVORA! B_input += (1 << 1); - if (dtVetoReadPzCount >= adesso) + if (DtHelp.dtVetoReadPzCount >= adesso) { // tolgo eventuale veto al contapezzi... - dtVetoReadPzCount = DateTime.Now.AddSeconds(-1); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddSeconds(-1); } } // se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM! diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs index 74425e59..c676e972 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUaSiemensRama.cs @@ -95,7 +95,7 @@ namespace IOB_WIN_OPC_UA.IobOpc plcWriteParams(ref list2Write); // aggiungo 1 altro step di attesa alla lettura pezzi x sicurezza... - dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount); } ///
diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 1072095e..60171751 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -40,7 +40,7 @@ namespace IOB_WIN_OSAI.Iob { // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; if (IOBConfFull != null) { @@ -710,7 +710,7 @@ namespace IOB_WIN_OSAI.Iob { // controllo se è passato intervallo minimo tra 2 controlli/elaborazioni // x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -743,16 +743,16 @@ namespace IOB_WIN_OSAI.Iob pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC OSAI: {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index ed52a548..31915b33 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -43,7 +43,7 @@ namespace IOB_WIN_SHELLY.Iob B_input = 0; // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index 4be5341a..cdc1e296 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -44,7 +44,7 @@ namespace IOB_WIN_SHELLY.Iob B_input = 0; // init datetime counters DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... diff --git a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs index fb5a6638..43572c65 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs @@ -175,7 +175,7 @@ namespace IOB_WIN { // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // init conf _IOBConf = IOBConf; @@ -705,7 +705,7 @@ namespace IOB_WIN { // ora processo il contapezzi... // controllo se è passato intervallo minimo tra 2 controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... @@ -734,16 +734,16 @@ namespace IOB_WIN lgInfo(string.Format("Errore salvataggio Contapezzi SIEMENST-TORRI: {0} | Contapezzi interno {1} | Errore salvataggio: {2}", lastCountCNC, contapezzi, retVal)); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo(string.Format("Attenzione: mancanza ODL non procedo con gestione contapezzi. Contapezzi SIEMENST-TORRI: {0} | Contapezzi interno {1}", lastCountCNC, contapezzi)); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index f4dc94be..31f95907 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -55,7 +55,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens { // gestione invio ritardato contapezzi - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; // inizializzo parametri... parametri = new connParamS7() @@ -1510,7 +1510,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // procedura corrente... needRefresh = false; lgInfoStartup("SIEMENS: tryConnect"); - lastConnectTry = DateTime.Now; + DtHelp.lastConnectTry = DateTime.Now; tryConnect(); lgInfoStartup("End init Adapter SIEMENS"); if (isVerboseLog) diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs index 6ff38b52..f72d13f9 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFape.cs @@ -155,7 +155,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // verifico NON sia vietato gestione contapezzi if (!plcPzCountValid || !rawInputRead) { - lgInfo($"Veto check contapezzi e ODL fino alle {dtVetoReadPzCount}"); + lgInfo($"Veto check contapezzi e ODL fino alle {DtHelp.dtVetoReadPzCount}"); } else { @@ -163,7 +163,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens { // ora processo il contapezzi... controllo se è passato intervallo minimo tra 2 // controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -195,17 +195,17 @@ namespace IOB_WIN_SIEMENS.IobSiemens pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC SIEMENS {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs index 3bbd9cd4..65aa4a7a 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensFapeV2.cs @@ -259,7 +259,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // verifico NON sia vietato gestione contapezzi if (!plcPzCountValid || !rawInputRead) { - lgInfo($"Veto check contapezzi e ODL fino alle {dtVetoReadPzCount}"); + lgInfo($"Veto check contapezzi e ODL fino alle {DtHelp.dtVetoReadPzCount}"); } else { @@ -267,7 +267,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens { // ora processo il contapezzi... controllo se è passato intervallo minimo tra 2 // controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -300,17 +300,17 @@ namespace IOB_WIN_SIEMENS.IobSiemens pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC SIEMENS {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs index dac4568b..3ff09d78 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensLasco.cs @@ -196,7 +196,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // reset type counter valore += 4; // controlla se resettare il pz reset... - forcePzReset = forcePzResetUntil >= DateTime.Now; + forcePzReset = DtHelp.forcePzResetUntil >= DateTime.Now; } valore += (byte)(counterMes2Plc << 7); MemBlock[0] = (byte)valore; @@ -206,7 +206,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens case taskType.forceResetPzCount: // forzo reset contapezzi forcePzReset = true; - forcePzResetUntil = DateTime.Now.AddSeconds(3); + DtHelp.forcePzResetUntil = DateTime.Now.AddSeconds(3); break; case taskType.setParameter: @@ -235,7 +235,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens lgInfo("!!! Richiesta startSetup - BLOCCO contapezzi LASCO !!!"); // forzo reset contapezzi forcePzReset = true; - forcePzResetUntil = DateTime.Now.AddSeconds(3); + DtHelp.forcePzResetUntil = DateTime.Now.AddSeconds(3); break; case taskType.stopSetup: @@ -244,7 +244,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens lgInfo("!!! Richiesta stopSetup - SBLOCCO contapezzi LASCO!!!"); // forzo reset contapezzi forcePzReset = true; - forcePzResetUntil = DateTime.Now.AddSeconds(3); + DtHelp.forcePzResetUntil = DateTime.Now.AddSeconds(3); break; case taskType.fixStopSetup: diff --git a/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs b/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs index e53c940e..40ab6823 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/SiemensTorri.cs @@ -37,7 +37,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens } // provo ad aggiungere un piccolo delay lettura pzCount - dtVetoReadPzCount = DateTime.Now.AddSeconds(10); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddSeconds(10); lgInfo("NEW IOB SIEMENS versione TORRI avviato | vers 2024"); } @@ -377,7 +377,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // verifico NON sia vietato gestione contapezzi if (!plcPzCountValid || !rawInputRead) { - lgInfo($"Veto check contapezzi e ODL fino alle {dtVetoReadPzCount}"); + lgInfo($"Veto check contapezzi e ODL fino alle {DtHelp.dtVetoReadPzCount}"); } else { @@ -385,7 +385,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens { // controllo se è passato intervallo minimo tra 2 controlli/elaborazioni x // distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // semplifico: se ho differenza invio in blocco if (contapezziPLC > contapezziIOB) @@ -410,15 +410,15 @@ namespace IOB_WIN_SIEMENS.IobSiemens } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC SIEMENST-TORRI: {contapezziPLC} | contapezziIOB: {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } // metto cmq 1 sec a veto verifica contapezzi - dtVetoReadPzCount = DateTime.Now.AddSeconds(1); + DtHelp.dtVetoReadPzCount = DateTime.Now.AddSeconds(1); } // log opzionale! if (verboseLog) diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 38bd60f8..521f7f4b 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -43,7 +43,7 @@ namespace IOB_WIN /// /// DataOra ultimo avvio adapter x watchdog /// - protected DateTime adpStartRun; + protected DateTime DtHelp.adpStartRun; /// /// Vettore 32 BIT valori in ingresso al filtro @@ -78,12 +78,12 @@ namespace IOB_WIN /// /// ultimo tentativo connessione... /// - protected DateTime lastConnectTry; + protected DateTime DtHelp.lastConnectTry; /// /// Ultimo invio contapezzi (x invio delayed) /// - protected DateTime lastPzCountSend; + protected DateTime DtHelp.lastPzCountSend; /// /// Dizionario ultimi valori (double) delle TSVC @@ -363,7 +363,7 @@ namespace IOB_WIN // configurazione... cIobConf = IOBConf; - lastConnectTry = DateTime.Now; + DtHelp.lastConnectTry = DateTime.Now; // aggiungo nel logger IDX Macchina lg = LogManager.GetCurrentClassLogger(); @@ -3184,13 +3184,13 @@ namespace IOB_WIN { // imposto flag adapter running.. adpCommAct = true; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; } catch (Exception exc) { string errore = $"Adapter NOT STARTED!!!{Environment.NewLine}{exc}"; adpCommAct = false; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; currDispData.newLiveLogData = errore; } if (adpCommAct) @@ -3278,11 +3278,11 @@ namespace IOB_WIN // log ADP running lgError("Non eseguo chiamata: ADP ancora in running"); // se è bloccato da oltre maxSec lo sblocco... - if (DateTime.Now.Subtract(adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) + if (DateTime.Now.Subtract(DtHelp.adpStartRun).TotalSeconds > utils.CRI("maxAdapterLockSec")) { // tolgo flag running adpCommAct = false; - adpStartRun = DateTime.Now; + DtHelp.adpStartRun = DateTime.Now; } } } @@ -3293,11 +3293,11 @@ namespace IOB_WIN { // controllo se sia scaduto periodi di veto al tryConnect... int waitRecMSec = utils.CRI("waitRecMSec") * 2; - DateTime dtVeto = lastConnectTry.AddMilliseconds(waitRecMSec); + DateTime dtVeto = DtHelp.lastConnectTry.AddMilliseconds(waitRecMSec); if (DateTime.Now > dtVeto) { lgInfo($"Retry Time Elapsed (waited for {waitRecMSec} ms)--> NOW tryConnect"); - lastConnectTry = DateTime.Now; + DtHelp.lastConnectTry = DateTime.Now; tryConnect(); } } diff --git a/IOB-WIN/IobOSAI.cs b/IOB-WIN/IobOSAI.cs index 71dec67f..a7d172dd 100644 --- a/IOB-WIN/IobOSAI.cs +++ b/IOB-WIN/IobOSAI.cs @@ -59,7 +59,7 @@ namespace IOB_WIN { // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; lastWarnODL = DateTime.Now; if (IOBConf != null) { @@ -251,7 +251,7 @@ namespace IOB_WIN if (!string.IsNullOrEmpty(currODL) && currODL != "0") { // controllo se è passato intervallo minimo tra 2 controlli/elaborazioni x distanziare invio e ridurre letture - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { // se sono differenti MOSTRO... if (contapezziPLC != contapezziIOB) @@ -282,16 +282,16 @@ namespace IOB_WIN pzCntReload(true); } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } else { - if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay)) + if (DateTime.Now >= DtHelp.lastPzCountSend.AddMilliseconds(pzCountDelay)) { lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC OSAI: {contapezziPLC} | contapezziIOB {contapezziIOB}"); // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } } diff --git a/IOB-WIN/IobSimula.cs b/IOB-WIN/IobSimula.cs index f0ff7617..9703402e 100644 --- a/IOB-WIN/IobSimula.cs +++ b/IOB-WIN/IobSimula.cs @@ -107,7 +107,7 @@ namespace IOB_WIN // gestione invio ritardato contapezzi DateTime adesso = DateTime.Now; - lastPzCountSend = adesso; + DtHelp.lastPzCountSend = adesso; lastWarnODL = adesso; DtHelp.lastEvCheck = adesso; DtHelp.lastSimData = adesso; @@ -407,7 +407,7 @@ namespace IOB_WIN } } // resetto timer... - lastPzCountSend = DateTime.Now; + DtHelp.lastPzCountSend = DateTime.Now; } } // provo a fare split ODL SE NON E' multi.... diff --git a/refactor_progress_tracker.pdf b/refactor_progress_tracker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b8ec450df356f1d98b342a9549c25baedfc37e64 GIT binary patch literal 545846 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 _ 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

8E2KPksE^^61Qj9rq?II%3|2&xdmcuU7}> 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%0D7Pmg+An%`MD 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 LcYpcY&+q;Vq39=K literal 0 HcmV?d00001 From 8cff27105ed11b115371c403b20da921e573bf40 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 21:55:01 +0200 Subject: [PATCH 38/39] Ulteriori modifiche datetime vars --- IOB-UT-NEXT/Iob/BaseObj.cs | 19 ------------- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 26 ++++++++++++++---- IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs | 2 +- IOB-WIN-FANUC/Iob/Fanuc.cs | 6 ++-- IOB-WIN-FORM/Iob/Generic.cs | 4 +-- IOB-WIN-FORM/Iob/PingWatchDog.cs | 2 +- IOB-WIN-FORM/Iob/Simula.cs | 2 +- IOB-WIN-FTP/Iob/Ftp.cs | 2 +- IOB-WIN-KAWASAKI/Iob/Kawasaki.cs | 6 ++-- IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 2 +- IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs | 6 ++-- IOB-WIN-MTC/Iob/MTConn.cs | 4 +-- IOB-WIN-OMRON/Iob/Omron.cs | 6 ++-- IOB-WIN-OPC-UA/IobOpc/OpcUa.cs | 4 +-- IOB-WIN-OSAI/Iob/OSAI.cs | 6 ++-- IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 2 +- IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 2 +- .../IobSiemens/IobSiemensTorri_legacy.cs | 6 ++-- IOB-WIN-SIEMENS/IobSiemens/Siemens.cs | 4 +-- IOB-WIN/IobGeneric.cs | 2 +- IOB-WIN/IobOSAI.cs | 6 ++-- IOB-WIN/IobSimula.cs | 2 +- refactor_progress_tracker.pdf | Bin 545846 -> 545846 bytes 23 files changed, 58 insertions(+), 63 deletions(-) diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index 5ad36184..aea33c0d 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -233,7 +233,6 @@ namespace IOB_UT_NEXT.Iob ///

public int vetoPingSec = 1; - /// /// alias booleano true = W /// @@ -253,7 +252,6 @@ namespace IOB_UT_NEXT.Iob ///
public static bool DemoOut => utils.CRB("DemoOut"); - /// /// Verifica se sia abilitato test lettura blocchi memoria all'avvio /// @@ -460,7 +458,6 @@ namespace IOB_UT_NEXT.Iob ///
protected bool forcePzReset = false; - /// /// DEADBAND globale se impostata (es x gestire variazioni minime valori FluxLog da inviare...) /// @@ -481,13 +478,11 @@ namespace IOB_UT_NEXT.Iob ///
protected bool inSetup = false; - /// /// Dizionario ULTIMI valori impostati x produzione /// protected Dictionary lastProdData = new Dictionary(); - /// /// Dizionario ultimi valori (string) delle TSS /// @@ -503,16 +498,6 @@ namespace IOB_UT_NEXT.Iob ///
protected Dictionary LastTSVC = new Dictionary(); - /// - /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) - /// - protected DateTime lastWarnODL; - - /// - /// ultima data-ora invio parametri write x ribadire status - /// - protected DateTime lastWriteParamsUpsert = DateTime.Now; - /// /// Separatore linea (tipicamente x commenti) /// @@ -592,10 +577,6 @@ namespace IOB_UT_NEXT.Iob ///
protected int pingServerMsTimeout = utils.CRI("PingMsTimeout"); - /// - /// DataOra avvio Programma x check avvio - /// - protected DateTime prgStarted = DateTime.Now; /// /// Ritardo minimo x invio contapezzi diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index eb14ff72..477e3489 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -94,6 +94,11 @@ namespace IOB_UT_NEXT.Iob /// public DateTime lastPingConn = DateTime.Now.AddMinutes(-10); + /// + /// Ultimo invio contapezzi (x invio delayed) + /// + public DateTime lastPzCountSend; + /// /// DataOra ultima lettura da PLC /// @@ -114,6 +119,11 @@ namespace IOB_UT_NEXT.Iob ///
public DateTime lastSimData; + /// + /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) + /// + public DateTime lastWarnODL; + /// /// dataOra ultimo segnale inviato al SERVER... /// @@ -124,6 +134,16 @@ namespace IOB_UT_NEXT.Iob ///
public DateTime lastWatchDogPLC = DateTime.Now; + /// + /// ultima data-ora invio parametri write x ribadire status + /// + public DateTime lastWriteParamsUpsert = DateTime.Now; + + /// + /// DataOra avvio Programma x check avvio + /// + public DateTime prgStarted = DateTime.Now; + /// /// Data/ora ultimo spegnimento adapter /// @@ -161,11 +181,5 @@ namespace IOB_UT_NEXT.Iob public DateTime vetoSplit = DateTime.Now.AddMinutes(1); #endregion Public Fields - - - /// - /// Ultimo invio contapezzi (x invio delayed) - /// - public DateTime lastPzCountSend; } } \ No newline at end of file diff --git a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs index b1ba9b30..24a19f10 100644 --- a/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs +++ b/IOB-WIN-BECKHOFF/IobBeckhoff/Beckhoff.cs @@ -29,7 +29,7 @@ namespace IOB_WIN_BECKHOFF.IobBeckhoff // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; vetoCheckStatus = adesso; // ora leggo il file di conf specifico.... diff --git a/IOB-WIN-FANUC/Iob/Fanuc.cs b/IOB-WIN-FANUC/Iob/Fanuc.cs index eb4e2545..88a488dd 100644 --- a/IOB-WIN-FANUC/Iob/Fanuc.cs +++ b/IOB-WIN-FANUC/Iob/Fanuc.cs @@ -36,7 +36,7 @@ namespace IOB_WIN_FANUC.Iob // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; int gSize = 0; int rSize = 8; int xSize = 8; @@ -2587,10 +2587,10 @@ namespace IOB_WIN_FANUC.Iob } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 8206b888..4f5f2769 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -8683,13 +8683,13 @@ namespace IOB_WIN_FORM.Iob else { // se scaduto tempo da ultimo invio... e ho valori - if (currWritePar.Count > 0 && (lastWriteParamsUpsert.AddMinutes(vetoSendWriteUpsert) < adesso)) + if (currWritePar.Count > 0 && (DtHelp.lastWriteParamsUpsert.AddMinutes(vetoSendWriteUpsert) < adesso)) { // invio su cloud parametri! string rawData = JsonSerialize(currWritePar); var res = HttpService.CallUrlPost($"{urlUpdateWriteParams}", rawData); lgInfo($"Reinviato a server stato {updatedPar.Count} parametri WRITE"); - lastWriteParamsUpsert = adesso; + DtHelp.lastWriteParamsUpsert = adesso; } } } diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index ba73a6b8..b034d944 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -32,7 +32,7 @@ namespace IOB_WIN_FORM.Iob // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... DtHelp.lastPING = adesso; diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index ce6f7adf..3e5a75a7 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -72,7 +72,7 @@ namespace IOB_WIN_FORM.Iob // gestione invio ritardato contapezzi DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; DtHelp.lastEvCheck = adesso; DtHelp.lastSimData = adesso; DtHelp.lastRedDuration = adesso; diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index 498c1968..58c8bc73 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -40,7 +40,7 @@ namespace IOB_WIN_FTP.Iob // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... DtHelp.lastPING = adesso; diff --git a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs index c26045da..7c0e5d52 100644 --- a/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs +++ b/IOB-WIN-KAWASAKI/Iob/Kawasaki.cs @@ -24,7 +24,7 @@ namespace IOB_WIN_KAWASAKI.Iob // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; // init connessione setConnection(); @@ -867,10 +867,10 @@ namespace IOB_WIN_KAWASAKI.Iob } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index 73161e69..144dbf0c 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -47,7 +47,7 @@ namespace IOB_WIN_MBUS.IobModbusTCP // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; // inizializzo parametri... parametri = new ModBusTcpParamConf(); #if false diff --git a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs index da35c448..75c3866a 100644 --- a/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs +++ b/IOB-WIN-MITSUBISHI/Iob/Mitsubishi.cs @@ -27,7 +27,7 @@ namespace IOB_WIN_MITSUBISHI.Iob // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; // inizializzo correttamente aree memoria secondo CONF - da IobConfFull IOB_UT_NEXT.Config.Mem.MemAreaDto currArea = new IOB_UT_NEXT.Config.Mem.MemAreaDto(); @@ -1643,10 +1643,10 @@ namespace IOB_WIN_MITSUBISHI.Iob } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index 35e01dd7..e367ae95 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -61,7 +61,7 @@ namespace IOB_WIN_MTC.Iob // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; lastCurrent = adesso; // ora leggo il file di conf specifico.... string jsonFileName = getOptPar("MTC_PARAM_CONF"); @@ -1350,7 +1350,7 @@ namespace IOB_WIN_MTC.Iob // fix tempi! DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; lastCurrent = adesso; } catch diff --git a/IOB-WIN-OMRON/Iob/Omron.cs b/IOB-WIN-OMRON/Iob/Omron.cs index 59ad6623..7c39c1ff 100644 --- a/IOB-WIN-OMRON/Iob/Omron.cs +++ b/IOB-WIN-OMRON/Iob/Omron.cs @@ -26,7 +26,7 @@ namespace IOB_WIN_OMRON.Iob // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; // imposto i parametri PLC setParamPlc(); @@ -706,10 +706,10 @@ namespace IOB_WIN_OMRON.Iob } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs index e097cdc7..0c9e864f 100644 --- a/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs +++ b/IOB-WIN-OPC-UA/IobOpc/OpcUa.cs @@ -53,7 +53,7 @@ namespace IOB_WIN_OPC_UA.IobOpc // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; lastCurrent = adesso; lgTrace($"Aggiornato IobOpcUa.lastCurrent: {lastCurrent}"); opcUaParams = IOBConfFull.Special.OpcUaConf; @@ -2659,7 +2659,7 @@ namespace IOB_WIN_OPC_UA.IobOpc // fix tempi! DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; lastCurrent = adesso; lgTrace($"Aggiornato doConnect.lastCurrent: {lastCurrent}"); } diff --git a/IOB-WIN-OSAI/Iob/OSAI.cs b/IOB-WIN-OSAI/Iob/OSAI.cs index 60171751..d101a16a 100644 --- a/IOB-WIN-OSAI/Iob/OSAI.cs +++ b/IOB-WIN-OSAI/Iob/OSAI.cs @@ -41,7 +41,7 @@ namespace IOB_WIN_OSAI.Iob // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; if (IOBConfFull != null) { signLUT = IOBConfFull.Device.SigLUT; @@ -690,10 +690,10 @@ namespace IOB_WIN_OSAI.Iob } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 31915b33..569fea3d 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -44,7 +44,7 @@ namespace IOB_WIN_SHELLY.Iob // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... DtHelp.lastPING = adesso; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index cdc1e296..3beef34d 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -45,7 +45,7 @@ namespace IOB_WIN_SHELLY.Iob // init datetime counters DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; vetoCheckStatus = adesso; // 2023.09.05 imposto anche primo ping e check disconnected... DtHelp.lastPING = adesso; diff --git a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs index 43572c65..7239f637 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/IobSiemensTorri_legacy.cs @@ -176,7 +176,7 @@ namespace IOB_WIN // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; // init conf _IOBConf = IOBConf; // inizializzo parametri... @@ -695,10 +695,10 @@ namespace IOB_WIN } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } if (currODL != null && currODL != "" && currODL != "0") diff --git a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs index 31f95907..ec89d272 100644 --- a/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs +++ b/IOB-WIN-SIEMENS/IobSiemens/Siemens.cs @@ -56,7 +56,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens // gestione invio ritardato contapezzi DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; // inizializzo parametri... parametri = new connParamS7() { @@ -359,7 +359,7 @@ namespace IOB_WIN_SIEMENS.IobSiemens if (pzCountAll >= 0) { // 2025.02.26 aggiunto controllo che non sia appena avviato IOB - bool isJustStarted = DateTime.Now.Subtract(prgStarted).TotalMinutes < 3; + bool isJustStarted = DateTime.Now.Subtract(DtHelp.prgStarted).TotalMinutes < 3; // verifico il NUOVO contapezzi PRIMA di accettare ogni incremento... int deltaPzCount = pzCountAll - contapezziPLC; double maxDelta = DateTime.Now.Subtract(plcLastPzRead).TotalMinutes / (plcAvgTc / 60); diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 521f7f4b..821f618c 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -93,7 +93,7 @@ namespace IOB_WIN /// /// Ultima registrazione warning x ODL mancante (x scrivere solo ogni 15 secondi) /// - protected DateTime lastWarnODL; + protected DateTime DtHelp.lastWarnODL; /// /// indica se serva refresh parametri e quindi PLC... diff --git a/IOB-WIN/IobOSAI.cs b/IOB-WIN/IobOSAI.cs index a7d172dd..00820361 100644 --- a/IOB-WIN/IobOSAI.cs +++ b/IOB-WIN/IobOSAI.cs @@ -60,7 +60,7 @@ namespace IOB_WIN // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); DtHelp.lastPzCountSend = DateTime.Now; - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; if (IOBConf != null) { // inizializzo correttamente aree memoria secondo CONF - iniFileName @@ -232,10 +232,10 @@ namespace IOB_WIN } catch (Exception exc) { - if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15) + if (DateTime.Now.Subtract(DtHelp.lastWarnODL).TotalSeconds > 15) { lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL); - lastWarnODL = DateTime.Now; + DtHelp.lastWarnODL = DateTime.Now; } } } diff --git a/IOB-WIN/IobSimula.cs b/IOB-WIN/IobSimula.cs index 9703402e..b09c88bd 100644 --- a/IOB-WIN/IobSimula.cs +++ b/IOB-WIN/IobSimula.cs @@ -108,7 +108,7 @@ namespace IOB_WIN DateTime adesso = DateTime.Now; DtHelp.lastPzCountSend = adesso; - lastWarnODL = adesso; + DtHelp.lastWarnODL = adesso; DtHelp.lastEvCheck = adesso; DtHelp.lastSimData = adesso; // sistemo parametri x simulazione... diff --git a/refactor_progress_tracker.pdf b/refactor_progress_tracker.pdf index b8ec450df356f1d98b342a9549c25baedfc37e64..9657994b661f4b5dc55be7dc38cd6caef379ae86 100644 GIT binary patch delta 478 zcmdnCLUG#)#R-3yO$^K?{#O7|o0S+3gz}nb+SzfX=B4B(WpSBIX6)6SEEB$m(RA|7 z@UN3^g!62^72(9lXfk)(&N1ObcQ3$F25|OT{p7Vf%TloQ4CmGESoOWo#Sp8d_LRmb)RwXwYnWquuldBM>tIF*6Xe05K~Nvu!uM!OmC%0OIGq^Z)<= delta 450 zcmdnCLUG#)#R-3yjSMU&{#O7|o0S+3gigK@&ckRjnXy-QvP}4%$>$Y13(3=t1Y zf{Hx6AoJ#@gao!x9<_e4 zYR|995BBj-{=D}cpP{Cm9am~zN`6unm$B*O!aUv0GW&&?CQmryJh|}ThROZMTqma; z+Q4WydEzmr&5?)wf%XBVULOHTxlF!)bR(nT=84B9LWEOKK!hVt0)@dkd{2ROxJ-U< zdOcXjGy!fS47C*|wYBU}vlW E0H|)fDgXcg From fce6b0b026a76a821865605390823debfa79379f Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Sat, 23 May 2026 22:32:21 +0200 Subject: [PATCH 39/39] Modifica gestione Queue: condensate in helper --- IOB-UT-NEXT/IOB-UT-NEXT.csproj | 1 + IOB-UT-NEXT/Iob/BaseObj.cs | 71 ++-------- IOB-UT-NEXT/Iob/DateTimeHelper.cs | 3 +- IOB-UT-NEXT/Iob/QueueHelper.cs | 66 ++++++++++ IOB-WIN-FILE/IobFile/IobFileSoitaab.cs | 2 +- IOB-WIN-FORM/AdapterForm.cs | 18 +-- IOB-WIN-FORM/Iob/Generic.cs | 168 ++++++++++++------------ IOB-WIN-FORM/Iob/PingWatchDog.cs | 20 +-- IOB-WIN-FORM/Iob/Simula.cs | 2 +- IOB-WIN-FTP/Iob/Ftp.cs | 16 +-- IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs | 1 - IOB-WIN-MTC/Iob/MTConn.cs | 1 - IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs | 16 +-- IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs | 16 +-- IOB-WIN-SQL/Iob/IobFileSoitaab.cs | 2 +- IOB-WIN-SQL/IobSql/SqlServPama.cs | 2 +- IOB-WIN/IobGeneric.cs | 82 ++++++------ 17 files changed, 252 insertions(+), 235 deletions(-) create mode 100644 IOB-UT-NEXT/Iob/QueueHelper.cs diff --git a/IOB-UT-NEXT/IOB-UT-NEXT.csproj b/IOB-UT-NEXT/IOB-UT-NEXT.csproj index c05344f2..4b3f227b 100644 --- a/IOB-UT-NEXT/IOB-UT-NEXT.csproj +++ b/IOB-UT-NEXT/IOB-UT-NEXT.csproj @@ -157,6 +157,7 @@ + diff --git a/IOB-UT-NEXT/Iob/BaseObj.cs b/IOB-UT-NEXT/Iob/BaseObj.cs index aea33c0d..6702d662 100644 --- a/IOB-UT-NEXT/Iob/BaseObj.cs +++ b/IOB-UT-NEXT/Iob/BaseObj.cs @@ -104,6 +104,11 @@ namespace IOB_UT_NEXT.Iob /// public DateTimeHelper DtHelp; + /// + /// Collettore di tutte le Queue gestite + /// + public QueueHelper QHelp; + /// /// Abilitazione lettura PrgName /// @@ -155,59 +160,12 @@ namespace IOB_UT_NEXT.Iob ///
public bool procIotMem = false; - /// - /// Coda valori ALLARMI ove gestiti... - /// - public DataQueue QueueAlarm; - - /// - /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) - /// - public DataQueue QueueFLog; - - /// - /// Oggetto della coda degli elementi letti (e non ancora trasmessi) - /// - public DataQueue QueueIN; - - /// - /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... - /// - 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) - /// NB: sono salvati serializzati come stringhe - /// - public DataQueue QueueRawTransf; - - /// - /// Coda delle richieste dal server (Task2Exe) - /// - public DataQueue QueueSrvReq; - - // = new DataQueue("000", "QueueRawTransf", false); - /// - /// Coda delle risposte al server (Task2Exe) - /// - public DataQueue QueueSrvResp; - - /// - /// Coda valori LOG UTENTE (da non sottocampionare come samples)... - /// - public DataQueue QueueULog; /// /// alias booleano false = R /// public bool R = false; - // = new DataQueue("000", "QueueULog", false); /// /// 32 byte input base (es strobe, 8 word da 32 bit di flags...) /// @@ -868,17 +826,16 @@ namespace IOB_UT_NEXT.Iob string codIob = IOBConfFull.General.FilenameIOB; bool useRedis = IOBConfFull.General.EnabRedisQue; // valutare se portare di nuovo in redis... - QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan); - QueueSrvResp = new DataQueue(codIob, "QueueServResp", useRedis, redisMan); + QHelp.QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan); + QHelp.QueueSrvResp = new DataQueue(codIob, "QueueServResp", useRedis, redisMan); // fix a NON redis - QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan); - QueueFLog = new DataQueue(codIob, "QueueFLog", false, redisMan); - //QueueFLog = new DataQueue(codIob, "QueueFLog", useRedis, redisMan); - QueueMessages = new DataQueue(codIob, "QueueMessages", false, redisMan); - QueuePing = new DataQueue(codIob, "QueuePing", false, redisMan); - QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan); - QueueSrvReq = new DataQueue(codIob, "QueueServReq", false, redisMan); - QueueULog = new DataQueue(codIob, "QueueULog", false, redisMan); + QHelp.QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan); + QHelp.QueueFLog = new DataQueue(codIob, "QueueFLog", false, redisMan); + QHelp.QueueMessages = new DataQueue(codIob, "QueueMessages", false, redisMan); + QHelp.QueuePing = new DataQueue(codIob, "QueuePing", false, redisMan); + QHelp.QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan); + QHelp.QueueSrvReq = new DataQueue(codIob, "QueueServReq", false, redisMan); + QHelp.QueueULog = new DataQueue(codIob, "QueueULog", false, redisMan); } /// diff --git a/IOB-UT-NEXT/Iob/DateTimeHelper.cs b/IOB-UT-NEXT/Iob/DateTimeHelper.cs index 477e3489..3c8964cc 100644 --- a/IOB-UT-NEXT/Iob/DateTimeHelper.cs +++ b/IOB-UT-NEXT/Iob/DateTimeHelper.cs @@ -8,7 +8,8 @@ using System.Threading.Tasks; namespace IOB_UT_NEXT.Iob { /// - /// Classe helper di tutti gli oggetti scadenze DateTime impiegate + /// Classe helper di tutti gli oggetti DateTime impiegati + /// ...da validare... /// public class DateTimeHelper { diff --git a/IOB-UT-NEXT/Iob/QueueHelper.cs b/IOB-UT-NEXT/Iob/QueueHelper.cs new file mode 100644 index 00000000..75d47ebc --- /dev/null +++ b/IOB-UT-NEXT/Iob/QueueHelper.cs @@ -0,0 +1,66 @@ +using IOB_UT_NEXT.Services.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IOB_UT_NEXT.Iob +{ + /// + /// Classe helper di tutti gli oggetti Queue impiegati + /// ...da validare... + /// + public class QueueHelper + { + #region Public Fields + + /// + /// Coda valori ALLARMI ove gestiti... + /// + public DataQueue QueueAlarm; + + /// + /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) + /// + public DataQueue QueueFLog; + + /// + /// Oggetto della coda degli elementi letti (e non ancora trasmessi) + /// + public DataQueue QueueIN; + + /// + /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... + /// + 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) + /// NB: sono salvati serializzati come stringhe + /// + public DataQueue QueueRawTransf; + + /// + /// Coda delle richieste dal server (Task2Exe) + /// + public DataQueue QueueSrvReq; + + /// + /// Coda delle risposte al server (Task2Exe) + /// + public DataQueue QueueSrvResp; + + /// + /// Coda valori LOG UTENTE (da non sottocampionare come samples)... + /// + public DataQueue QueueULog; + + #endregion Public Fields + } +} \ No newline at end of file diff --git a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs index 05aac825..656bd435 100644 --- a/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs +++ b/IOB-WIN-FILE/IobFile/IobFileSoitaab.cs @@ -454,7 +454,7 @@ namespace IOB_WIN_FILE.IobFile if (queueInEnabCurr) { // --> accodo (valore già formattato)! - QueueIN.Enqueue(currVal); + QHelp.QueueIN.Enqueue(currVal); // loggo! lgTrace(string.Format("[QUEUE-IN] {0}", currVal)); counterSigIN++; diff --git a/IOB-WIN-FORM/AdapterForm.cs b/IOB-WIN-FORM/AdapterForm.cs index ed23e1f1..a8a89858 100644 --- a/IOB-WIN-FORM/AdapterForm.cs +++ b/IOB-WIN-FORM/AdapterForm.cs @@ -785,7 +785,7 @@ namespace IOB_WIN_FORM if (sendStartFLog) { // segnalo reboot (programma)... - iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB Adapter Started")); + iobObj.QHelp.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB Adapter Started")); } } catch (Exception exc) @@ -1748,7 +1748,7 @@ namespace IOB_WIN_FORM } else { - lgTrace($"VETO DoExecMachineTasks.queueInEnabCurr | veto attivo | {adesso:yyyy.MM.dd HH:mm:ss}"); + lgTrace($"VETO DoExecMachineTasks.QueueINEnabCurr | veto attivo | {adesso:yyyy.MM.dd HH:mm:ss}"); iobObj.checkVetoQueueIn(); } } @@ -1809,7 +1809,7 @@ namespace IOB_WIN_FORM } else { - lgTrace($"VETO DoExecServerTasksAsync.queueInEnabCurr | veto attivo | {adesso:yyyy.MM.dd HH:mm:ss}"); + lgTrace($"VETO DoExecServerTasksAsync.QueueINEnabCurr | veto attivo | {adesso:yyyy.MM.dd HH:mm:ss}"); iobObj.checkVetoQueueIn(); } } @@ -2141,12 +2141,12 @@ namespace IOB_WIN_FORM private void refreshFormData() { // aggiorno visualizzazioni varie in form... - alQueueLen = iobObj.QueueAlarm.Count; - evQueueLen = iobObj.QueueIN.Count; - flQueueLen = iobObj.QueueFLog.Count; - msQueueLen = iobObj.QueueMessages.Count; - rtrQueueLen = iobObj.QueueRawTransf.Count; - ulQueueLen = iobObj.QueueULog.Count; + alQueueLen = iobObj.QHelp.QueueAlarm.Count; + evQueueLen = iobObj.QHelp.QueueIN.Count; + flQueueLen = iobObj.QHelp.QueueFLog.Count; + msQueueLen = iobObj.QHelp.QueueMessages.Count; + rtrQueueLen = iobObj.QHelp.QueueRawTransf.Count; + ulQueueLen = iobObj.QHelp.QueueULog.Count; // aggiorno labels counters... counterIob = $"pz MES {iobObj.contapezziIOB}"; counterMac = $"pz PLC {iobObj.contapezziPLC}"; diff --git a/IOB-WIN-FORM/Iob/Generic.cs b/IOB-WIN-FORM/Iob/Generic.cs index 4f5f2769..7eccfbdd 100644 --- a/IOB-WIN-FORM/Iob/Generic.cs +++ b/IOB-WIN-FORM/Iob/Generic.cs @@ -388,7 +388,7 @@ namespace IOB_WIN_FORM.Iob // mostro dati variati letti... displayOtherData(val); // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); + QHelp.QueueFLog.Enqueue(encodedVal); // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) // ho allarmi perdurati... @@ -426,18 +426,18 @@ namespace IOB_WIN_FORM.Iob // mostro dati variati letti... displayOtherData(val); // --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); + QHelp.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) + if (QHelp.QueueFLog.Count > maxQueueFLog) { // elimino valori iniziali fino a tornare al max ammesso... - while (QueueFLog.Count > maxQueueFLog) + while (QHelp.QueueFLog.Count > maxQueueFLog) { string currVal = ""; - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); lgInfo($"Eliminazione da coda FLog per superamento maxLengh: {currVal}"); } } @@ -511,18 +511,18 @@ namespace IOB_WIN_FORM.Iob string encodedVal = JsonSerialize(newVal); // --> accodo (valore già formattato)! - QueueRawTransf.Enqueue(encodedVal); + QHelp.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) + if (QHelp.QueueRawTransf.Count > maxQueueRawTransf) { // elimino valori iniziali fino a tornare al max ammesso... - while (QueueRawTransf.Count > maxQueueRawTransf) + while (QHelp.QueueRawTransf.Count > maxQueueRawTransf) { string currVal = ""; - QueueRawTransf.TryDequeue(out currVal); + QHelp.QueueRawTransf.TryDequeue(out currVal); lgInfo($"Eliminazione da coda RawTransf per superamento maxLengh: {currVal}"); } } @@ -557,7 +557,7 @@ namespace IOB_WIN_FORM.Iob if (queueInEnabCurr) { // --> accodo (valore già formattato)! - QueueIN.Enqueue(qEncodeIN); + QHelp.QueueIN.Enqueue(qEncodeIN); // loggo! lgDebug($"[QUEUE-IN] {qEncodeIN}"); } @@ -579,7 +579,7 @@ namespace IOB_WIN_FORM.Iob counterSigIN = 0; } } - lgDebug($"accodaSigIN 02 | nReadFilt: {nReadFilt} | counterSigIN: {counterSigIN} | QueueIN len: {QueueIN.Count}"); + lgDebug($"accodaSigIN 02 | nReadFilt: {nReadFilt} | counterSigIN: {counterSigIN} | QHelp.QueueIN len: {QHelp.QueueIN.Count}"); } /// @@ -592,7 +592,7 @@ namespace IOB_WIN_FORM.Iob // mostro dati variati letti... displayOtherData(val); // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! - QueueULog.Enqueue(encodedVal); + QHelp.QueueULog.Enqueue(encodedVal); // loggo! lgInfo(string.Format("[QUEUE-USER-LOG] {0}", encodedVal)); @@ -616,7 +616,7 @@ namespace IOB_WIN_FORM.Iob if (DemoOut) { - currentAnsw = (QueueIN.Count + QueueFLog.Count >= nMaxSend); + currentAnsw = (QHelp.QueueIN.Count + QHelp.QueueFLog.Count >= nMaxSend); } else { @@ -682,7 +682,7 @@ namespace IOB_WIN_FORM.Iob } /// - /// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile + /// Verifica veto coda QHelp.QueueIN ed aggiorna abilitazione su variabile /// public void checkVetoQueueIn() { @@ -978,14 +978,14 @@ namespace IOB_WIN_FORM.Iob public void ExecServerRequests() { // verifica se ci siano richieste da eseguire - if (QueueSrvReq.Count > 0) + if (QHelp.QueueSrvReq.Count > 0) { if (MPOnline) { if (IobOnline) { // prendo elenco - List listaValori = QueueSrvReq.ToList(); + List listaValori = QHelp.QueueSrvReq.ToList(); foreach (var rawJob in listaValori) { @@ -1003,7 +1003,7 @@ namespace IOB_WIN_FORM.Iob } // svuoto! - QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); + QHelp.QueueSrvReq = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); } } } @@ -3374,7 +3374,7 @@ namespace IOB_WIN_FORM.Iob queueInEnabCurr = false; string msgVeto = $"Impostato veto QUEUE-IN a {vetoQueueIn}s | fino alle {utils.dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"; lgInfoStartup(msgVeto); - lgDebug($"startAdapter completed | veto queueIn impostato per {vetoQueueIn}s fino alle {utils.dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"); + lgDebug($"startAdapter completed | veto QHelp.QueueIN impostato per {vetoQueueIn}s fino alle {utils.dtVetoQueueIN:yyyy.MM.dd HH:mm:ss}"); } /// @@ -3392,28 +3392,28 @@ namespace IOB_WIN_FORM.Iob { // svuoto le code dei valori letti e non ancora trasmessi... parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda segnali...", true); - while (QueueIN.Count > 0) + while (QHelp.QueueIN.Count > 0) { // INVIO COMUNQUE...!!! string valore = ""; - QueueIN.TryDequeue(out valore); + QHelp.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) + if (QHelp.QueueFLog.Count > minJsonData) { - while (QueueFLog.Count > 0) + while (QHelp.QueueFLog.Count > 0) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) + if (QHelp.QueueFLog.Count > maxJsonData) { string currVal = ""; // prendoi primi maxJsonDataValori for (int i = 0; i < maxJsonData; i++) { - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); listaValori.Add(currVal); } await sendDataBlock(urlType.FLog, listaValori); @@ -3421,12 +3421,12 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueFLog.ToList(); + listaValori = QHelp.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); + QHelp.QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueFLog", false, redisMan); + //QHelp.QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueFLog", IOBConfFull.General.EnabRedisQue, redisMan); } } // HO FINITO invio di FLog... @@ -3434,26 +3434,26 @@ namespace IOB_WIN_FORM.Iob else { string currVal = ""; - while (QueueFLog.Count > 0) + while (QHelp.QueueFLog.Count > 0) { // INVIO COMUNQUE...!!! - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); await sendToMoonPro(urlType.FLog, currVal); } } // svuoto coda ULog - while (QueueULog.Count > 0) + while (QHelp.QueueULog.Count > 0) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueULog.Count > maxJsonData) + if (QHelp.QueueULog.Count > maxJsonData) { string currVal = ""; // prendoi primi maxJsonDataValori for (int i = 0; i < maxJsonData; i++) { - QueueULog.TryDequeue(out currVal); + QHelp.QueueULog.TryDequeue(out currVal); listaValori.Add(currVal); } await sendDataBlock(urlType.ULog, listaValori); @@ -3461,11 +3461,11 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueULog.ToList(); + listaValori = QHelp.QueueULog.ToList(); // invio await sendDataBlock(urlType.ULog, listaValori); // svuoto! - QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueULog", false, redisMan); + QHelp.QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueULog", false, redisMan); } } } @@ -3497,12 +3497,12 @@ namespace IOB_WIN_FORM.Iob public async Task svuotaCodaSignInAsync() { // verifico SE la coda abbia dei valori... - if (QueueIN.Count > 0) + if (QHelp.QueueIN.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { - if (QueueIN.Count > 0) + if (QHelp.QueueIN.Count > 0) { string currVal = ""; // se online provo @@ -3511,16 +3511,16 @@ namespace IOB_WIN_FORM.Iob if (IobOnline) { // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueIN.Count > 1) + if (QHelp.QueueIN.Count > 1) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueIN.Count > maxJsonDataEv) + if (QHelp.QueueIN.Count > maxJsonDataEv) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonDataEv; j++) { - QueueIN.TryDequeue(out currVal); + QHelp.QueueIN.TryDequeue(out currVal); listaValori.Add(currVal); } await sendDataBlock(urlType.SignIN, listaValori); @@ -3528,17 +3528,17 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueIN.ToList(); + listaValori = QHelp.QueueIN.ToList(); // invio await sendDataBlock(urlType.SignIN, listaValori); // svuoto! - QueueIN = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueIN", IOBConfFull.General.EnabRedisQue, redisMan); + QHelp.QueueIN = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueIN", IOBConfFull.General.EnabRedisQue, redisMan); } } else { // INVIO SINGOLO...!!! - QueueIN.TryDequeue(out currVal); + QHelp.QueueIN.TryDequeue(out currVal); await sendToMoonPro(urlType.SignIN, currVal); } // salvo come last signal in il currVal ultimo... SE !="" @@ -4600,9 +4600,9 @@ namespace IOB_WIN_FORM.Iob JobTaskData jobTask = new JobTaskData(codTav, newReq); // accodo richiesta serializzata string serVal = JsonSerialize(jobTask); - QueueSrvReq.Enqueue(serVal); + QHelp.QueueSrvReq.Enqueue(serVal); // loggo! - lgDebug($"[QUEUE] | QueueSrvReq len: {QueueSrvReq.Count}"); + lgDebug($"[QUEUE] | QHelp.QueueSrvReq len: {QHelp.QueueSrvReq.Count}"); } /// @@ -4615,9 +4615,9 @@ namespace IOB_WIN_FORM.Iob JobTaskData jobTask = new JobTaskData(codTav, rawData); // accodo richiesta serializzata string serVal = JsonSerialize(jobTask); - QueueSrvResp.Enqueue(serVal); + QHelp.QueueSrvResp.Enqueue(serVal); // loggo! - lgDebug($"[QUEUE] | QueueSrvResp len: {QueueSrvResp.Count}"); + lgDebug($"[QUEUE] | QHelp.QueueSrvResp len: {QHelp.QueueSrvResp.Count}"); } /// @@ -9046,13 +9046,13 @@ namespace IOB_WIN_FORM.Iob private async Task ServerPutRespAsync() { // verifico SE la coda abbia dei valori... - if (QueueSrvResp.Count > 0) + if (QHelp.QueueSrvResp.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... - if (QueueSrvResp.Count > 0) + if (QHelp.QueueSrvResp.Count > 0) { string currVal = ""; if (MPOnline) @@ -9061,12 +9061,12 @@ namespace IOB_WIN_FORM.Iob { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueSrvResp.Count > maxJsonData) + if (QHelp.QueueSrvResp.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { - QueueSrvResp.TryDequeue(out currVal); + QHelp.QueueSrvResp.TryDequeue(out currVal); listaValori.Add(currVal); } await SendTaskResult(listaValori); @@ -9074,10 +9074,10 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueSrvResp.ToList(); + listaValori = QHelp.QueueSrvResp.ToList(); await SendTaskResult(listaValori); // svuoto! - QueueSrvResp = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); + QHelp.QueueSrvResp = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan); } } else @@ -9123,18 +9123,18 @@ namespace IOB_WIN_FORM.Iob { string codIob = IOBConfFull.General.FilenameIOB; bool useRedis = IOBConfFull.General.EnabRedisQue; - QueueIN.Dispose(); - QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan); - QueueSrvResp.Dispose(); - QueueSrvResp = new DataQueue(codIob, "QueueServResp", useRedis, redisMan); + QHelp.QueueIN.Dispose(); + QHelp.QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan); + QHelp.QueueSrvResp.Dispose(); + QHelp.QueueSrvResp = new DataQueue(codIob, "QueueServResp", useRedis, redisMan); // no coda redis - QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan); - QueueFLog = new DataQueue(codIob, "QueueFLog", false, redisMan); - QueueMessages = new DataQueue(codIob, "QueueMessages", false, redisMan); - QueuePing = new DataQueue(codIob, "QueuePing", false, redisMan); - QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan); - QueueSrvReq = new DataQueue(codIob, "QueueServReq", false, redisMan); - QueueULog = new DataQueue(codIob, "QueueULog", false, redisMan); + QHelp.QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan); + QHelp.QueueFLog = new DataQueue(codIob, "QueueFLog", false, redisMan); + QHelp.QueueMessages = new DataQueue(codIob, "QueueMessages", false, redisMan); + QHelp.QueuePing = new DataQueue(codIob, "QueuePing", false, redisMan); + QHelp.QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan); + QHelp.QueueSrvReq = new DataQueue(codIob, "QueueServReq", false, redisMan); + QHelp.QueueULog = new DataQueue(codIob, "QueueULog", false, redisMan); } // imposto contatori blink a zero... i_counters = new int[32]; @@ -9239,13 +9239,13 @@ namespace IOB_WIN_FORM.Iob lgInfo("Impostato sendOnMachineOff a true"); } // verifico SE la coda abbia dei valori... - if (QueueFLog.Count > 0) + if (QHelp.QueueFLog.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... - if (QueueFLog.Count > 0) + if (QHelp.QueueFLog.Count > 0) { string currVal = ""; if (MPOnline) @@ -9253,16 +9253,16 @@ namespace IOB_WIN_FORM.Iob if (IobOnline || sendOnMachineOff) { // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > 1) + if (QHelp.QueueFLog.Count > 1) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) + if (QHelp.QueueFLog.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); listaValori.Add(currVal); } await sendDataBlock(urlType.FLog, listaValori); @@ -9271,19 +9271,19 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueFLog.ToList(); + listaValori = QHelp.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); + QHelp.QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueFLog", false, redisMan); + //QHelp.QueueFLog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueFLog", IOBConfFull.General.EnabRedisQue, redisMan); DtHelp.lastWatchDog = DateTime.Now; } } else { // INVIO SINGOLO...!!! - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); await sendToMoonPro(urlType.FLog, currVal); DtHelp.lastWatchDog = DateTime.Now; } @@ -9313,13 +9313,13 @@ namespace IOB_WIN_FORM.Iob { bool fatto = false; // verifico SE la coda abbia dei valori... - if (QueueRawTransf.Count > 0) + if (QHelp.QueueRawTransf.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... - if (QueueRawTransf.Count > 0) + if (QHelp.QueueRawTransf.Count > 0) { string currVal = ""; if (MPOnline || force) @@ -9328,12 +9328,12 @@ namespace IOB_WIN_FORM.Iob { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueRawTransf.Count > maxJsonData) + if (QHelp.QueueRawTransf.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { - QueueRawTransf.TryDequeue(out currVal); + QHelp.QueueRawTransf.TryDequeue(out currVal); listaValori.Add(currVal); } fatto = await sendDataBlock(urlType.RawTransf, listaValori, force); @@ -9341,13 +9341,13 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueRawTransf.ToList(); + listaValori = QHelp.QueueRawTransf.ToList(); // invio fatto = await sendDataBlock(urlType.RawTransf, listaValori, force); if (fatto) { // svuoto se ha okReport! - QueueRawTransf = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueRawTransf", false, redisMan); + QHelp.QueueRawTransf = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueRawTransf", false, redisMan); } } } @@ -9376,13 +9376,13 @@ namespace IOB_WIN_FORM.Iob private async Task SvuotaCodaULogAsync() { // verifico SE la coda abbia dei valori... - if (QueueULog.Count > 0) + if (QHelp.QueueULog.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... - if (QueueULog.Count > 0) + if (QHelp.QueueULog.Count > 0) { string currVal = ""; if (MPOnline) @@ -9391,12 +9391,12 @@ namespace IOB_WIN_FORM.Iob { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueULog.Count > maxJsonData) + if (QHelp.QueueULog.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { - QueueULog.TryDequeue(out currVal); + QHelp.QueueULog.TryDequeue(out currVal); listaValori.Add(currVal); } await sendDataBlock(urlType.ULog, listaValori); @@ -9404,11 +9404,11 @@ namespace IOB_WIN_FORM.Iob else { // invio in blocco - listaValori = QueueULog.ToList(); + listaValori = QHelp.QueueULog.ToList(); // invio await sendDataBlock(urlType.ULog, listaValori); // svuoto! - QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueULog", false, redisMan); + QHelp.QueueULog = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueueULog", false, redisMan); } } else diff --git a/IOB-WIN-FORM/Iob/PingWatchDog.cs b/IOB-WIN-FORM/Iob/PingWatchDog.cs index b034d944..5b16a6a4 100644 --- a/IOB-WIN-FORM/Iob/PingWatchDog.cs +++ b/IOB-WIN-FORM/Iob/PingWatchDog.cs @@ -43,7 +43,7 @@ namespace IOB_WIN_FORM.Iob bInOn = IOBConfFull.Special.PingConf.B_PowerOn; vetoCheckSec = IOBConfFull.Special.PingConf.VetoCheckSec; // fix coda ping - QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan); + QHelp.QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueuePing", false, redisMan); lgDebug($"L'adapter effettuera' PING di controllo all'indirizzo {IOBConfFull.Device.Connect.IpAddr} per forzare stato poweroff dopo {PoweroffTimeoutSec} sec"); } @@ -96,7 +96,7 @@ namespace IOB_WIN_FORM.Iob sw.Start(); byte[] MemBlock = new byte[2]; // faccio comunque ping se non ne ho collezionati a sufficienza... - if (QueuePing.Count < maxQueuePing) + if (QHelp.QueuePing.Count < maxQueuePing) { // in primis salvo data ping comunque... DtHelp.lastPING = DateTime.Now; @@ -182,7 +182,7 @@ namespace IOB_WIN_FORM.Iob } lgDebug("PING: tryConnect step 04"); - lgDebug("PING: Reset QueuePing"); + lgDebug("PING: Reset QHelp.QueuePing"); Stopwatch sw = new Stopwatch(); sw.Start(); @@ -245,14 +245,14 @@ namespace IOB_WIN_FORM.Iob if (B_input == 0 && pingOk) { B_input = bInOn; - QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan); - lgTrace($"QueuePing resetted on addTest"); + QHelp.QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueuePing", false, redisMan); + lgTrace($"QHelp.QueuePing resetted on addTest"); } - QueuePing.Enqueue($"{score}"); - while (QueuePing.Count > maxQueuePing) + QHelp.QueuePing.Enqueue($"{score}"); + while (QHelp.QueuePing.Count > maxQueuePing) { string res = ""; - QueuePing.TryDequeue(out res); + QHelp.QueuePing.TryDequeue(out res); } } @@ -265,10 +265,10 @@ namespace IOB_WIN_FORM.Iob protected bool pingStatusOk() { bool answ = true; - long numVal = QueuePing.Count; + long numVal = QHelp.QueuePing.Count; if (numVal > maxQueuePing / 2) { - var listaValori = QueuePing.ToList(); + var listaValori = QHelp.QueuePing.ToList(); long numOk = listaValori.Where(x => x == "1").Count(); long numKo = numVal - numOk; answ = numOk >= numKo; diff --git a/IOB-WIN-FORM/Iob/Simula.cs b/IOB-WIN-FORM/Iob/Simula.cs index 3e5a75a7..5a7f0fc9 100644 --- a/IOB-WIN-FORM/Iob/Simula.cs +++ b/IOB-WIN-FORM/Iob/Simula.cs @@ -1933,7 +1933,7 @@ namespace IOB_WIN_FORM.Iob if (queueInEnabCurr) { // --> accodo (valore già formattato)! - QueueIN.Enqueue(currVal); + QHelp.QueueIN.Enqueue(currVal); // loggo! lgDebug(string.Format("[QUEUE-IN] {0}", currVal)); counterSigIN++; diff --git a/IOB-WIN-FTP/Iob/Ftp.cs b/IOB-WIN-FTP/Iob/Ftp.cs index 58c8bc73..f1e8ea27 100644 --- a/IOB-WIN-FTP/Iob/Ftp.cs +++ b/IOB-WIN-FTP/Iob/Ftp.cs @@ -461,8 +461,6 @@ namespace IOB_WIN_FTP.Iob lgDebug($"FTP: tryConnect step 01 | connectionOk: {connectionOk}"); if (!connectionOk) { - //// resetto coda... - //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { @@ -646,14 +644,14 @@ namespace IOB_WIN_FTP.Iob if (B_input == 0 && pingOk) { B_input = 1; - QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan); - lgTrace($"QueuePing resetted on addTest"); + QHelp.QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueuePing", false, redisMan); + lgTrace($"QHelp.QueuePing resetted on addTest"); } - QueuePing.Enqueue($"{score}"); - while (QueuePing.Count > maxQueuePing) + QHelp.QueuePing.Enqueue($"{score}"); + while (QHelp.QueuePing.Count > maxQueuePing) { string res = ""; - QueuePing.TryDequeue(out res); + QHelp.QueuePing.TryDequeue(out res); } } @@ -943,10 +941,10 @@ namespace IOB_WIN_FTP.Iob private bool pingStatusOk() { bool answ = false; - long numVal = QueuePing.Count; + long numVal = QHelp.QueuePing.Count; if (numVal > maxQueuePing / 2) { - var listaValori = QueuePing.ToList(); + var listaValori = QHelp.QueuePing.ToList(); long numOk = listaValori.Where(x => x == "1").Count(); long numKo = numVal - numOk; answ = numOk >= numKo; diff --git a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs index 144dbf0c..053bc129 100644 --- a/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs +++ b/IOB-WIN-MBUS/IobModbustTCP/ModbusTCP.cs @@ -538,7 +538,6 @@ namespace IOB_WIN_MBUS.IobModbusTCP // refresh stato allarmi!!! if (connectionOk) { - //queueInEnabCurr = true; lgInfo($"Connessione OK: {connectionOk}"); if (adpRunning) { diff --git a/IOB-WIN-MTC/Iob/MTConn.cs b/IOB-WIN-MTC/Iob/MTConn.cs index e367ae95..09a75e8d 100644 --- a/IOB-WIN-MTC/Iob/MTConn.cs +++ b/IOB-WIN-MTC/Iob/MTConn.cs @@ -270,7 +270,6 @@ namespace IOB_WIN_MTC.Iob if (connectionOk) { checkVetoQueueIn(); - //queueInEnabCurr = true; if (adpRunning) { lgInfo("Connessione OK"); diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs index 569fea3d..1a501c0d 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen1.cs @@ -185,8 +185,6 @@ namespace IOB_WIN_SHELLY.Iob lgDebug($"Shelly: tryConnect step 01 | connectionOk: {connectionOk}"); if (!connectionOk) { - //// resetto coda... - //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { @@ -266,14 +264,14 @@ namespace IOB_WIN_SHELLY.Iob if (B_input == 0 && pingOk) { B_input = 1; - QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan); - lgTrace($"QueuePing resetted on addTest"); + QHelp.QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueuePing", false, redisMan); + lgTrace($"QHelp.QueuePing resetted on addTest"); } - QueuePing.Enqueue($"{score}"); - while (QueuePing.Count > maxQueuePing) + QHelp.QueuePing.Enqueue($"{score}"); + while (QHelp.QueuePing.Count > maxQueuePing) { string res = ""; - QueuePing.TryDequeue(out res); + QHelp.QueuePing.TryDequeue(out res); } } @@ -286,10 +284,10 @@ namespace IOB_WIN_SHELLY.Iob private bool pingStatusOk() { bool answ = false; - long numVal = QueuePing.Count; + long numVal = QHelp.QueuePing.Count; if (numVal > maxQueuePing / 2) { - var listaValori = QueuePing.ToList(); + var listaValori = QHelp.QueuePing.ToList(); long numOk = listaValori.Where(x => x == "1").Count(); long numKo = numVal - numOk; answ = numOk >= numKo; diff --git a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs index 3beef34d..ac494de6 100644 --- a/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs +++ b/IOB-WIN-SHELLY/Iob/ShellyClientGen2.cs @@ -260,8 +260,6 @@ namespace IOB_WIN_SHELLY.Iob lgDebug($"Shelly: tryConnect step 01 | connectionOk: {connectionOk}"); if (!connectionOk) { - //// resetto coda... - //QueuePing = new DataQueue("000", "QueuePing", false); // controllo che il ping sia stato tentato almeno pingTestSec fa... if (DateTime.Now.Subtract(DtHelp.lastPING).TotalSeconds > vetoPingSec) { @@ -346,14 +344,14 @@ namespace IOB_WIN_SHELLY.Iob if (B_input == 0 && pingOk) { B_input = 1; - QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan); - lgTrace($"QueuePing resetted on addTest"); + QHelp.QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QHelp.QueuePing", false, redisMan); + lgTrace($"QHelp.QueuePing resetted on addTest"); } - QueuePing.Enqueue($"{score}"); - while (QueuePing.Count > maxQueuePing) + QHelp.QueuePing.Enqueue($"{score}"); + while (QHelp.QueuePing.Count > maxQueuePing) { string res = ""; - QueuePing.TryDequeue(out res); + QHelp.QueuePing.TryDequeue(out res); } } @@ -366,10 +364,10 @@ namespace IOB_WIN_SHELLY.Iob private bool pingStatusOk() { bool answ = false; - long numVal = QueuePing.Count; + long numVal = QHelp.QueuePing.Count; if (numVal > maxQueuePing / 2) { - var listaValori = QueuePing.ToList(); + var listaValori = QHelp.QueuePing.ToList(); long numOk = listaValori.Where(x => x == "1").Count(); long numKo = numVal - numOk; answ = numOk >= numKo; diff --git a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs index 34a76840..45a93973 100644 --- a/IOB-WIN-SQL/Iob/IobFileSoitaab.cs +++ b/IOB-WIN-SQL/Iob/IobFileSoitaab.cs @@ -454,7 +454,7 @@ namespace IOB_WIN_SQL.IobFile if (queueInEnabCurr) { // --> accodo (valore già formattato)! - QueueIN.Enqueue(currVal); + QHelp.QueueIN.Enqueue(currVal); // loggo! lgTrace(string.Format("[QUEUE-IN] {0}", currVal)); counterSigIN++; diff --git a/IOB-WIN-SQL/IobSql/SqlServPama.cs b/IOB-WIN-SQL/IobSql/SqlServPama.cs index bcada3ba..abd61375 100644 --- a/IOB-WIN-SQL/IobSql/SqlServPama.cs +++ b/IOB-WIN-SQL/IobSql/SqlServPama.cs @@ -682,7 +682,7 @@ namespace IOB_WIN_SQL.IobSql if (queueInEnabCurr) { // --> accodo (valore già formattato)! - QueueIN.Enqueue(currVal); + QHelp.QueueIN.Enqueue(currVal); // loggo! lgTrace(string.Format("[QUEUE-IN] {0}", currVal)); counterSigIN++; diff --git a/IOB-WIN/IobGeneric.cs b/IOB-WIN/IobGeneric.cs index 821f618c..62052c1b 100644 --- a/IOB-WIN/IobGeneric.cs +++ b/IOB-WIN/IobGeneric.cs @@ -282,22 +282,22 @@ namespace IOB_WIN /// /// Coda valori ALLARMI ove gestiti... /// - public ConcurrentQueue QueueAlarm = new ConcurrentQueue(); + public ConcurrentQueue QHelp.QueueAlarm = new ConcurrentQueue(); /// /// Oggetto della coda degli elementi letti di tipo FluxLog (e non ancora trasmessi) /// - public ConcurrentQueue QueueFLog = new ConcurrentQueue(); + public ConcurrentQueue QHelp.QueueFLog = new ConcurrentQueue(); /// /// Oggetto della coda degli elementi letti (e non ancora trasmessi) /// - public ConcurrentQueue QueueIN = new ConcurrentQueue(); + public ConcurrentQueue QHelp.QueueIN = new ConcurrentQueue(); /// /// Coda valori MESSAGGI/EVENTI (da non sottocampionare come samples)... /// - public ConcurrentQueue QueueMessages = new ConcurrentQueue(); + public ConcurrentQueue QHelp.QueueMessages = new ConcurrentQueue(); /// /// alias booleano false = R @@ -412,7 +412,7 @@ namespace IOB_WIN { if (DemoOut) { - answ = (QueueIN.Count + QueueFLog.Count >= nMaxSend); + answ = (QHelp.QueueIN.Count + QHelp.QueueFLog.Count >= nMaxSend); } else { @@ -1769,10 +1769,10 @@ namespace IOB_WIN // svuoto code se richiesto if (resetQueue) { - QueueIN = new ConcurrentQueue(); - QueueFLog = new ConcurrentQueue(); - QueueAlarm = new ConcurrentQueue(); - QueueMessages = new ConcurrentQueue(); + QHelp.QueueIN = new ConcurrentQueue(); + QHelp.QueueFLog = new ConcurrentQueue(); + QHelp.QueueAlarm = new ConcurrentQueue(); + QHelp.QueueMessages = new ConcurrentQueue(); } // imposto contatori blink a zero... i_counters = new int[32]; @@ -1831,13 +1831,13 @@ namespace IOB_WIN DtHelp.lastWatchDog = DateTime.Now; } // verifico SE la coda abbia dei valori... - if (QueueFLog.Count > 0) + if (QHelp.QueueFLog.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { // SE ho qualcosa in coda... - if (QueueFLog.Count > 0) + if (QHelp.QueueFLog.Count > 0) { string currVal = ""; if (MPOnline) @@ -1845,16 +1845,16 @@ namespace IOB_WIN if (IobOnline) { // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > 1) + if (QHelp.QueueFLog.Count > 1) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) + if (QHelp.QueueFLog.Count > maxJsonData) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonData; j++) { - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); listaValori.Add(currVal); } sendDataBlock(urlType.FLog, listaValori); @@ -1862,17 +1862,17 @@ namespace IOB_WIN else { // invio in blocco - listaValori = QueueFLog.ToList(); + listaValori = QHelp.QueueFLog.ToList(); // invio sendDataBlock(urlType.FLog, listaValori); // svuoto! - QueueFLog = new ConcurrentQueue(); + QHelp.QueueFLog = new ConcurrentQueue(); } } else { // INVIO SINGOLO...!!! - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); sendToMoonPro(urlType.FLog, currVal); } } @@ -2807,7 +2807,7 @@ namespace IOB_WIN // mostro dati variati letti... displayOtherData(val); // accodo IN PRIMIS al FluxLog --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); + QHelp.QueueFLog.Enqueue(encodedVal); // accodo ANCHE alla coda allarmi che trasmetterò SOLO SE a scadenza impostata (10 sec?) ho allarmi perdurati... // loggo! @@ -2829,18 +2829,18 @@ namespace IOB_WIN // mostro dati variati letti... displayOtherData(val); // --> accodo (valore già formattato)! - QueueFLog.Enqueue(encodedVal); + QHelp.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) + if (QHelp.QueueFLog.Count > maxQueueFLog) { // elimino valori iniziali fino a tornare al max ammesso... - while (QueueFLog.Count > maxQueueFLog) + while (QHelp.QueueFLog.Count > maxQueueFLog) { string currVal = ""; - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); lgInfo($"Eliminazione ca coda FLog per superamento maxLengh: {currVal}"); } } @@ -2875,7 +2875,7 @@ namespace IOB_WIN // mostro dati variati letti... displayInData(ref currDispData); // --> accodo (valore già formattato)! - QueueIN.Enqueue(qEncodeIN); + QHelp.QueueIN.Enqueue(qEncodeIN); // loggo! lgInfo(string.Format("[QUEUE-IN] {0}", qEncodeIN)); // aggiorno counters ed eventuale reset @@ -4449,28 +4449,28 @@ namespace IOB_WIN { // svuoto le code dei valori letti e non ancora trasmessi... parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda segnali..."); - while (QueueIN.Count > 0) + while (QHelp.QueueIN.Count > 0) { // INVIO COMUNQUE...!!! string valore = ""; - QueueIN.TryDequeue(out valore); + QHelp.QueueIN.TryDequeue(out valore); sendToMoonPro(urlType.SignIN, valore); } parentForm.displayTaskAndLog("[CLOSING] Svuotamento FORZATO coda FluxLOG..."); // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueFLog.Count > minJsonData) + if (QHelp.QueueFLog.Count > minJsonData) { - while (QueueFLog.Count > 0) + while (QHelp.QueueFLog.Count > 0) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueFLog.Count > maxJsonData) + if (QHelp.QueueFLog.Count > maxJsonData) { string currVal = ""; // prendoi primi maxJsonDataValori for (int i = 0; i < maxJsonData; i++) { - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); listaValori.Add(currVal); } sendDataBlock(urlType.FLog, listaValori); @@ -4478,11 +4478,11 @@ namespace IOB_WIN else { // invio in blocco - listaValori = QueueFLog.ToList(); + listaValori = QHelp.QueueFLog.ToList(); // invio sendDataBlock(urlType.FLog, listaValori); // svuoto! - QueueFLog = new ConcurrentQueue(); + QHelp.QueueFLog = new ConcurrentQueue(); } } // HO FINITO invio di FLog... @@ -4490,10 +4490,10 @@ namespace IOB_WIN else { string currVal = ""; - while (QueueFLog.Count > 0) + while (QHelp.QueueFLog.Count > 0) { // INVIO COMUNQUE...!!! - QueueFLog.TryDequeue(out currVal); + QHelp.QueueFLog.TryDequeue(out currVal); sendToMoonPro(urlType.FLog, currVal); } } @@ -4519,12 +4519,12 @@ namespace IOB_WIN public void svuotaCodaSignIN() { // verifico SE la coda abbia dei valori... - if (QueueIN.Count > 0) + if (QHelp.QueueIN.Count > 0) { // invio pacchetto di dati (max da conf) for (int i = 0; i < nMaxSend; i++) { - if (QueueIN.Count > 0) + if (QHelp.QueueIN.Count > 0) { string currVal = ""; // se online provo @@ -4533,16 +4533,16 @@ namespace IOB_WIN if (IobOnline) { // se ho + di 2 elementi in coda --> uso invio JSON in blocco... - if (QueueIN.Count > 1) + if (QHelp.QueueIN.Count > 1) { List listaValori = new List(); // se ho + di maxJsonData elementi --> invio un set di dati alla volta - if (QueueIN.Count > maxJsonDataEv) + if (QHelp.QueueIN.Count > maxJsonDataEv) { // prendoi primi maxJsonDataValori for (int j = 0; j < maxJsonDataEv; j++) { - QueueIN.TryDequeue(out currVal); + QHelp.QueueIN.TryDequeue(out currVal); listaValori.Add(currVal); } sendDataBlock(urlType.SignIN, listaValori); @@ -4550,17 +4550,17 @@ namespace IOB_WIN else { // invio in blocco - listaValori = QueueIN.ToList(); + listaValori = QHelp.QueueIN.ToList(); // invio sendDataBlock(urlType.SignIN, listaValori); // svuoto! - QueueIN = new ConcurrentQueue(); + QHelp.QueueIN = new ConcurrentQueue(); } } else { // INVIO SINGOLO...!!! - QueueIN.TryDequeue(out currVal); + QHelp.QueueIN.TryDequeue(out currVal); sendToMoonPro(urlType.SignIN, currVal); } // salvo come last signal in il currVal ultimo... SE !=""