Merge branch 'release/TestFluitek_01'

This commit is contained in:
Samuele Locatelli
2026-02-18 16:32:37 +01:00
20 changed files with 2640 additions and 1101 deletions
+6 -1
View File
@@ -80,7 +80,7 @@ namespace IOB_UT_NEXT.Config.Base
/// <summary>
/// Soglia massima errori prima della disconnessione automatica in check
/// </summary>
public int MaxErroriCheck { get; set; } = 100;
public int MaxErroriCheck { get; set; } = 50;
/// <summary>
/// Max tentativi ping permessi (default: 5)
@@ -107,6 +107,11 @@ namespace IOB_UT_NEXT.Config.Base
/// </summary>
public bool EnabRedisQue { get; set; } = true;
/// <summary>
/// Forza esecuzione WorkLoopMachine (verso macchina PLC/CNC) in SingleThread (vs ThreadPool)
/// </summary>
public bool MachWLoopSingleThread { get; set; } = true;
/// <summary>
/// Versione software IOB
/// </summary>
+4 -2
View File
@@ -125,6 +125,7 @@ namespace IOB_UT_NEXT.Config
Customer = fIni.ReadString("TAGS", "Customer", "EgalWare"),
EnabelPodlManFull = bool.Parse(fIni.ReadString("OPTPAR", "EnabelPodlManFull", "false")),
EnabRedisQue = bool.Parse(fIni.ReadString("IOB", "EnableRedisQueue", "true")),
MachWLoopSingleThread = bool.Parse(fIni.ReadString("OPTPAR", "MachSingleThread", "false")),
MaxErroriCheck = fIni.ReadInteger("OPTPAR", "MAX_ERR_CHECK", maxErrCheck),
MaxPingRetry = fIni.ReadInteger("OPTPAR", "MAX_PING_RETRY", 5),
MinDeltaSec = fIni.ReadInteger("IOB", "MinDeltaSec", 6),
@@ -140,11 +141,11 @@ namespace IOB_UT_NEXT.Config
exclOptPar.Add("DELAY_READ_PZ_COUNT");
exclOptPar.Add("DISABLE_SEND_WDST");
exclOptPar.Add("EnabelPodlManFull");
exclOptPar.Add("MachSingleThread");
exclOptPar.Add("MAX_ERR_CHECK");
exclOptPar.Add("MAX_PING_RETRY");
exclOptPar.Add("SEND_ALARM_RESET");
exclOptPar.Add("SEND_MACHINE_CONF");
exclOptPar.Add("timerIntMs");
exclOptPar.Add("WAIT_REC_MSEC");
exclOptPar.Add("WATCHDOG_PERIOD");
@@ -155,6 +156,7 @@ namespace IOB_UT_NEXT.Config
{
int.TryParse(rawTimer, out uiTimer);
}
exclOptPar.Add("timerIntMs");
// ultimo controllo: timer deve essere compreso tra 10 a 500 x evitare task troppo veloci o lenti...
uiTimer = uiTimer < 10 ? 10 : uiTimer;
uiTimer = uiTimer > 500 ? 500 : uiTimer;
@@ -272,7 +274,7 @@ namespace IOB_UT_NEXT.Config
PzTotMode = fIni.ReadString("OPTPAR", "PZGTOT_MODE", ""),
ReadErrorMax = fIni.ReadInteger("OPTPAR", "READ_ERROR_MAX", 20),
ReadErrorSleepTime = fIni.ReadInteger("OPTPAR", "READ_ERROR_SLEEP_TIME", 20000),
StartupVetoQueueIN = fIni.ReadInteger("OPTPAR", "VETO_QUEUE_IN", 10),
StartupVetoQueueIN = fIni.ReadInteger("OPTPAR", "VETO_QUEUE_IN", 6),
Vendor = fIni.ReadString("MACHINE", "VENDOR", "STEAMWARE"),
Connect = new ConnectionDto()
{
+3
View File
@@ -200,8 +200,11 @@
<Compile Include="iobRefreshedEventArgs.cs" />
<Compile Include="IobWinStatus.cs" />
<Compile Include="Iob\BaseObj.cs" />
<Compile Include="JobTask2Exe.cs" />
<Compile Include="MeasureUtils.cs" />
<Compile Include="plcMemMapExt.cs" />
<Compile Include="TimerMan.cs" />
<Compile Include="SingleThreadTaskScheduler.cs" />
<Compile Include="TCMan.cs" />
<Compile Include="TimeUtils.cs" />
<Compile Include="ToMapo.cs" />
+15 -3
View File
@@ -238,6 +238,16 @@ namespace IOB_UT_NEXT.Iob
/// </summary>
public DataQueue QueueRawTransf;// = new DataQueue("000", "QueueRawTransf", false);
/// <summary>
/// Coda delle richieste dal server (Task2Exe)
/// </summary>
public DataQueue QueueSrvReq;
/// <summary>
/// Coda delle risposte al server (Task2Exe)
/// </summary>
public DataQueue QueueSrvResp;
/// <summary>
/// Coda valori LOG UTENTE (da non sottocampionare come samples)...
/// </summary>
@@ -621,16 +631,18 @@ namespace IOB_UT_NEXT.Iob
{
string codIob = IOBConfFull.General.FilenameIOB;
bool useRedis = IOBConfFull.General.EnabRedisQue;
QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan);
// valutare se portare di nuovo in redis...
QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan);
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);
QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan);
QueueULog = new DataQueue(codIob, "QueueULog", 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);
}
#endregion Protected Methods
+65
View File
@@ -0,0 +1,65 @@
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
{
/// <summary>
/// Classe per gestione Job Task2Exe x una macchina
/// </summary>
public class JobTaskData
{
#region Public Constructors
public JobTaskData(string codTav, string rawData)
{
CodTav = codTav;
RawData = rawData;
}
#endregion Public Constructors
#if false
public JobTaskData(string codTav, Dictionary<string, string> newDict)
{
CodTav = codTav;
RawData = JsonConvert.SerializeObject(newDict);
}
#endif
#region Public Properties
/// <summary>
/// Codice tavola (empty = main)
/// </summary>
public string CodTav { get; private set; } = "";
/// <summary>
/// Dizionario in formato raw (da deserializzare
/// </summary>
public string RawData { get; private set; } = "";
#endregion Public Properties
#region Public Methods
/// <summary>
/// Dizionario dei Task associati
/// </summary>
public static Dictionary<string, string> TaskDict(string rawData)
{
var answ = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(rawData))
{
answ = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawData) ?? new Dictionary<string, string>();
}
return answ;
}
#endregion Public Methods
}
}
+47
View File
@@ -0,0 +1,47 @@
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<Task> _tasks = new BlockingCollection<Task>();
private readonly Thread _thread;
public SynchronizationContext SyncContext { get; private set; }
public SingleThreadTaskScheduler(string threadName)
{
var tcs = new TaskCompletionSource<bool>();
_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<Task> GetScheduledTasks() => _tasks;
public override int MaximumConcurrencyLevel => 1;
public void Dispose() => _tasks.CompleteAdding();
}
+64
View File
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IOB_UT_NEXT
{
/// <summary>
/// Classe di gestione info Timer per esecuzione ordinata processi secondo priorità
/// </summary>
public class TimerMan
{
#region Public Constructors
/// <summary>
/// Init classe timer
/// </summary>
/// <param name="contesto"></param>
public TimerMan(ContextType contesto)
{
Contesto = contesto;
}
#endregion Public Constructors
#region Public Enums
/// <summary>
/// Tipologia contesto timer
/// </summary>
public enum ContextType
{
Machine = 0,
Server
}
#endregion Public Enums
#region Public Properties
/// <summary>
/// Contesto del timer
/// </summary>
public ContextType Contesto { get; private set; } = ContextType.Machine;
/// <summary>
/// Contatore chiamate timers x debug timing
/// </summary>
public Dictionary<gatherCycle, int> CycleCount { get; set; } = new Dictionary<gatherCycle, int>();
/// <summary>
/// Contatore durata exec x debug timing
/// </summary>
public Dictionary<gatherCycle, double> CycleElaps { get; set; } = new Dictionary<gatherCycle, double>();
/// <summary>
/// Dizionario scadenza timers interni x esecuzione dei vari task a differente frequenza di chiamata
/// </summary>
public Dictionary<gatherCycle, DateTime> Veto { get; set; } = new Dictionary<gatherCycle, DateTime>();
#endregion Public Properties
}
}
+1 -45
View File
@@ -351,36 +351,6 @@ namespace IOB_UT_NEXT
}
}
#if false
/// <summary>
/// Versione async della chiamata ad URL
/// </summary>
/// <param name="URL"></param>
/// <returns></returns>
public static string callUrlAsync(string URL)
{
// Chiamo in modalità task...
var resp = Task.Run(() => callUrl(URL));
resp.Wait();
return resp.Result;
}
/// <summary>
/// Versione async della chiamata ad URL
/// </summary>
/// <param name="URL"></param>
/// <param name="payload"></param>
/// <returns></returns>
public static string callUrlAsync(string URL, string payload)
{
// Chiamo in modalità task...
var resp = Task.Run(() => callUrl(URL, payload));
resp.Wait();
return resp.Result;
}
#endif
/// <summary>
/// Effettua chiamata URL IMMEDIATAMENTE e restituisce risultato
/// </summary>
@@ -435,7 +405,7 @@ namespace IOB_UT_NEXT
clientPayload = new WebClientWT();
}
clientPayload.Headers.Add("user-agent", $"{CRS("appName")}-PAYLOAD");
clientPayload.Headers.Add("Content-Type", "application/json");
clientPayload.Headers.Add("Content-ContextType", "application/json");
try
{
// problema certificati locali...
@@ -553,20 +523,6 @@ namespace IOB_UT_NEXT
public static string CRS(string key)
{
return ConfigurationManager.AppSettings[key] ?? string.Empty;
#if false
string answ = "";
if (ConfigurationManager.AppSettings.Count > 0)
{
try
{
answ = ConfigurationManager.AppSettings[key].ToString();
}
catch
{ }
}
return answ;
#endif
}
/// <summary>
+2 -2
View File
@@ -25,7 +25,7 @@
<add key="urlCallTOut" value="9999" />
<add key="urlRandWait" value="200" />
<!-- ciclo corto x invio a server: ms -->
<add key="timerIntMs" value="10" />
<add key="timerIntMs" value="50" />
<add key="fastCount" value="20" />
<add key="normCount" value="200" />
<add key="slowCount" value="300" />
@@ -73,7 +73,7 @@
<add key="windowCanMax" value="true" />
<add key="trayClose" value="true" />
<add key="autoSaveSec" value="60" />
<add key="waitRecMSec" value="60000" />
<add key="waitRecMSec" value="20000" />
<add key="testCharSep" value="|" />
<add key="delayShowLogMs" value="1000" />
<add key="vetoSeconds" value="5" />
+6
View File
@@ -86,6 +86,12 @@ DISABLE_SEND_WDST=TRUE
; bit da scrivere come trace ad ogni check
;MEM_2_TRACE=|BIT3|BIT4|BIT5|
PARAM_CONF=3028.json
; test timing & errori
timerIntMs=30
MachSingleThread=true
MAX_ERR_CHECK=10
;WAIT_REC_MSEC=15000
[BRANCH]
NAME=master
+4
View File
@@ -88,6 +88,10 @@ ENABLE_SEND_PZC_BLOCK=TRUE
MIN_SEND_PZC_BLOCK=0
MAX_SEND_PZC_BLOCK=100
DISABLE_SEND_WDST=TRUE
; test timing & errori
;timerIntMs=40
;MachSingleThread=true
MAX_ERR_CHECK=10
; bit da scrivere come trace ad ogni check
;MEM_2_TRACE=|BIT3|BIT4|BIT5|
PARAM_CONF=3029.json
+1
View File
@@ -71,6 +71,7 @@ MIN_SEND_PZC_BLOCK=0
MAX_SEND_PZC_BLOCK=100
; Disabilito conteggio Pezzi come richiesto
DISABLE_PZCOUNT=TRUE
DISABLE_SEND_WDST=TRUE
[BRANCH]
NAME=master
+3 -1
View File
@@ -26,10 +26,12 @@ CLI_INST=SteamWareSim
;STARTLIST=3004
;STARTLIST=3002
;STARTLIST=GT575
;STARTLIST=3029
;STARTLIST=3004,3005
;STARTLIST=SIMUL_01
;STARTLIST=FOV106
STARTLIST=FOV107
;STARTLIST=3028
;STARTLIST=3029
MAXCNC=10
+5 -1
View File
@@ -162,6 +162,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="DATA\CONF\3001.ini" />
<None Include="DATA\CONF\3001.json" />
<None Include="DATA\CONF\3002.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -177,6 +178,7 @@
<None Include="DATA\CONF\3021.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="DATA\CONF\3021.json" />
<None Include="DATA\CONF\3025.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -231,7 +233,9 @@
<None Include="DATA\CONF\FOV100.ini" />
<None Include="DATA\CONF\FOV101.ini" />
<None Include="DATA\CONF\FOV102.ini" />
<None Include="DATA\CONF\FOV106.ini" />
<None Include="DATA\CONF\FOV106.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="DATA\CONF\FOV107.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
+591 -421
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -48,6 +48,7 @@ REM ROBOCOPY %2 \\192.168.1.133\Steamware\IOB-WIN-FANUC-DEB /MIR
REM ROBOCOPY %2 \\10.51.90.10\Steamware\IOB-WIN-FANUC-DEB /MIR
REM ROBOCOPY %2 \\10.51.90.14\Steamware\IOB-WIN-FANUC-DEB /MIR
REM ROBOCOPY %2 \\10.51.90.13\Steamware\IOB-WIN-FANUC-DEB /MIR
ROBOCOPY %2 \\10.51.90.15\Steamware\IOB-WIN-FANUC-DEB /MIR
REM ROBOCOPY %2 \\10.51.90.9\Steamware\IOB-WIN-FANUC-DEB /MIR
File diff suppressed because it is too large Load Diff
+430 -340
View File
@@ -32,6 +32,11 @@ namespace IOB_WIN_FORM.Iob
{
#region Protected Fields
/// <summary>
/// Variabile numero errori vari (in lettura) --&gt; se supera soglia maxErroriCheck --&gt; disconnette
/// </summary>
protected static int numErroriCheck = 0;
protected bool _connOk = false;
/// <summary>
@@ -59,13 +64,6 @@ namespace IOB_WIN_FORM.Iob
/// </summary>
protected int B_output;
#if false
/// <summary>
/// Ultimo valore B_output inviato
/// </summary>
protected int B_output_sent = -1;
#endif
/// <summary>
/// Vettore 32 BIT valori precedenti
/// </summary>
@@ -308,11 +306,6 @@ namespace IOB_WIN_FORM.Iob
/// </summary>
protected bool numArtCharTrim = false;
/// <summary>
/// Variabile numero errori vari (in lettura) --&gt; se supera soglia maxErroriCheck --&gt; disconnette
/// </summary>
protected int numErroriCheck = 0;
/// <summary>
/// Timeout x ping al server
/// </summary>
@@ -678,16 +671,6 @@ namespace IOB_WIN_FORM.Iob
}
}
protected bool queueInEnabCurr
{
get => qInEnabCurr;
set
{
qInEnabCurr = value;
lgInfo($"SET queueInEnabCurr: {value} | {DateTime.Now:HHmmss}");
}
}
/// <summary>
/// Definizioni x replace in file ricette
/// </summary>
@@ -1026,13 +1009,6 @@ namespace IOB_WIN_FORM.Iob
{
get => $@"{urlCommandIob("remTask2Exe")}?taskName=";
}
/// <summary>
/// URL per richiamo task da eseguire...
/// </summary>
protected string urlRemTask2ExeTav(string codTav)
{
return $@"{urlCommandIob("remTask2Exe")}|{codTav}?taskName=";
}
/// <summary>
/// URL per salvataggio dati PARAMETRI IOB...
@@ -1139,31 +1115,6 @@ namespace IOB_WIN_FORM.Iob
#region Protected Methods
/// <summary>
/// Verifico i dynData x validità:
/// - siano almeno 1
/// - NON siano tutti identici
/// </summary>
/// <param name="currDynData"></param>
/// <returns></returns>
protected bool checkValidDynData(Dictionary<string, string> currDynData)
{
bool answ = false;
// conto num valori
int numVal = currDynData.Count;
bool dataPresent = numVal > 0;
if (dataPresent)
{
// prendo il primo valore..
string firstVal = currDynData.FirstOrDefault().Value;
// conto val uguali al primo
int numEq = currDynData.Where(x => x.Value == firstVal).Count();
// se solo 1 o almeno 1 è diverso è ok
answ = numVal == 1 || (numVal > numEq);
}
return answ;
}
/// <summary>
/// Decodifica file MAP (caso <paramref name="ByteNum" />.bit)
/// </summary>
@@ -1226,6 +1177,26 @@ namespace IOB_WIN_FORM.Iob
}
}
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;
}
/// <summary>
/// Decodifica valore della coda IN nel formato hasVeto[0]=dtEve hasVeto[1]=valore hasVeto[2]=counter
/// </summary>
@@ -1246,6 +1217,36 @@ namespace IOB_WIN_FORM.Iob
return answ;
}
/// <summary>
/// Accodamento richieste server
/// </summary>
/// <param name="codTav"></param>
/// <param name="newReq"></param>
protected void accodaServReq(string codTav, string newReq)
{
JobTaskData jobTask = new JobTaskData(codTav, newReq);
// accodo richiesta serializzata
string serVal = JsonConvert.SerializeObject(jobTask);
QueueSrvReq.Enqueue(serVal);
// loggo!
lgDebug($"[QUEUE] | QueueSrvReq len: {QueueSrvReq.Count}");
}
/// <summary>
/// Accodamento risposte per il server
/// </summary>
/// <param name="codTav"></param>
/// <param name="rawData"></param>
protected void accodaServResp(string codTav, string rawData)
{
JobTaskData jobTask = new JobTaskData(codTav, rawData);
// accodo richiesta serializzata
string serVal = JsonConvert.SerializeObject(jobTask);
QueueSrvResp.Enqueue(serVal);
// loggo!
lgDebug($"[QUEUE] | QueueSrvResp len: {QueueSrvResp.Count}");
}
/// <summary>
/// Classe di base implementazione traduzione di una LUT da memoria come valore BIT a valore
/// esplicito x FLog
@@ -1268,11 +1269,28 @@ namespace IOB_WIN_FORM.Iob
}
/// <summary>
/// Verifica veto coda QueueIN ed aggiorna abilitazione su variabile
/// Verifico i dynData x validità:
/// - siano almeno 1
/// - NON siano tutti identici
/// </summary>
protected void checkVetoQueueIn()
/// <param name="currDynData"></param>
/// <returns></returns>
protected bool checkValidDynData(Dictionary<string, string> currDynData)
{
queueInEnabCurr = dtVetoQueueIN < DateTime.Now;
bool answ = false;
// conto num valori
int numVal = currDynData.Count;
bool dataPresent = numVal > 0;
if (dataPresent)
{
// prendo il primo valore..
string firstVal = currDynData.FirstOrDefault().Value;
// conto val uguali al primo
int numEq = currDynData.Where(x => x.Value == firstVal).Count();
// se solo 1 o almeno 1 è diverso è ok
answ = numVal == 1 || (numVal > numEq);
}
return answ;
}
/// <summary>
@@ -1352,6 +1370,42 @@ namespace IOB_WIN_FORM.Iob
}
}
/// <summary>
/// Verifica se salvare (nel log) info dei FluxLog filtrati
/// - per scadenza valore VetoFlushFiltFL
/// - per un numero complessivo di eventi superiori a soglia
/// </summary>
/// <param name="minCount">numero minimo di eventi necessari al salvataggio (come somma complessiva)</param>
protected void FiltFluxLogCheckSave(long minCount)
{
DateTime adesso = DateTime.Now;
bool doWrite = VetoFlushFiltFL < adesso;
// conteggio valori
if (!doWrite)
{
long numFL = DictFiltFLog.Sum(x => x.Value);
doWrite = numFL > minCount;
}
if (doWrite)
{
// scrivo log x ogni valore
string sep = "------------------------------------------------------------";
_logger.Info(sep);
_logger.Info("- Paret0 Eventi FluxLog filtrati");
_logger.Info(sep);
foreach (var item in DictFiltFLog.OrderByDescending(x => x.Value))
{
// loggo!
_logger.Info($"{item.Key}: {item.Value}");
}
_logger.Info(sep);
// reset dizionario
DictFiltFLog.Clear();
// nuovo veto a 1h
VetoFlushFiltFL = adesso.AddHours(1);
}
}
/// <summary>
/// Imposta eventuali altri valori default
/// </summary>
@@ -1740,7 +1794,7 @@ namespace IOB_WIN_FORM.Iob
}
answ = await utils.callUrlAsync(url2call);
#if false
answ = utils.callUrlNow(url2call);
answ = utils.callUrlNow(url2call);
#endif
}
return answ;
@@ -3190,28 +3244,28 @@ namespace IOB_WIN_FORM.Iob
url2call = $"{urlRemTask2ExeTav(codTav)}{taskName}";
}
#if false
try
{
Task.Run(async () => answ = await utils.callUrlAsync(url2call))
.GetAwaiter()
.GetResult();
lgInfo($"Task2Exe.remTask2exe | {esitoTask} | chiamata URL {url2call} | answ: {answ}");
}
catch (Exception ex)
{
lgError("ProcessAutoOdl | Crash nel ponte Sync/Async: " + ex.Message);
}
#endif
await utils.callUrlAsync(url2call);
#if false
answ = utils.callUrlNow(url2call);
#endif
}
return answ;
}
/// <summary>
/// Salva nel dizionario il num di valori letti e filtrati/non inviati
/// </summary>
/// <param name="codFlux"></param>
protected void SaveFiltFluxLog(string codFlux)
{
if (DictFiltFLog.ContainsKey(codFlux))
{
DictFiltFLog[codFlux]++;
}
else
{
DictFiltFLog.Add(codFlux, 1);
_logger.Info($"FluxLog | veto | {codFlux}");
}
}
/// <summary>
/// Invio reset allarmi all'avvio per sicurezza...
/// </summary>
@@ -3335,7 +3389,7 @@ namespace IOB_WIN_FORM.Iob
lgInfo("chiamata URL " + urlSetM2IOB);
await utils.callUrlAsync(urlSetM2IOB);
#if false
utils.callUrlNow(urlSetM2IOB);
utils.callUrlNow(urlSetM2IOB);
#endif
}
}
@@ -3424,7 +3478,7 @@ namespace IOB_WIN_FORM.Iob
lgInfo("chiamata URL " + url2call);
await utils.callUrlAsync(url2call);
#if false
utils.callUrlNow(url2call);
utils.callUrlNow(url2call);
#endif
}
}
@@ -3912,33 +3966,6 @@ namespace IOB_WIN_FORM.Iob
}
}
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;
}
private static readonly JsonSerializerOptions options = new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = false // Optional: set to true for pretty-printing
};
/// <summary>
/// Traccia in redis l'attività di lettura dati
/// </summary>
@@ -4123,6 +4150,7 @@ namespace IOB_WIN_FORM.Iob
}
return fatto;
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura ODL specifico
/// </summary>
@@ -4448,6 +4476,7 @@ namespace IOB_WIN_FORM.Iob
}
raiseRefresh(currDispData);
}
/// <summary>
/// Versione sync chiamata setup PODL
/// </summary>
@@ -4542,6 +4571,14 @@ namespace IOB_WIN_FORM.Iob
return $@"{urlMesServer}{IOBConfFull.MapoMes.ApiUrl(cmqReq)}{IOBConfFull.General.FilenameIOB}";
}
/// <summary>
/// URL per richiamo task da eseguire...
/// </summary>
protected string urlRemTask2ExeTav(string codTav)
{
return $@"{urlCommandIob("remTask2Exe")}|{codTav}?taskName=";
}
/// <summary>
/// Verifica se il parametro passi il limite della DeadBand (globale o specifica se
/// configurata) Il riferimento è al prec valore currProdData
@@ -4595,6 +4632,12 @@ 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
};
/// <summary>
/// Oggetto logger della classe
/// </summary>
@@ -4610,64 +4653,6 @@ namespace IOB_WIN_FORM.Iob
/// </summary>
private Dictionary<string, long> DictFiltFLog = new Dictionary<string, long>();
/// <summary>
/// Valore veto al salvataggio di valori filtrati in FluxLog se NON superato limite chiamate
/// </summary>
private DateTime VetoFlushFiltFL = DateTime.Now.AddHours(1);
/// <summary>
/// Salva nel dizionario il num di valori letti e filtrati/non inviati
/// </summary>
/// <param name="codFlux"></param>
protected void SaveFiltFluxLog(string codFlux)
{
if (DictFiltFLog.ContainsKey(codFlux))
{
DictFiltFLog[codFlux]++;
}
else
{
DictFiltFLog.Add(codFlux, 1);
_logger.Info($"FluxLog | veto | {codFlux}");
}
}
/// <summary>
/// Verifica se salvare (nel log) info dei FluxLog filtrati
/// - per scadenza valore VetoFlushFiltFL
/// - per un numero complessivo di eventi superiori a soglia
/// </summary>
/// <param name="minCount">numero minimo di eventi necessari al salvataggio (come somma complessiva)</param>
protected void FiltFluxLogCheckSave(long minCount)
{
DateTime adesso = DateTime.Now;
bool doWrite = VetoFlushFiltFL < adesso;
// conteggio valori
if (!doWrite)
{
long numFL = DictFiltFLog.Sum(x => x.Value);
doWrite = numFL > minCount;
}
if (doWrite)
{
// scrivo log x ogni valore
string sep = "------------------------------------------------------------";
_logger.Info(sep);
_logger.Info("- Paret0 Eventi FluxLog filtrati");
_logger.Info(sep);
foreach (var item in DictFiltFLog.OrderByDescending(x => x.Value))
{
// loggo!
_logger.Info($"{item.Key}: {item.Value}");
}
_logger.Info(sep);
// reset dizionario
DictFiltFLog.Clear();
// nuovo veto a 1h
VetoFlushFiltFL = adesso.AddHours(1);
}
}
private string LastDayCurr = "";
/// <summary>
@@ -4685,6 +4670,17 @@ namespace IOB_WIN_FORM.Iob
private Dictionary<string, int> TrackDetStatsCount = new Dictionary<string, int>();
private Dictionary<string, Dictionary<string, string>> TrackDetValsCount = new Dictionary<string, Dictionary<string, string>>();
#if false
/// <summary>
/// Ultimo valore B_output inviato
/// </summary>
protected int B_output_sent = -1;
#endif
/// <summary>
/// Valore veto al salvataggio di valori filtrati in FluxLog se NON superato limite chiamate
/// </summary>
private DateTime VetoFlushFiltFL = DateTime.Now.AddHours(1);
/// <summary>
/// Dizionario dei valori bloccati x evitare log eccessivo
@@ -4720,88 +4716,6 @@ namespace IOB_WIN_FORM.Iob
#region Private Properties
/// <summary>
/// Verifica se la IOB sia ENABLED (da server o Demo)
/// </summary>
public async Task<bool> CheckIobEnabled()
{
// 1. Controllo Veto (Sincrono)
if (dtVetoCheckIOB >= DateTime.Now)
return IobOnline;
bool currentAnsw = false;
if (DemoOut)
{
currentAnsw = (QueueIN.Count + QueueFLog.Count >= nMaxSend);
}
else
{
currentAnsw = await ExecuteIobCheckWithRetry(maxRetries: 5);
#if false
// 2. Chiamata asincrona con Retry (Ponte Sync/Async)
currentAnsw = Task.Run(async () => await ExecuteIobCheckWithRetry(maxRetries: 5))
.GetAwaiter()
.GetResult();
#endif
}
// 3. Gestione Stato e Logica UI
UpdateIobState(currentAnsw);
return currentAnsw;
}
private async Task<bool> ExecuteIobCheckWithRetry(int maxRetries)
{
var rand = new Random();
for (int i = 0; i <= maxRetries; i++)
{
try
{
if (i > 0)
{
// Al terzo tentativo fallito resetto i client
if (i == 3) resetWebClients();
int delay = i == 3 ? rand.Next(250, 1000) : rand.Next(250, 500);
await Task.Delay(delay);
}
string callResp = await callUrl(urlIobEnabled, i < 3); // true per i primi tentativi
if (callResp == "OK") return true;
}
catch (Exception exc)
{
lgError($"Eccez<ione in ExecuteIobCheckWithRetry{Environment.NewLine}{exc}");
}
}
return false;
}
private void UpdateIobState(bool newStatus)
{
// Log se lo stato è cambiato
if (IobOnline != newStatus)
{
lgInfo(newStatus ? "IOB ONLINE for server MP/IO" : "IOB OFFLINE for server MP/IO");
IobOnline = newStatus;
if (newStatus)
{
lastIobOnline = DateTime.Now;
parentForm.commSrvActive = 2; // Stato Online
}
else
{
parentForm.commSrvActive = 1; // Stato Degradato/Offline
}
}
// Imposto il veto per il prossimo controllo
dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5);
}
/// <summary>
/// Boolean abilitazione coda eventi IN
/// </summary>
@@ -4815,98 +4729,6 @@ namespace IOB_WIN_FORM.Iob
get => redisMan.redHash($"IOB:Status:{IOBConfFull.General.FilenameIOB}:CurrProdData");
}
/// <summary>
/// Test ping all'indirizzo impostato nei parametri
/// </summary>
/// <returns></returns>
private IPStatus GetPingStatus()
{
var pStatus = Task.Run(async () => await GetPingStatusAsync(maxPingRetry + 1))
.GetAwaiter()
.GetResult();
return pStatus;
}
/// <summary>
/// Test ping all'indirizzo impostato nei parametri
/// </summary>
/// <param name="maxRetry">Numero max tentativi (maxPingRetry + 1)</param>
/// <returns></returns>
private async Task<IPStatus> GetPingStatusAsync(int maxAttempts)
{
// 1. Check disabilitazione
if (IOBConfFull.MapoMes.DisabPing) return IPStatus.Success;
// 2. Estrazione Host/IP pulita
string host = IOBConfFull.MapoMes.IpAddr;
try
{
// Rimuove protocollo (http://, ftp://, etc)
if (host.Contains("://"))
host = new Uri(host).Host;
else if (host.Contains(":"))
host = host.Split(':')[0];
}
catch { /* fallback al valore originale se Uri fallisce */ }
// 3. Risoluzione Indirizzo
if (!IPAddress.TryParse(host, out IPAddress address))
{
try
{
var addresses = Dns.GetHostAddresses(host);
if (addresses.Length > 0) address = addresses[0];
}
catch (Exception ex)
{
lgError($"Impossibile risolvere DNS per {host}: {ex.Message}");
return IPStatus.DestinationHostUnreachable;
}
}
if (address == null) return IPStatus.Unknown;
// 4. Ciclo di Ping con Retry
var rand = new Random();
using (Ping pingSender = new Ping())
{
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
// Timeout dinamico come nel tuo originale
int timeout = (attempt == 1)
? rand.Next(200, 400)
: (pingServerMsTimeout * attempt / 2);
PingReply reply = pingSender.Send(address, timeout);
if (reply.Status == IPStatus.Success)
{
if (attempt > 1) lgInfo("Server PING OK dopo retry!");
return IPStatus.Success;
}
lgInfo($"Ping tent. {attempt} fallito: {reply.Status} (Timeout: {timeout}ms)");
}
catch (Exception ex)
{
lgError($"Eccezione durante ping tent. {attempt}: {ex.Message}");
}
// Attesa prima del prossimo tentativo (se non è l'ultimo)
if (attempt < maxAttempts)
{
await Task.Delay(rand.Next(50, 200));
}
}
}
return IPStatus.TimedOut;
}
/// <summary>
/// URL di base del server MES da contattare
/// </summary>
@@ -5149,6 +4971,33 @@ namespace IOB_WIN_FORM.Iob
return fatto;
}
private async Task<bool> ExecuteIobCheckWithRetry(int maxRetries)
{
var rand = new Random();
for (int i = 0; i <= maxRetries; i++)
{
try
{
if (i > 0)
{
// Al terzo tentativo fallito resetto i client
if (i == 3) resetWebClients();
int delay = i == 3 ? rand.Next(250, 1000) : rand.Next(250, 500);
await Task.Delay(delay);
}
string callResp = await callUrl(urlIobEnabled, i < 3); // true per i primi tentativi
if (callResp == "OK") return true;
}
catch (Exception exc)
{
lgError($"Eccez<ione in ExecuteIobCheckWithRetry{Environment.NewLine}{exc}");
}
}
return false;
}
/// <summary>
/// Esegue filtraggio dati x bit blinking!!!
/// </summary>
@@ -5226,6 +5075,97 @@ namespace IOB_WIN_FORM.Iob
demFactDynData = IOBConfFull.FluxLog.DemFactDynData;
}
/// <summary>
/// Test ping all'indirizzo impostato nei parametri
/// </summary>
/// <returns></returns>
private IPStatus GetPingStatus()
{
var pStatus = Task.Run(async () => await GetPingStatusAsync(maxPingRetry + 1))
.GetAwaiter()
.GetResult();
return pStatus;
}
/// <summary>
/// Test ping all'indirizzo impostato nei parametri
/// </summary>
/// <param name="maxRetry">Numero max tentativi (maxPingRetry + 1)</param>
/// <returns></returns>
private async Task<IPStatus> GetPingStatusAsync(int maxAttempts)
{
// 1. Check disabilitazione
if (IOBConfFull.MapoMes.DisabPing) return IPStatus.Success;
// 2. Estrazione Host/IP pulita
string host = IOBConfFull.MapoMes.IpAddr;
try
{
// Rimuove protocollo (http://, ftp://, etc)
if (host.Contains("://"))
host = new Uri(host).Host;
else if (host.Contains(":"))
host = host.Split(':')[0];
}
catch { /* fallback al valore originale se Uri fallisce */ }
// 3. Risoluzione Indirizzo
if (!IPAddress.TryParse(host, out IPAddress address))
{
try
{
var addresses = Dns.GetHostAddresses(host);
if (addresses.Length > 0) address = addresses[0];
}
catch (Exception ex)
{
lgError($"Impossibile risolvere DNS per {host}: {ex.Message}");
return IPStatus.DestinationHostUnreachable;
}
}
if (address == null) return IPStatus.Unknown;
// 4. Ciclo di Ping con Retry
var rand = new Random();
using (Ping pingSender = new Ping())
{
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
// Timeout dinamico come nel tuo originale
int timeout = (attempt == 1)
? rand.Next(200, 400)
: (pingServerMsTimeout * attempt / 2);
PingReply reply = pingSender.Send(address, timeout);
if (reply.Status == IPStatus.Success)
{
if (attempt > 1) lgInfo("Server PING OK dopo retry!");
return IPStatus.Success;
}
lgInfo($"Ping tent. {attempt} fallito: {reply.Status} (Timeout: {timeout}ms)");
}
catch (Exception ex)
{
lgError($"Eccezione durante ping tent. {attempt}: {ex.Message}");
}
// Attesa prima del prossimo tentativo (se non è l'ultimo)
if (attempt < maxAttempts)
{
await Task.Delay(rand.Next(50, 200));
}
}
}
return IPStatus.TimedOut;
}
/// <summary>
/// Verifica se il log di un dato errore sia permesso
/// </summary>
@@ -5632,7 +5572,6 @@ namespace IOB_WIN_FORM.Iob
{
lgError($"Errore in RecipeDoProcessPODL.callUrl: {ex.Message}");
}
}
answ = true;
}
@@ -5662,6 +5601,132 @@ namespace IOB_WIN_FORM.Iob
parentForm.dataProcLabel = string.Format("RAW: {0} --> IN: {1} --> OUT: {2}", nReadIN, nReadFilt, nSendOut);
}
/// <summary>
/// Chiamate ritorno task eseguiti al server
/// </summary>
/// <param name="listaValori"></param>
/// <returns></returns>
private async Task SendTaskResult(List<string> listaValori)
{
foreach (var rawJob in listaValori)
{
// deserializzo...
JobTaskData jobTask = JsonConvert.DeserializeObject<JobTaskData>(rawJob);
#if false
// loggo tutti i task done...
foreach (var item in jobTask.TaskList)
{
sendToTaskWatch(item.Key, item.Value, jobTask.CodTav);
}
#endif
// ora chiamo la cancellazione dei task eseguiti...
var taskDict = JobTaskData.TaskDict(jobTask.RawData);
foreach (var item in taskDict)
{
await remTask2exe(item.Key, item.Value, jobTask.CodTav);
}
}
}
/// <summary>
/// Effettua ciclo recupero richieste server
/// </summary>
private async Task ServerGetRequestsAsync()
{
// recupero elenco delle cose da fare
string resp = await getTask2exe("");
// se ho qualcosa --> creo obj e lo accodo...
//if (!string.IsNullOrEmpty(resp))
if (!string.IsNullOrEmpty(resp) && resp.Length > 2)
{
//await ProcessResp(resp, "");
accodaServReq("", resp);
if (isMulti)
{
foreach (var item in IOBConfFull.Device.MultiIobList)
{
resp = await getTask2exe(item);
accodaServReq(item, resp);
//await ProcessResp(resp, rawJob);
}
}
}
// vers originale
#if false
// recupero elenco delle cose da fare
string resp = await getTask2exe("");
await ProcessResp(resp, "");
// se fosse multi --> eseguo task per le varie sub (Tavole/Pallet)
if (isMulti)
{
foreach (var item in IOBConfFull.Device.MultiIobList)
{
resp = await getTask2exe(item);
await ProcessResp(resp, item);
}
}
#endif
}
/// <summary>
/// Effettua ciclo invio rispsote (=esito esecuzione richieste) al server
/// </summary>
private async Task ServerPutRespAsync()
{
// verifico SE la coda abbia dei valori...
if (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)
{
string currVal = "";
if (MPOnline)
{
if (IobOnline)
{
List<string> listaValori = new List<string>();
// se ho + di maxJsonData elementi --> invio un set di dati alla volta
if (QueueSrvResp.Count > maxJsonData)
{
// prendoi primi maxJsonDataValori
for (int j = 0; j < maxJsonData; j++)
{
QueueSrvResp.TryDequeue(out currVal);
listaValori.Add(currVal);
}
await SendTaskResult(listaValori);
}
else
{
// invio in blocco
listaValori = QueueSrvResp.ToList();
await SendTaskResult(listaValori);
// svuoto!
QueueSrvResp = new DataQueue(IOBConfFull.General.FilenameIOB, "QueueServResp", IOBConfFull.General.EnabRedisQue, redisMan);
}
}
else
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
}
}
/// <summary>
/// Imposto alcuni valori di default
/// </summary>
@@ -5687,15 +5752,18 @@ namespace IOB_WIN_FORM.Iob
{
string codIob = IOBConfFull.General.FilenameIOB;
bool useRedis = IOBConfFull.General.EnabRedisQue;
QueueAlarm = new DataQueue(codIob, "QueueAlarm", false, redisMan);
QueueIN.Dispose();
QueueIN = new DataQueue(codIob, "QueueIN", useRedis, redisMan);
QueueSrvResp.Dispose();
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);
QueueRawTransf = new DataQueue(codIob, "QueueRawTransf", false, redisMan);
QueueULog = new DataQueue(codIob, "QueueULog", 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);
}
// imposto contatori blink a zero...
i_counters = new int[32];
@@ -5982,6 +6050,28 @@ namespace IOB_WIN_FORM.Iob
}
}
private void UpdateIobState(bool newStatus)
{
// Log se lo stato è cambiato
if (IobOnline != newStatus)
{
lgInfo(newStatus ? "IOB ONLINE for server MP/IO" : "IOB OFFLINE for server MP/IO");
IobOnline = newStatus;
if (newStatus)
{
lastIobOnline = DateTime.Now;
parentForm.commSrvActive = 2; // Stato Online
}
else
{
parentForm.commSrvActive = 1; // Stato Degradato/Offline
}
}
// Imposto il veto per il prossimo controllo
dtVetoCheckIOB = DateTime.Now.AddMilliseconds(baseUtils.nextPauseSendMSec * 5);
}
#endregion Private Methods
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -17,6 +17,8 @@
}
],
"fluxLogVeto": [
"E_nblock1",
"E_nline1",
"S_nactS1P1",
"S_nloadS1P1",
"S_nactUP1",